From 15a2f6435c4989a425dc841a0e2f0e3bb91fdffa Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Nov 2024 16:29:26 +0000 Subject: [PATCH 001/274] 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 }; From baeca30c6d7c199978175b27ef735d973e02f88b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Nov 2024 17:41:54 +0000 Subject: [PATCH 002/274] Fix PRAMDAC. Now it shows as working in Windows 2000? --- doc/nv3 driver init status.txt | 105 ++++++++++++++++++++++ src/video/nv/nv3/subsystems/nv3_pramdac.c | 12 +-- 2 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 doc/nv3 driver init status.txt diff --git a/doc/nv3 driver init status.txt b/doc/nv3 driver init status.txt new file mode 100644 index 000000000..b6f470f92 --- /dev/null +++ b/doc/nv3 driver init status.txt @@ -0,0 +1,105 @@ +Driver version: Windows 2000 Version 3.37 (23 March 1999) - SDK: Windows 2000 build 1996.1 +Features: + +Resource Manager Yes +DirectDraw Yes +OpenGL Yes +Direct3D No + + +get started: +MUST USE X86 WINDBG +Does older windbg support coff??? + +sxe ld nv3_mini.sys +bp nv3_mini + 0x409ac - 0x10000 (nv3_mini+0x309ac) + +offset purpose +30a1a RmInitRm call + +6be7 initClientInfo call +6878 initClientInfo +6bec Check for initClientInfo success +6bf4 initGrPatchPool call +6bf9 Check for initGrPatchPool success +6c01 initDmaListElementPool call +6c06 Check for initDmaListElementPool +6c1c initDisplayInfo call + +6c26 rmInitRm End + Success: eax=FFFFFFFFh / -1 +6c26 Fail eax=0 + +30c8b NvFindAdapter +30cb6 NvFindAdapter -> NVIsPresent call +1010 NVIsPresent function +102f NVIsPresent VideoPortGetAccessRanges call +103b NVIsPresent VideoPortGetAccessRanges call success check (only possible way to fail) +127c NVIsPresent end +30cbb NvFindAdapter -> NVIsPresent success check + Success: al=1 + Failure: al=0 +30cca NVIsPresent NVMapMemoryRanges call +e9e NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #1 [PCI Space] +ea4 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #1 success check [PCI Space] +ebd NVIsPresent NVMapMemoryRanges VideoPortFreeDeviceBase [conditional] +ec3 NVIsPresent NVMapMemoryRanges VideoPortFreeDeviceBase [conditional] success check +ed6 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #2 [MMIO] +edc NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #2 success check [MMIO] +f0c NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #3 [LFB/RAMIN?] +f12 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #3 success check [LFB/RAMIN?] + +30ccf NvFindAdapter NVMapMemoryRanges success check + Success: eax=0 + Failure: eax=87 +30cf1 NvFindAdapter RmInitNvMapping call +6ce6 NvFindAdapter RmInitNvMapping +30cf6 NVIsPresent RmInitNvMapping success check + Success: eax!=0 (in practice 0xFFFFFFFF/-1) + Failure: eax=0 +30d5c NvFindAdapter +30d64 NvFindAdapter RmPostNvDevice call +6d88 RmPostNvDevice function +6d91 NvFindAdapter DevinitInitializeDevice call +6d96 NvFindAdapter DevinitInitializeDevice success check + Success: eax=0 (?) + Failure: eax=1 +e546 DevinitInitializeDevice function + [very complicated] + [several register reads] +e61d DevinitPrepDeviceForInit call +e641 DevinitPrepDeviceForInit function +e627 InitNV call +e67a InitNV function + +30d64 NVIsPresent RmPostNvDevice success check + Success: eax=0 (?) + Failure: eax=1 +30d78 NVIsPresent NVGetNVInfo call +30d7d NVIsPresent NVGetNVInfo success check + +3e9a NvFindAdapter end + Success: eax=0 + Fail eax=55 (RmInitNvMapping or NVIsPresent failed) + Fail eax=87 (NVMapMemoryRanges or NVMapFrameBuffer failed) + +30ea3 NVInitialize +30f02 NVStartIO +2aa6 NVInterrupt +30a2d NVGetChildDescriptor +30a9c NVGetPowerState +30b20 NVSetPowerState + + +Driver Init Status: +DriverEntry Success +rmInitRm -> initClientInfo Success +rmInitRm -> initGrPatchPool Success +rmInitRm -> initDmaListElementPool Success +rmInitRm -> initDisplayInfo Success +rmInitRm overall Success +NvFindAdapter Failing (EAX=87) +NvFindAdapter -> NvIsPresent Success 16:19 24/11/2024 +NvFindAdapter -> NvMapMemoryRanges Success 19:15 26/11/2024 +NvFindAdapter -> RmInitNvMapping Success 19:18 26/11/2024 +NvFindAdapter -> RmPostNvDevice diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index a2a39b6cb..1716fd3ef 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -54,16 +54,16 @@ uint32_t nv3_pramdac_get_vram_clock_register() // 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 + return (nv3->pramdac.memory_clock_m); + + (nv3->pramdac.memory_clock_n << 8); + + (nv3->pramdac.memory_clock_p << 16); // 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 + return (nv3->pramdac.pixel_clock_m); + + (nv3->pramdac.pixel_clock_n << 8); + + (nv3->pramdac.pixel_clock_p << 16); // 0-3 } void nv3_pramdac_set_vram_clock_register(uint32_t value) From 1c1471867761210c8cf63cc589fda90bfa8c7c0d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Nov 2024 18:15:54 +0000 Subject: [PATCH 003/274] anti-stupidity --- src/video/nv/nv3/subsystems/nv3_pramdac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 1716fd3ef..39b5b918f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -54,15 +54,15 @@ uint32_t nv3_pramdac_get_vram_clock_register() // M divisor [7-0] // N divisor [16-8] // P divisor [18-16] - return (nv3->pramdac.memory_clock_m); - + (nv3->pramdac.memory_clock_n << 8); + return (nv3->pramdac.memory_clock_m) + + (nv3->pramdac.memory_clock_n << 8) + (nv3->pramdac.memory_clock_p << 16); // 0-3 } uint32_t nv3_pramdac_get_pixel_clock_register() { - return (nv3->pramdac.pixel_clock_m); - + (nv3->pramdac.pixel_clock_n << 8); + return (nv3->pramdac.pixel_clock_m) + + (nv3->pramdac.pixel_clock_n << 8) + (nv3->pramdac.pixel_clock_p << 16); // 0-3 } From b816a7d3796d57de961ac21241df2853a1835b8f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 1 Dec 2024 20:56:42 +0000 Subject: [PATCH 004/274] Initial VBLANK draft. Still fucked up --- doc/NV_PFB_CONFIG_0.txt | 29 ++++++++++ doc/nv3 driver init status.txt | 6 ++- src/include/86box/nv/vid_nv.h | 3 ++ src/include/86box/nv/vid_nv3.h | 67 +++++++++++++++++------- src/video/nv/nv3/nv3_core_arbiter.c | 2 + src/video/nv/nv3/subsystems/nv3_pfb.c | 48 +++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 33 +++++++++--- src/video/nv/nv3/subsystems/nv3_pmc.c | 20 +++++-- 8 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 doc/NV_PFB_CONFIG_0.txt diff --git a/doc/NV_PFB_CONFIG_0.txt b/doc/NV_PFB_CONFIG_0.txt new file mode 100644 index 000000000..dee3b646c --- /dev/null +++ b/doc/NV_PFB_CONFIG_0.txt @@ -0,0 +1,29 @@ +NV_PFB_CONFIG_0 + +Observed Valus: +Drivers 0x1000 +BIOS 0x1114 + +Bits +5:0 Resolution +9:8 Pixel depth +12:12 Tiling +13:13 Tiling Debug +14:14 Tiling Debug Tile Size +17:15 "Tetris" tiling +19:18 "Tetris" tiling shift +22:20 Bank Swap +23 Unused + +NV_PFB_CONFIG_1 +2:0 CAS Latency +3:3 NEC Mode (PC-98?) +7:4 RAS Default / 9 Cycles +10:8 RAS PCHG +14:12 RAS Low +18:16 MRS to RAS +22:20 Write to Read +26:24 RAS to CAS +30:28 Read to Write +31:31 Read to PCFg + diff --git a/doc/nv3 driver init status.txt b/doc/nv3 driver init status.txt index b6f470f92..6ac794588 100644 --- a/doc/nv3 driver init status.txt +++ b/doc/nv3 driver init status.txt @@ -98,8 +98,10 @@ rmInitRm -> initGrPatchPool Success rmInitRm -> initDmaListElementPool Success rmInitRm -> initDisplayInfo Success rmInitRm overall Success -NvFindAdapter Failing (EAX=87) +NvFindAdapter Success 17:32 27/11/2024 NvFindAdapter -> NvIsPresent Success 16:19 24/11/2024 NvFindAdapter -> NvMapMemoryRanges Success 19:15 26/11/2024 NvFindAdapter -> RmInitNvMapping Success 19:18 26/11/2024 -NvFindAdapter -> RmPostNvDevice +NvFindAdapter -> RmPostNvDevice Success 17:32 27/11/2024 +NvFindAdapter -> NVGetNVInfo Success 17:32 27/11/2024 +NvFindAdapter -> NVMapFrameBuffer Success 17:32 27/11/2024 diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index ca0922a44..cabd9f46b 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -158,6 +158,8 @@ typedef struct nv3_straps_s typedef struct nv3_pfb_s { uint32_t boot; + uint32_t config_0; + uint32_t config_1; } nv3_pfb_t; #define NV3_RMA_NUM_REGS 4 @@ -460,6 +462,7 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now); // NV3 PGRAPH void nv3_pgraph_init(); +void nv3_pgraph_vblank_start(svga_t* svga); // NV3 PFIFO void nv3_pfifo_init(); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a9a4884d9..bd8d373b7 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -34,6 +34,8 @@ extern nv3_t* nv3; #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 +#define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation +#define NV3T_VBIOS_DIAMOND_V330_V182B "roms/video/nvidia/nv3/nv3t182b.rom" // Diamond Multimedia Viper V330 8M BIOS - Version 1.82B // Temporary, will be loaded from settings #define VRAM_SIZE_2MB 0x200000 // 2MB @@ -113,6 +115,7 @@ extern nv3_t* nv3; #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 @@ -138,6 +141,23 @@ extern nv3_t* nv3; #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_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 // Determines if PGRAPH is enabled. +#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 + #define NV3_PMC_END 0xfff // overlaps with CIO #define NV3_CIO_START 0x3b0 // Legacy SVGA Emulation Subsystem @@ -189,6 +209,32 @@ extern nv3_t* nv3; #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_CONFIG_0 0x100200 // Framebuffer interface config register 0 +#define NV3_PFB_CONFIG_0_RESOLUTION 0 +// 1=40 horiz. resolution +// i assume it can be divided by some kind of divisor to produce the vertical resolution (e.g. 3/2 or multiply by 2/3) to get the final +// horiz is 32*value +// theoretically it should support resolutions from 40-2560 horiz + +// WHAT ARE THE TIMINGS: ARE THEY IN THE VBIOS? +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_320 0xA +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_400 0xD +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_480 0xF +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_512 0x10 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_640 0x14 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_800 0x19 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_960 0x1E +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1024 0x20 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1152 0x24 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1280 0x28 +#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1600 0x32 + +#define NV3_PFB_CONFIG_0_PIXEL_DEPTH 8 +#define NV3_PFB_CONFIG_0_DEPTH_8BPP 0x1 +#define NV3_PFB_CONFIG_0_DEPTH_16BPP 0x2 +#define NV3_PFB_CONFIG_0_DEPTH_32BPP 0x3 + +#define NV3_PFB_CONFIG_1 0x100204 // Framebuffer interface config register 1 #define NV3_PFB_END 0x100FFF #define NV3_PEXTDEV_START 0x101000 // External Devices #define NV3_PSTRAPS 0x101000 // Straps Bits @@ -238,6 +284,7 @@ extern nv3_t* nv3; #define NV3_PGRAPH_INTR_0 0x400100 #define NV3_PGRAPH_INTR_1 0x400104 #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 +#define NV3_PGRAPH_INTR_EN_0_VBLANK 8 // Fired every frame //todo: add what this does #define NV3_PGRAPH_INTR_EN_1 0x400180 // Interrupt Control for PGRAPH #2 (it can receive two at onc) @@ -384,26 +431,6 @@ extern nv3_t* nv3; // 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) diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 0b307e84a..1ef1c0e36 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -67,6 +67,8 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) 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_PFIFO_START && address <= NV3_PFIFO_END) + ret = nv3_pfifo_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) diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index d644185a2..e1d1c8352 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -28,8 +28,14 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> +// Functions only used in this translation unit +uint32_t nv3_pfb_config0_read(); +void nv3_pfb_config0_write(uint32_t val); + nv_register_t pfb_registers[] = { { NV3_PFB_BOOT, "PFB Boot Config", NULL, NULL}, + { NV3_PFB_CONFIG_0, "PFB Framebuffer Config 0", nv3_pfb_config0_read, nv3_pfb_config0_write }, + { NV3_PFB_CONFIG_1, "PFB Framebuffer Config 1", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -78,7 +84,8 @@ uint32_t nv3_pfb_read(uint32_t address) { case NV3_PFB_BOOT: return nv3->pfb.boot; - break; + case NV3_PFB_CONFIG_1: + return nv3->pfb.config_1; } } } @@ -90,7 +97,7 @@ 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); + nv_log("NV3: PFB Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) @@ -103,7 +110,40 @@ void nv3_pfb_write(uint32_t address, uint32_t value) // on-read function if (reg->on_write) reg->on_write(value); - + else + { + switch (reg->address) + { + case NV3_PFB_CONFIG_1: // Config Register 1 + nv3->pfb.config_1 = value; + } + } } - +} + +uint32_t nv3_pfb_config0_read() +{ + return nv3->pfb.config_0; + +} + +void nv3_pfb_config0_write(uint32_t val) +{ + nv3->pfb.config_0 = val; + + // i think the actual size and pixel depth are set in PRAMDAC + // so we don't update things here for now + + uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; + uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; + nv_log("NV3: Framebuffer Config Change\n"); + nv_log("NV3: Horizontal Size=%d pixels\n", new_pfb_htotal); + + if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) + nv_log("NV3: Bit Depth=8bpp\n"); + else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_16BPP) + nv_log("NV3: Bit Depth=16bpp\n"); + else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_32BPP) + nv_log("NV3: Bit Depth=32bpp\n"); + } \ 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 index d37ccdf9f..ac2970e35 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -28,19 +28,20 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> -// Single unified write function... + +// Initialise the PGRAPH subsystem. +void nv3_pgraph_init() +{ + nv_log("NV3: Initialising PGRAPH..."); + // Set up the vblank interrupt + nv3->nvbase.svga.vblank_start = nv3_pgraph_vblank_start; + nv_log("Done!\n"); +} // // ****** 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 }, @@ -141,11 +142,27 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // and only bit0-16 is defined in intr_1 case NV3_PGRAPH_INTR_EN_0: nv3->pgraph.interrupt_enable_0 = value & 0x11111111; + nv3_pmc_handle_interrupts(true); break; case NV3_PGRAPH_INTR_EN_1: nv3->pgraph.interrupt_enable_1 = value & 0x00011111; + nv3_pmc_handle_interrupts(true); + break; } } } +} + +// Fire a VALID Pgraph interrupt: num is the bit# of the interrupt in the GPU subsystem INTR_EN register. +void nv3_pgraph_interrupt_valid(uint32_t num) +{ + nv3->pgraph.interrupt_enable_0 |= (1 << num); + nv3_pmc_handle_interrupts(true); +} + +// VBlank. Fired every single frame. +void nv3_pgraph_vblank_start(svga_t* svga) +{ + nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); } \ 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 index 10717f7bc..63b37f11c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -35,6 +35,7 @@ void nv3_pmc_init() nv_log("NV3: Initialising PMC....\n"); nv3->pmc.boot = NV3_BOOT_REG_DEFAULT; + nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_HARDWARE; nv_log("NV3: Initialising PMC: Done\n"); } @@ -113,11 +114,23 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) // If interrupts are disabled don't bother if (!nv3->pmc.interrupt_enable) + { nv3_pmc_clear_interrupts(); + return nv3->pmc.interrupt_status; + } + // if we actually need to send the interrupt (i.e. this is a write) send it now if (send_now) { + // no interrupts to send + if (!(nv3->pmc.interrupt_status) + || !(nv3->pmc.interrupt_status - 0x80000000)) + { + nv3_pmc_clear_interrupts(); + return nv3->pmc.interrupt_status; + } + if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) { if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_HARDWARE) @@ -213,8 +226,8 @@ void nv3_pmc_write(uint32_t address, uint32_t value) switch (reg->address) { case NV3_PMC_INTERRUPT_STATUS: - // this - if (!(nv3->pmc.interrupt_status & (NV3_PMC_INTERRUPT_SOFTWARE - 1))) + // This can only be done by software interrupts... + if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) { 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); @@ -225,7 +238,8 @@ void nv3_pmc_write(uint32_t address, uint32_t value) nv3->pmc.interrupt_status = value; break; case NV3_PMC_INTERRUPT_ENABLE: - nv3->pmc.interrupt_enable = value; + nv3->pmc.interrupt_enable = value & 0x03; + nv3_pmc_handle_interrupts(value != 0); break; case NV3_PMC_ENABLE: nv3->pmc.enable = value; From 9753b0eca5a861e9017ce23d25878f4b77b23453 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 2 Dec 2024 17:40:31 +0000 Subject: [PATCH 005/274] Make it so you can switch vbioses --- src/include/86box/nv/vid_nv3.h | 10 +++- src/video/CMakeLists.txt | 2 +- src/video/nv/nv3/nv3_core.c | 23 ++++--- src/video/nv/nv3/nv3_core_config.c | 96 ++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 src/video/nv/nv3/nv3_core_config.c diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index bd8d373b7..8f136944c 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -16,10 +16,11 @@ */ // vid_nv3.h: NV3 Architecture Hardware Reference (open-source) -// Last updated 26 November 2024 +// Last updated 2 December 2024 // The GPU base structure extern nv3_t* nv3; +extern const device_config_t nv3_config[]; #define NV3_MMIO_SIZE 0x1000000 // Max MMIO size @@ -34,8 +35,13 @@ extern nv3_t* nv3; #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 -#define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation #define NV3T_VBIOS_DIAMOND_V330_V182B "roms/video/nvidia/nv3/nv3t182b.rom" // Diamond Multimedia Viper V330 8M BIOS - Version 1.82B +#define NV3T_VBIOS_ASUS_V170 "roms/video/nvidia/nv3/A170D03T.rom" // ASUS AGP-V3000 ZXTV BIOS - V1.70D.03 (C) 1996-98 Nvidia Corporation +#define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation +#define NV3T_VBIOS_REFERENCE_CEK_V172 "roms/video/nvidia/nv3/vgasgram.rom" // Reference(?) BIOS: RIVA 128 ZX BIOS - V1.72B (C) 1996-98 NVidia Corporation + +// The default VBIOS to use +#define NV3_VBIOS_DEFAULT NV3_VBIOS_ERAZOR_V15403 // Temporary, will be loaded from settings #define VRAM_SIZE_2MB 0x200000 // 2MB diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 1aec0e98d..a7bc62326 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -29,7 +29,7 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c vid_tvp3026_ramdac.c vid_att2xc498_ramdac.c vid_xga.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/nv3_core.c nv/nv3/nv3_core_config.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 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 3b4394561..83b198461 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -734,15 +734,22 @@ 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); + // Figure out which vbios the user selected + const char* vbios_id = device_get_config_bios("VBIOS"); + const char* vbios_file = ""; + + // depends on the bus we are using + if (nv3->nvbase.bus_generation == nv_bus_pci) + vbios_file = device_get_bios_file(&nv3_device_pci, vbios_id, 0); + else + vbios_file = device_get_bios_file(&nv3_device_agp, vbios_id, 0); + + int32_t err = rom_init(&nv3->nvbase.vbios, vbios_file, 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); + nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); // set up the bus and start setting up SVGA core if (nv3->nvbase.bus_generation == nv_bus_pci) @@ -818,7 +825,8 @@ const device_t nv3_device_pci = .init = nv3_init_pci, .close = nv3_close, .speed_changed = nv3_speed_changed, - .force_redraw = nv3_force_redraw + .force_redraw = nv3_force_redraw, + .config = nv3_config, }; // NV3 (RIVA 128) @@ -833,5 +841,6 @@ const device_t nv3_device_agp = .init = nv3_init_agp, .close = nv3_close, .speed_changed = nv3_speed_changed, - .force_redraw = nv3_force_redraw + .force_redraw = nv3_force_redraw, + .config = nv3_config, }; \ No newline at end of file diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c new file mode 100644 index 000000000..f30df0afa --- /dev/null +++ b/src/video/nv/nv3/nv3_core_config.c @@ -0,0 +1,96 @@ +/* + * 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. + * + * Provides NV3 configuration + * + * + * 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> + +const device_config_t nv3_config[] = +{ + { + .name = "VBIOS", + .description = "VBIOS", + .type = CONFIG_BIOS, + .default_string = "NV3_VBIOS_ERAZOR_V15403", + .default_int = 0, + .bios = + { + { + .name = "[NV3 - 1997-09-30] ELSA VICTORY Erazor VBE 3.0 DDC2B DPMS Video BIOS Ver. 1.47.01 (ZZ/ A/00)", .files_no = 1, + .internal_name = "NV3_VBIOS_ERAZOR_V14700", + .files = {NV3_VBIOS_ERAZOR_V14700, ""} + }, + { + .name = "[NV3 - 1998-02-06] ELSA VICTORY Erazor Ver. 1.54.03 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, + .internal_name = "NV3_VBIOS_ERAZOR_V15403", + .files = {NV3_VBIOS_ERAZOR_V15403, ""} + }, + { + .name = "[NV3 - 1998-05-04] ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, + .internal_name = "NV3_VBIOS_ERAZOR_V15500", + .files = {NV3_VBIOS_ERAZOR_V15500, ""} + }, + { + .name = "[NV3 - 1998-01-14] Diamond Multimedia Systems, Inc. Viper V330 Version 1.62-CO", .files_no = 1, + .internal_name = "NV3_VBIOS_DIAMOND_V330_V162", + .files = {NV3_VBIOS_DIAMOND_V330_V162, ""}, + }, + { + .name = "[NV3 - 1997-09-06] ASUS AGP/3DP-V3000 BIOS 1.51B", .files_no = 1, + .internal_name = "NV3_VBIOS_ASUS_V3000_V151", + .files = {NV3_VBIOS_ASUS_V3000_V151, ""}, + }, + { + .name = "[NV3 - 1997-12-17] STB Velocity 128 (RIVA 128) Ver.1.82", .files_no = 1, + .internal_name = "NV3_VBIOS_STB_V128_V182", + .files = {NV3_VBIOS_STB_V128_V182, ""}, + }, + { + .name = "[NV3T - 1998-09-15] Diamond Multimedia Viper V330 8M BIOS - Version 1.82B", .files_no = 1, + .internal_name = "NV3T_VBIOS_DIAMOND_V330_V182B", + .files = {NV3T_VBIOS_DIAMOND_V330_V182B, ""}, + }, + { + .name = "[NV3T - 1998-08-04] ASUS AGP-V3000 ZXTV BIOS - V1.70D.03", .files_no = 1, + .internal_name = "NV3T_VBIOS_ASUS_V170", + .files = {NV3T_VBIOS_ASUS_V170, ""}, + }, + { + .name = "[NV3T - 1998-07-30] RIVA 128 ZX BIOS - V1.71B-N", .files_no = 1, + .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V171", + .files = {NV3T_VBIOS_REFERENCE_CEK_V171, ""}, + }, + + { + .name = "[NV3T+SGRAM - 1998-08-15] RIVA 128 ZX BIOS - V1.72B", .files_no = 1, + .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V172", + .files = {NV3T_VBIOS_REFERENCE_CEK_V172, ""}, + }, + } + }, + { + .type = CONFIG_END + } +}; \ No newline at end of file From b51420307929c77eb0eb8431d8f6ccfa50c9ea96 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 2 Dec 2024 21:15:16 +0000 Subject: [PATCH 006/274] correctly set interrupt_enable at boot + start working on vram config --- src/video/nv/nv3/nv3_core_config.c | 25 +++++++++++++++++++++++++ src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index f30df0afa..7a5bf10ea 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -29,6 +29,7 @@ const device_config_t nv3_config[] = { + // VBIOS type configuration { .name = "VBIOS", .description = "VBIOS", @@ -90,6 +91,30 @@ const device_config_t nv3_config[] = }, } }, + // Memory configuration + { + .name = "VRAM", + .description = "VRAM", + .type = CONFIG_SELECTION, + .selection = + { + // This never existed officially but was planned. Same for 64-bit bus. Debug only + { + .description = "2 MB (Never officially sold)", + .value = VRAM_SIZE_2MB, + }, + + { + .description = "4 MB", + .value = VRAM_SIZE_4MB, + }, + { + .description = "8 MB", + .value = VRAM_SIZE_8MB, + }, + } + + }, { .type = CONFIG_END } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 63b37f11c..c2d010cfd 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -35,7 +35,7 @@ void nv3_pmc_init() nv_log("NV3: Initialising PMC....\n"); nv3->pmc.boot = NV3_BOOT_REG_DEFAULT; - nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_HARDWARE; + nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_SOFTWARE; nv_log("NV3: Initialising PMC: Done\n"); } From e7ca410d36b96fc7d0a58c654c5db5687b07aced Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 5 Dec 2024 22:30:20 +0000 Subject: [PATCH 007/274] added the ability to switch between different amounts of vram --- src/include/86box/nv/vid_nv3.h | 5 +++++ src/video/nv/nv3/nv3_core.c | 13 ++++++++----- src/video/nv/nv3/nv3_core_config.c | 1 + src/video/nv/nv3/subsystems/nv3_pbus.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pramin.c | 24 ++++++++++++------------ 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 8f136944c..1e9b8064c 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -24,6 +24,11 @@ extern const device_config_t nv3_config[]; #define NV3_MMIO_SIZE 0x1000000 // Max MMIO size +#define NV3_LFB_RAMIN_MIRROR_START 0x400000 // Mirror of ramin (VERIFY ON HARDWARE) +#define NV3_LFB_2NDHALF_START 0x800000 // The second half of LFB(?) +#define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start +#define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN + // various vbioses for testing // Coming soon: MIROmagic Premium BIOS (when I get mine dumped) //todo: move to hash system diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 83b198461..c185200f4 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -696,9 +696,10 @@ void nv3_update_mappings() // 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); + + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_MIRROR_START, NV3_LFB_MAPPING_SIZE); + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_2NDHALF_START, NV3_LFB_MAPPING_SIZE); + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); // TODO: RAMIN and its mirror // Did we change the banked SVGA mode? @@ -751,6 +752,8 @@ void* nv3_init(const device_t *info) else nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); + uint32_t vram_amount = device_get_config_int("VRAM"); + // set up the bus and start setting up SVGA core if (nv3->nvbase.bus_generation == nv_bus_pci) { @@ -758,7 +761,7 @@ void* nv3_init(const device_t *info) 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, + svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); } else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) @@ -767,7 +770,7 @@ void* nv3_init(const device_t *info) 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, + svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); } diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index 7a5bf10ea..f23286693 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -96,6 +96,7 @@ const device_config_t nv3_config[] = .name = "VRAM", .description = "VRAM", .type = CONFIG_SELECTION, + .default_int = VRAM_SIZE_4MB, .selection = { // This never existed officially but was planned. Same for 64-bit bus. Debug only diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index a344d8627..fddedd5cf 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -152,7 +152,7 @@ uint8_t nv3_pbus_rma_read(uint16_t addr) // 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); + ret = svga_read_linear((real_final_address - NV3_MMIO_SIZE) & (nv3->nvbase.svga.vram_max - 1), &nv3->nvbase.svga); nv3->nvbase.svga.chain4 = false; nv3->nvbase.svga.packed_chain4 = false; } @@ -234,7 +234,7 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val) { 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); + svga_writel_linear((nv3->pbus.rma.addr - NV3_MMIO_SIZE) & (nv3->nvbase.svga.vram_max - 1), nv3->pbus.rma.data, &nv3->nvbase.svga); nv3->nvbase.svga.chain4 = false; nv3->nvbase.svga.packed_chain4 = false; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index cef9597c9..9750e2f4c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -43,10 +43,10 @@ // Read 8-bit ramin uint8_t nv3_ramin_read8(uint32_t addr, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max - 1); uint32_t raw_addr = addr; // saved after and - addr ^= (VRAM_SIZE_4MB - 0x10); + addr ^= (nv3->nvbase.svga.vram_max- 0x10); uint8_t val = nv3->nvbase.svga.vram[addr]; @@ -58,14 +58,14 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) // Read 16-bit ramin uint16_t nv3_ramin_read16(uint32_t addr, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 1; // what uint16_t val = vram_16bit[addr]; // what @@ -78,14 +78,14 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) // Read 32-bit ramin uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what uint32_t val = vram_32bit[addr]; @@ -98,14 +98,14 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) // Write 8-bit ramin void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max - 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); + addr ^= (nv3->nvbase.svga.vram_max - 0x10); nv3->nvbase.svga.vram[addr] = val; @@ -115,14 +115,14 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) // Write 16-bit ramin void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max- 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 ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 1; // what vram_16bit[addr] = val; @@ -133,14 +133,14 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) // Write 32-bit ramin void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) { - addr &= (VRAM_SIZE_4MB - 1); + addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what vram_32bit[addr] = val; From 831afb5fa16c78c1c0c1759113cd625c8b60e91b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 5 Dec 2024 23:10:50 +0000 Subject: [PATCH 008/274] GPU revision switch, pmc_boot fixes --- src/include/86box/nv/vid_nv.h | 7 ++++++- src/video/nv/nv3/nv3_core.c | 6 ++++-- src/video/nv/nv3/nv3_core_config.c | 23 ++++++++++++++++++++++- src/video/nv/nv3/subsystems/nv3_pmc.c | 8 +++++++- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index cabd9f46b..bac0220ea 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -97,6 +97,7 @@ typedef struct nv_base_s 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) + uint32_t gpu_revision; // GPU Stepping } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E @@ -120,7 +121,11 @@ typedef struct nv_register_s nv_register_t* nv_get_register(uint32_t address, nv_register_t* register_list, uint32_t num_regs); -#define NV3_BOOT_REG_DEFAULT 0x00300111 +// Default value for the boot information register. +// Depends on the chip +#define NV3_BOOT_REG_REV_A00 0x00030100 +#define NV3_BOOT_REG_REV_B00 0x00030110 +#define NV3_BOOT_REG_REV_C00 0x00030120 // Master Control typedef struct nv3_pmc_s diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index c185200f4..033563998 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -158,7 +158,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) break; case NV3_PCI_CFG_REVISION: - ret = NV3_PCI_CFG_REVISION_B00; // Commercial release + ret = nv3->nvbase.gpu_revision; // Commercial release break; case PCI_REG_PROG_IF: @@ -752,8 +752,10 @@ void* nv3_init(const device_t *info) else nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); + // set the vram amount and gpu revision uint32_t vram_amount = device_get_config_int("VRAM"); - + nv3->nvbase.gpu_revision = device_get_config_int("Chip Revision"); + // set up the bus and start setting up SVGA core if (nv3->nvbase.bus_generation == nv_bus_pci) { diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index f23286693..d7944b69c 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -94,7 +94,7 @@ const device_config_t nv3_config[] = // Memory configuration { .name = "VRAM", - .description = "VRAM", + .description = "VRAM Size", .type = CONFIG_SELECTION, .default_int = VRAM_SIZE_4MB, .selection = @@ -116,6 +116,27 @@ const device_config_t nv3_config[] = } }, + { + .name = "Chip Revision", + .description = "Chip Revision", + .type = CONFIG_SELECTION, + .default_int = NV3_PCI_CFG_REVISION_B00, + .selection = + { + { + .description = "NV3/STG3000 Engineering Sample / Stepping A0 (January 1997)", + .value = NV3_PCI_CFG_REVISION_A00, + }, + { + .description = "RIVA 128 (NV3) / Stepping B0 (August 1997)", + .value = NV3_PCI_CFG_REVISION_B00, + }, + { + .description = "RIVA 128 ZX (NV3T) / Stepping C0 (March 1998)", + .value = NV3_PCI_CFG_REVISION_C00, + }, + } + }, { .type = CONFIG_END } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index c2d010cfd..50eccfae3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -34,7 +34,13 @@ void nv3_pmc_init() { nv_log("NV3: Initialising PMC....\n"); - nv3->pmc.boot = NV3_BOOT_REG_DEFAULT; + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_A00) + nv3->pmc.boot = NV3_BOOT_REG_REV_A00; + else if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_B00) + nv3->pmc.boot = NV3_BOOT_REG_REV_B00; + else + nv3->pmc.boot = NV3_BOOT_REG_REV_C00; + nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_SOFTWARE; nv_log("NV3: Initialising PMC: Done\n"); From 4c2477a4cf05c040f4368e6149034dd6a7c849f5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 9 Dec 2024 11:57:54 +0000 Subject: [PATCH 009/274] Add read and write PTIMER registers. Doesn't actually do anything as a timer yet. Fix software and hardware interrupts being the wrong way around --- src/include/86box/nv/vid_nv.h | 8 +++- src/include/86box/nv/vid_nv3.h | 5 +++ src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- src/video/nv/nv3/subsystems/nv3_ptimer.c | 47 +++++++++++++++++++++++- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index bac0220ea..31191833f 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -290,8 +290,12 @@ typedef struct nv3_pextdev_s typedef struct nv3_ptimer_s { - uint32_t interrupt_status; // Interrupt status - uint32_t interrupt_enable; // Interrupt enable + uint32_t interrupt_status; // PTIMER Interrupt status + uint32_t interrupt_enable; // PTIMER Interrupt enable + uint32_t clock_numerator; // PTIMER (tick?) numerator + uint32_t clock_denominator; // PTIMER (tick?) denominator + uint64_t time; // time + uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; // Graphics object hashtable diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 1e9b8064c..1a2f75d0b 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -194,6 +194,11 @@ extern const device_config_t nv3_config[]; #define NV3_PTIMER_START 0x9000 // Programmable Interval Timer #define NV3_PTIMER_INTR 0x9100 #define NV3_PTIMER_INTR_EN 0x9140 +#define NV3_PTIMER_NUMERATOR 0x9200 +#define NV3_PTIMER_DENOMINATOR 0x9210 +#define NV3_PTIMER_TIME_0_NSEC 0x9400 // nanoseconds [31:5] +#define NV3_PTIMER_TIME_1_NSEC 0x9410 // nanoseconds [28:0] +#define NV3_PTIMER_ALARM_NSEC 0x9420 // nanoseconds [31:5] #define NV3_PTIMER_END 0x9FFF #define NV3_VGA_VRAM_START 0xA0000 // VGA Emulation VRAM #define NV3_VGA_VRAM_END 0xBFFFF diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 033563998..d0cbdae3b 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -797,7 +797,7 @@ void* nv3_init(const device_t *info) 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 + nv3_pvideo_init(); // Initialise video overlay engine return nv3; } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 50eccfae3..379c4ed7c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -137,7 +137,7 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) return nv3->pmc.interrupt_status; } - if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) + if ((nv3->pmc.interrupt_status & 0x7FFFFFFF)) { if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_HARDWARE) { diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 9e15a1035..1a6c9d73d 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -32,6 +32,11 @@ nv_register_t ptimer_registers[] = { { NV3_PTIMER_INTR, "PTIMER - Interrupt Status", NULL, NULL}, { NV3_PTIMER_INTR_EN, "PTIMER - Interrupt Enable", NULL, NULL,}, + { NV3_PTIMER_NUMERATOR, "PTIMER - Numerator", NULL, NULL, }, + { NV3_PTIMER_DENOMINATOR, "PTIMER - Denominator", NULL, NULL, }, + { NV3_PTIMER_TIME_0_NSEC, "PTIMER - Time0", NULL, NULL, }, + { NV3_PTIMER_TIME_1_NSEC, "PTIMER - Time1", NULL, NULL, }, + { NV3_PTIMER_ALARM_NSEC, "PTIMER - Alarm", NULL, NULL, }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -75,6 +80,24 @@ uint32_t nv3_ptimer_read(uint32_t address) return nv3->ptimer.interrupt_status; case NV3_PTIMER_INTR_EN: return nv3->ptimer.interrupt_enable; + case NV3_PTIMER_NUMERATOR: + return nv3->ptimer.clock_denominator; // 15:0 + break; + case NV3_PTIMER_DENOMINATOR: + return nv3->ptimer.clock_denominator ; //15:0 + break; + // 64-bit value + // High part + case NV3_PTIMER_TIME_0_NSEC: + return nv3->ptimer.time & 0xFFFFFFFF; //28:0 + break; + // Low part + case NV3_PTIMER_TIME_1_NSEC: + return nv3->ptimer.time >> 32; // 31:5 + break; + case NV3_PTIMER_ALARM_NSEC: + return nv3->ptimer.alarm; // 31:5 + break; } } } @@ -112,7 +135,29 @@ void nv3_ptimer_write(uint32_t address, uint32_t value) nv3_pmc_clear_interrupts(); break; case NV3_PTIMER_INTR_EN: - nv3->ptimer.interrupt_enable = value & 0x00000001; + nv3->ptimer.interrupt_enable = value & 0x1; + break; + case NV3_PTIMER_NUMERATOR: + nv3->ptimer.clock_denominator = value & 0xFFFF; // 15:0 + break; + case NV3_PTIMER_DENOMINATOR: + // prevent Div0 + if (!value) + value = 1; + + nv3->ptimer.clock_denominator = value & 0xFFFF; //15:0 + break; + // 64-bit value + // High part + case NV3_PTIMER_TIME_0_NSEC: + nv3->ptimer.time = (value >> 32) & 0xFFFFFFE0; //28:0 + break; + // Low part + case NV3_PTIMER_TIME_1_NSEC: + nv3->ptimer.time = ((value & 0xFFFFFFE0) << 32); // 31:5 + break; + case NV3_PTIMER_ALARM_NSEC: + nv3->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 break; } } From 2f1cac86a77b9fd4b7e3d6c82ab9f68b1c7f5474 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 13 Dec 2024 18:33:37 +0000 Subject: [PATCH 010/274] This is just sot hat we can sync --- src/include/86box/nv/vid_nv3.h | 2 ++ src/video/nv/nv3/subsystems/nv3_ptimer.c | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 1a2f75d0b..f95d1c5f4 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -193,6 +193,7 @@ extern const device_config_t nv3_config[]; #define NV3_PRMIO_END 0x7FFF #define NV3_PTIMER_START 0x9000 // Programmable Interval Timer #define NV3_PTIMER_INTR 0x9100 +#define NV3_PTIMER_INTR_ALARM 0 // Alarm interrupt #define NV3_PTIMER_INTR_EN 0x9140 #define NV3_PTIMER_NUMERATOR 0x9200 #define NV3_PTIMER_DENOMINATOR 0x9210 @@ -301,6 +302,7 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_INTR_1 0x400104 #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 #define NV3_PGRAPH_INTR_EN_0_VBLANK 8 // Fired every frame +#define NV3_PGRAPH_INTR_EN_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? //todo: add what this does #define NV3_PGRAPH_INTR_EN_1 0x400180 // Interrupt Control for PGRAPH #2 (it can receive two at onc) diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 1a6c9d73d..1acebc98f 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -48,14 +48,28 @@ void nv3_ptimer_init() nv_log("Done!\n"); } +// Handles the PTIMER alarm interrupt +void nv3_ptimer_interrupt(uint32_t num) +{ + nv3->ptimer.interrupt_enable |= (1 << num); + nv3_pmc_handle_interrupts(true); +} + +// Ticks the timer. +void nv3_ptimer_tick() +{ + if (nv3->ptimer.time >= nv3->ptimer.alarm) + { + nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); + } +} + uint32_t nv3_ptimer_read(uint32_t address) { - // before doing anything, check the subsystem enablement + // always enabled 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 From 6a1979bef82c81616b44c5a0e05633e1ad71adf3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 20 Dec 2024 18:05:34 +0000 Subject: [PATCH 011/274] Actually add the memory and pixel clocks --- src/include/86box/nv/vid_nv.h | 5 ++++- src/video/nv/nv3/nv3_core.c | 20 ++++++++++++++++++++ src/video/nv/nv3/nv3_core_arbiter.c | 2 -- src/video/nv/nv3/subsystems/nv3_ptimer.c | 10 +++++++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 31191833f..e9bd9041c 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -82,7 +82,7 @@ typedef struct nv_base_s { rom_t vbios; // NVIDIA/OEm VBIOS // move to nv3_cio_t? - svga_t svga; // SVGA core (separate to nv3) + svga_t svga; // SVGA core (separate to nv3) - Weitek licensed // 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 @@ -98,6 +98,8 @@ typedef struct nv_base_s 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) uint32_t gpu_revision; // GPU Stepping + pc_timer_t pixel_clock_timer; // Pixel Clock Timer + pc_timer_t memory_clock_timer; // Memory Clock Timer } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E @@ -497,6 +499,7 @@ void nv3_pramdac_set_pixel_clock(); // NV3 PTIMER void nv3_ptimer_init(); +void nv3_ptimer_tick(); // NV3 PVIDEO void nv3_pvideo_init(); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index d0cbdae3b..fa85b6f7e 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -728,6 +728,20 @@ void nv3_update_mappings() } } +// Polls the pixel clock. +// This updates the 2D/3D engine PGRAPH +void nv3_pixel_clock_poll(void* priv) +{ + +} + +// Polls the memory clock. +void nv3_memory_clock_poll(void* poll) +{ + // Let's hope qeeg was right here. + nv3_ptimer_tick(); +} + // // Init code // @@ -799,6 +813,12 @@ void* nv3_init(const device_t *info) nv3_ptimer_init(); // Initialise programmable interval timer nv3_pvideo_init(); // Initialise video overlay engine + nv_log("NV3: Starting timers..."); + + // Add the + timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pixel_clock_poll, nv3, true); + timer_add(&nv3->nvbase.memory_clock_timer, nv3_memory_clock_poll, nv3, true); + return nv3; } diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 1ef1c0e36..c52be55e2 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -180,8 +180,6 @@ 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; }; diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 1acebc98f..990f9a7fb 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -51,13 +51,21 @@ void nv3_ptimer_init() // Handles the PTIMER alarm interrupt void nv3_ptimer_interrupt(uint32_t num) { - nv3->ptimer.interrupt_enable |= (1 << num); + nv3->ptimer.interrupt_status |= (1 << num); + nv3_pmc_handle_interrupts(true); } // Ticks the timer. void nv3_ptimer_tick() { + // get the current time + double current_time = ((double)nv3->ptimer.clock_numerator) / (double)nv3->ptimer.clock_denominator; // *10.0? + + // truncate it + nv3->ptimer.time += (uint64_t)current_time; + + // Check if the alarm has actually triggered... if (nv3->ptimer.time >= nv3->ptimer.alarm) { nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); From 60f2828aebb361fbe93f5226608828bbbda52c37 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 20 Dec 2024 19:00:59 +0000 Subject: [PATCH 012/274] fix ptimer numerator and time registers --- src/video/nv/nv3/subsystems/nv3_ptimer.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 990f9a7fb..75b1a3988 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -103,23 +103,18 @@ uint32_t nv3_ptimer_read(uint32_t address) case NV3_PTIMER_INTR_EN: return nv3->ptimer.interrupt_enable; case NV3_PTIMER_NUMERATOR: - return nv3->ptimer.clock_denominator; // 15:0 - break; + return nv3->ptimer.clock_numerator; // 15:0 case NV3_PTIMER_DENOMINATOR: return nv3->ptimer.clock_denominator ; //15:0 - break; // 64-bit value // High part case NV3_PTIMER_TIME_0_NSEC: return nv3->ptimer.time & 0xFFFFFFFF; //28:0 - break; // Low part case NV3_PTIMER_TIME_1_NSEC: return nv3->ptimer.time >> 32; // 31:5 - break; case NV3_PTIMER_ALARM_NSEC: return nv3->ptimer.alarm; // 31:5 - break; } } } @@ -156,11 +151,14 @@ void nv3_ptimer_write(uint32_t address, uint32_t value) nv3->ptimer.interrupt_status &= ~value; nv3_pmc_clear_interrupts(); break; + + // Interrupt enablement state case NV3_PTIMER_INTR_EN: nv3->ptimer.interrupt_enable = value & 0x1; break; + // case NV3_PTIMER_NUMERATOR: - nv3->ptimer.clock_denominator = value & 0xFFFF; // 15:0 + nv3->ptimer.clock_numerator = value & 0xFFFF; // 15:0 break; case NV3_PTIMER_DENOMINATOR: // prevent Div0 @@ -172,11 +170,11 @@ void nv3_ptimer_write(uint32_t address, uint32_t value) // 64-bit value // High part case NV3_PTIMER_TIME_0_NSEC: - nv3->ptimer.time = (value >> 32) & 0xFFFFFFE0; //28:0 + nv3->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 break; // Low part case NV3_PTIMER_TIME_1_NSEC: - nv3->ptimer.time = ((value & 0xFFFFFFE0) << 32); // 31:5 + nv3->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 break; case NV3_PTIMER_ALARM_NSEC: nv3->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 From 2f2fb2724de873e9fe123eadd998126b1005270e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 20 Dec 2024 20:14:38 +0000 Subject: [PATCH 013/274] Correctly set the timers, but setting it all breaks? --- src/include/86box/nv/vid_nv.h | 4 ++ src/include/86box/nv/vid_nv3.h | 3 + src/video/nv/nv3/nv3_core.c | 23 ++----- src/video/nv/nv3/subsystems/nv3_pramdac.c | 75 ++++++++++++++++------- src/video/nv/nv3/subsystems/nv3_ptimer.c | 13 +++- 5 files changed, 76 insertions(+), 42 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index e9bd9041c..930244584 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -98,7 +98,9 @@ typedef struct nv_base_s 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) uint32_t gpu_revision; // GPU Stepping + double pixel_clock_period; // Period in seconds for pixel clock pc_timer_t pixel_clock_timer; // Pixel Clock Timer + double memory_clock_period; // Period in seconds for pixel clock pc_timer_t memory_clock_timer; // Memory Clock Timer } nv_base_t; @@ -496,6 +498,8 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val); void nv3_pramdac_init(); void nv3_pramdac_set_vram_clock(); void nv3_pramdac_set_pixel_clock(); +void nv3_pramdac_pixel_clock_poll(void* priv); +void nv3_pramdac_memory_clock_poll(void* priv); // NV3 PTIMER void nv3_ptimer_init(); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index f95d1c5f4..ef7041505 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -29,6 +29,9 @@ extern const device_config_t nv3_config[]; #define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start #define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN +#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 100 // The amount by which we have to ration out the memory clock because it's not fast enough... + // Multiply by this value to get the real clock speed. + // various vbioses for testing // Coming soon: MIROmagic Premium BIOS (when I get mine dumped) //todo: move to hash system diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index fa85b6f7e..9d7894f0c 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -728,19 +728,7 @@ void nv3_update_mappings() } } -// Polls the pixel clock. -// This updates the 2D/3D engine PGRAPH -void nv3_pixel_clock_poll(void* priv) -{ -} - -// Polls the memory clock. -void nv3_memory_clock_poll(void* poll) -{ - // Let's hope qeeg was right here. - nv3_ptimer_tick(); -} // // Init code @@ -813,12 +801,13 @@ void* nv3_init(const device_t *info) nv3_ptimer_init(); // Initialise programmable interval timer nv3_pvideo_init(); // Initialise video overlay engine - nv_log("NV3: Starting timers..."); - - // Add the - timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pixel_clock_poll, nv3, true); - timer_add(&nv3->nvbase.memory_clock_timer, nv3_memory_clock_poll, nv3, true); + nv_log("NV3: Initialising timers...\n"); + // These only get turned on when the requisite registers are twiddled + timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pramdac_pixel_clock_poll, nv3, false); + nv_log("NV3: Pixel clock OK. Will be started when the VBIOS tells us to start\n"); + timer_add(&nv3->nvbase.memory_clock_timer, nv3_pramdac_memory_clock_poll, nv3, false); + nv_log("NV3: Memory clock OK. Will be started when the VBIOS tells us to start\n"); return nv3; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 39b5b918f..08270aa8b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -42,15 +42,34 @@ void nv3_pramdac_init() 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"); } +// Polls the pixel clock. +// This updates the 2D/3D engine PGRAPH +void nv3_pramdac_pixel_clock_poll(void* priv) +{ + timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); + +} + +// Polls the memory clock. +void nv3_pramdac_memory_clock_poll(void* poll) +{ + // Let's hope qeeg was right here. + nv3_ptimer_tick(); + + timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); +} + +// Gets the vram clock register. uint32_t nv3_pramdac_get_vram_clock_register() { - // pack into 19 bits + // the clock format is packed into 19 bits // M divisor [7-0] // N divisor [16-8] // P divisor [18-16] @@ -95,39 +114,51 @@ void nv3_pramdac_set_vram_clock() else frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); + double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box + nv_log("NV3: Memory clock = %.2f MHz\n", frequency / 1000000.0f); + + nv3->nvbase.memory_clock_period = time; + //Breaks everything? + //timer_set_delay_u64(&nv3->nvbase.memory_clock_timer, time * TIMER_USEC); // do we need to decrease } 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 + // frequency divider algorithm from old varcem/86box/pcbox riva driver, + // verified by reversing NT drivers v1.50e CalcMNP [symbols] function - // todo: actually implement it + // 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; - // } + // 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; + float frequency = 13500000.0f; - // prevent division by 0 - if (nv3->pramdac.pixel_clock_m == 0) - nv3->pramdac.pixel_clock_m == 1; - else + // 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; + nv3->nvbase.svga.clock = cpuclock / frequency; - nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); + double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box + + nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); + + nv3->nvbase.pixel_clock_period = time; + //Breaks everything? + //timer_set_delay_u64(&nv3->nvbase.pixel_clock_timer, time * TIMER_USEC); // do we need to decrease } // diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 75b1a3988..ca86716c0 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -59,8 +59,15 @@ void nv3_ptimer_interrupt(uint32_t num) // Ticks the timer. void nv3_ptimer_tick() { + // do not divide by zero + if (nv3->ptimer.clock_numerator == 0 + || nv3->ptimer.clock_denominator == 0) + return; + // get the current time - double current_time = ((double)nv3->ptimer.clock_numerator) / (double)nv3->ptimer.clock_denominator; // *10.0? + // Due to timer system limitations, the timer system is not capable of running at 100 megahertz. Therefore, we have to scale it down and then scale up the level of changes + // to the state. + double current_time = ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? // truncate it nv3->ptimer.time += (uint64_t)current_time; @@ -127,7 +134,7 @@ 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); + nv_log("NV3: PTIMER Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) @@ -156,7 +163,7 @@ void nv3_ptimer_write(uint32_t address, uint32_t value) case NV3_PTIMER_INTR_EN: nv3->ptimer.interrupt_enable = value & 0x1; break; - // + // nUMERATOR case NV3_PTIMER_NUMERATOR: nv3->ptimer.clock_numerator = value & 0xFFFF; // 15:0 break; From a1a15a83d4e34b784968e554730f666ca7458cf9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 20 Dec 2024 20:16:58 +0000 Subject: [PATCH 014/274] how do you even manage that --- CMakePresets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 94dfd3d79..c7bcba52a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -66,7 +66,7 @@ "NEW_DYNAREC": "ON", "QT": "ON", "USE_QT6": "OFF", - "Qt5_DIR": "/opt/homebrew/opt/qt@5/lSib/cmake/Qt5", + "Qt5_DIR": "/opt/homebrew/opt/qt@5/lib/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", From d2d08d16c184304e86a3f9b62774e06b551a824a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 20 Dec 2024 21:20:03 +0000 Subject: [PATCH 015/274] Fix the timers...but they run slow. --- src/include/86box/nv/vid_nv.h | 2 ++ src/include/86box/nv/vid_nv3.h | 2 +- src/video/nv/nv3/nv3_core.c | 1 + src/video/nv/nv3/subsystems/nv3_pramdac.c | 32 ++++++++++++++++------- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 930244584..b7c5fa29a 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -100,8 +100,10 @@ typedef struct nv_base_s uint32_t gpu_revision; // GPU Stepping double pixel_clock_period; // Period in seconds for pixel clock pc_timer_t pixel_clock_timer; // Pixel Clock Timer + bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_period; // Period in seconds for pixel clock pc_timer_t memory_clock_timer; // Memory Clock Timer + bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index ef7041505..336f6e67b 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -29,7 +29,7 @@ extern const device_config_t nv3_config[]; #define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start #define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN -#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 100 // The amount by which we have to ration out the memory clock because it's not fast enough... +#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1000 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. // various vbioses for testing diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 9d7894f0c..cf4946c44 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -346,6 +346,7 @@ void nv3_close(void* priv) { svga_close(&nv3->nvbase.svga); free(nv3); + nv3 = NULL; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 08270aa8b..3dec7329a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -53,17 +53,20 @@ void nv3_pramdac_init() // This updates the 2D/3D engine PGRAPH void nv3_pramdac_pixel_clock_poll(void* priv) { - timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); + nv3_t* nv3_poll = (nv3_t*)priv; + timer_on_auto(&nv3_poll->nvbase.pixel_clock_timer, nv3_poll->nvbase.pixel_clock_period); } // Polls the memory clock. -void nv3_pramdac_memory_clock_poll(void* poll) +void nv3_pramdac_memory_clock_poll(void* priv) { + nv3_t* nv3_poll = (nv3_t*)priv; + // Let's hope qeeg was right here. nv3_ptimer_tick(); - timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); + timer_on_auto(&nv3_poll->nvbase.memory_clock_timer, nv3_poll->nvbase.memory_clock_period); } // Gets the vram clock register. @@ -110,15 +113,21 @@ void nv3_pramdac_set_vram_clock() // 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); + nv3->pramdac.memory_clock_m = 1; + + if (nv3->pramdac.memory_clock_n == 0) + nv3->pramdac.memory_clock_n = 1; + + frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box nv_log("NV3: Memory clock = %.2f MHz\n", frequency / 1000000.0f); nv3->nvbase.memory_clock_period = time; + + timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); + //Breaks everything? //timer_set_delay_u64(&nv3->nvbase.memory_clock_timer, time * TIMER_USEC); // do we need to decrease } @@ -146,9 +155,12 @@ void nv3_pramdac_set_pixel_clock() // 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->pramdac.pixel_clock_m = 1; + + if (nv3->pramdac.memory_clock_n == 0) + nv3->pramdac.memory_clock_n = 1; + + frequency = (frequency * nv3->pramdac.pixel_clock_n) / (nv3->pramdac.pixel_clock_m << nv3->pramdac.pixel_clock_p); nv3->nvbase.svga.clock = cpuclock / frequency; @@ -157,6 +169,8 @@ void nv3_pramdac_set_pixel_clock() nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); nv3->nvbase.pixel_clock_period = time; + + timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); //Breaks everything? //timer_set_delay_u64(&nv3->nvbase.pixel_clock_timer, time * TIMER_USEC); // do we need to decrease } From 692efc3f0f8e23c9abc3b74cb21df5eaa811c961 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 22 Dec 2024 22:51:08 +0000 Subject: [PATCH 016/274] add a bunch more pgraph headers --- src/include/86box/nv/vid_nv3.h | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 336f6e67b..48ba92526 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -301,13 +301,40 @@ extern const device_config_t nv3_config[]; #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 +// PGRAPH Core +#define NV3_PGRAPH_DEBUG_0 0x400080 +#define NV3_PGRAPH_DEBUG_1 0x400084 +#define NV3_PGRAPH_DEBUG_2 0x400088 +#define NV3_PGRAPH_DEBUG_3 0x40008C #define NV3_PGRAPH_INTR_0 0x400100 #define NV3_PGRAPH_INTR_1 0x400104 #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 #define NV3_PGRAPH_INTR_EN_0_VBLANK 8 // Fired every frame #define NV3_PGRAPH_INTR_EN_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? //todo: add what this does -#define NV3_PGRAPH_INTR_EN_1 0x400180 // Interrupt Control for PGRAPH #2 (it can receive two at onc) +#define NV3_PGRAPH_INTR_EN_1 0x400144 // Interrupt Control for PGRAPH #2 (it can receive two at onc) +#define NV3_PGRAPH_CONTEXT_SWITCH 0x400180 // DMA context switcher +#define NV3_PGRAPH_CONTEXT_CONTROL 0x400190 // DMA context control +#define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename +#define NV3_PGRAPH_CONTEXT_CACHE(i) 0x4001A0+(i*4) // Context Cache +#define NV3_PGRAPH_CONTEXT_CACHE_SIZE 8 +#define NV3_PGRAPH_SRC_CANVAS_MIN 0x440550 // Minimum Source Canvas for Blit, X,Y position +#define NV3_PGRAPH_SRC_CANVAS_MAX 0x440554 // Maximum Source Canvas for Blit, X,Y position +#define NV3_PGRAPH_DST_CANVAS_MIN 0x440558 // Minimum Destination Canvas for Blit, X,Y position +#define NV3_PGRAPH_DST_CANVAS_MAX 0x44055C // Maximum Destination Canvas for Blit, X,Y position +#define NV3_PGRAPH_CLIP0_MIN 0x440690 // Clip for Blitting 0 Min +#define NV3_PGRAPH_CLIP0_MAX 0x440694 // Clip for Blitting 0 Max +#define NV3_PGRAPH_CLIP1_MIN 0x440698 // Clip for Blitting 1 Min +#define NV3_PGRAPH_CLIP1_MAX 0x44069C // Clip for Blitting 1 Max +#define NV3_PGRAPH_FIFO_ACCESS 0x4006A4 // Is PGRAPH enabled? +#define NV3_PGRAPH_FIFO_ACCESS_DISABLED 0x0 +#define NV3_PGRAPH_FIFO_ACCESS_ENABLED 0x1 +#define NV3_PGRAPH_CLIP_MISC 0x4006A0 // Miscellaneous clipping information +#define NV3_PGRAPH_STATUS 0x4006B0 // Current PGRAPH status +#define NV3_PGRAPH_TRAPPED_ADDRESS 0x4006B4 +#define NV3_PGRAPH_TRAPPED_DATA 0x4006B8 +#define NV3_PGRAPH_TRAPPED_INSTANCE 0x4006BC + // 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) From 86c52e657b73e0c0c2bf566a2654dae8cc04e07f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 25 Dec 2024 22:22:46 +0000 Subject: [PATCH 017/274] add sanity checks --- src/video/nv/nv3/nv3_core.c | 42 ++++++++++++++++++++++++++--- src/video/nv/nv3/nv3_core_arbiter.c | 8 ++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index cf4946c44..85abf5ba9 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -100,6 +100,10 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) { + // sanity check + if (!nv3) + return; + uint8_t ret = 0x00; // figure out what size this gets read as first @@ -249,10 +253,12 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) { - // TOTAL IRRELEVANCY + // sanity check + if (!nv3) + return; // 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 + // as an example, 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; @@ -354,7 +360,11 @@ void nv3_close(void* priv) // SVGA functions // void nv3_recalc_timings(svga_t* svga) -{ +{ + // sanity check + if (!nv3) + return; + nv3_t* nv3 = (nv3_t*)svga->priv; svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; @@ -405,6 +415,10 @@ void nv3_recalc_timings(svga_t* svga) void nv3_speed_changed(void* priv) { + // sanity check + if (!nv3) + return; + nv3_recalc_timings(&nv3->nvbase.svga); } @@ -412,12 +426,20 @@ void nv3_speed_changed(void* priv) // Reset etc. void nv3_force_redraw(void* priv) { + // sanity check + if (!nv3) + return; + nv3->nvbase.svga.fullchange = changeframecount; } // Read from SVGA core memory uint8_t nv3_svga_in(uint16_t addr, void* priv) { + // sanity check + if (!nv3) + return; + nv3_t* nv3 = (nv3_t*)priv; uint8_t ret = 0x00; @@ -466,6 +488,9 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) // Write to SVGA core memory void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) { + // sanity check + if (!nv3) + return; // If we need to RMA to GPU MMIO, go do that if (addr >= NV3_RMA_REGISTER_START @@ -550,6 +575,12 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) void nv3_draw_cursor(svga_t* svga, int32_t drawline) { + // sanity check + if (!nv3) + return; + + // this is a 2kb bitmap in vram...somewhere... + nv_log("nv3_draw_cursor drawline=0x%04x", drawline); } @@ -633,7 +664,10 @@ void nv3_init_mappings() // Updates the mappings after initialisation. void nv3_update_mappings() { - + // sanity check + if (!nv3) + return; + // setting this to 0 doesn't seem to disable it, based on the datasheet nv_log("\nMemory Mapping Config Change:\n"); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index c52be55e2..9f13f29d5 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -52,6 +52,10 @@ nv_register_t* nv_get_register(uint32_t address, nv_register_t* register_list, u // Arbitrates an MMIO read uint32_t nv3_mmio_arbitrate_read(uint32_t address) { + // sanity check + if (!nv3) + return; + uint32_t ret = 0x00; // note: some registers are byte aligned not dword aligned @@ -113,6 +117,10 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) { + // sanity check + if (!nv3) + return; + // 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; From a426db895607bd46ae8c2a9fdcf45da70e7dc3c2 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 25 Dec 2024 22:24:41 +0000 Subject: [PATCH 018/274] fix build --- src/video/nv/nv3/nv3_core.c | 15 ++++++++------- src/video/nv/nv3/nv3_core_arbiter.c | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 85abf5ba9..e78d624e1 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -100,11 +100,11 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) { + uint8_t ret = 0x00; + // sanity check if (!nv3) - return; - - uint8_t ret = 0x00; + return ret; // figure out what size this gets read as first // seems func does not matter at least here? @@ -436,14 +436,15 @@ void nv3_force_redraw(void* priv) // Read from SVGA core memory uint8_t nv3_svga_in(uint16_t addr, void* priv) { - // sanity check - if (!nv3) - return; nv3_t* nv3 = (nv3_t*)priv; uint8_t ret = 0x00; + // sanity check + if (!nv3) + return ret; + // If we need to RMA from GPU MMIO, go do that if (addr >= NV3_RMA_REGISTER_START && addr <= NV3_RMA_REGISTER_END) @@ -667,7 +668,7 @@ void nv3_update_mappings() // sanity check if (!nv3) return; - + // setting this to 0 doesn't seem to disable it, based on the datasheet nv_log("\nMemory Mapping Config Change:\n"); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 9f13f29d5..41799c760 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -54,7 +54,7 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) { // sanity check if (!nv3) - return; + return 0x00; uint32_t ret = 0x00; From 8e21fa55459a6b43799d3adbb4479b67655014b8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 15:57:55 +0000 Subject: [PATCH 019/274] Accumulated changes. Update copyright year in anticipation of 2025. More PGRAPH stuff. Start working on guest CPU independent timer (RivaTimer) but not really tested or used yet. Add more docs --- doc/RIVA fansites.txt | 24 ++ doc/Versions.txt | 8 + doc/gpucompanies.txt | 63 +++++ src/86box.c | 4 + src/include/86box/nv/vid_nv3.h | 34 ++- src/include/86box/nv/vid_nv_rivatimer.h | 83 ++++++ src/timer.c | 4 + src/video/CMakeLists.txt | 4 +- src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/nv3_core_arbiter.c | 2 +- src/video/nv/nv3/nv3_core_config.c | 2 +- src/video/nv/nv3/nv3_interrupt.c | 0 src/video/nv/nv3/subsystems/nv3_pbus.c | 2 +- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 2 +- src/video/nv/nv3/subsystems/nv3_pextdev.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 +- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- src/video/nv/nv3/subsystems/nv3_pme.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 2 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 2 +- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 2 +- src/video/nv/nv3/subsystems/nv3_ptimer.c | 2 +- src/video/nv/nv3/subsystems/nv3_pvideo.c | 2 +- src/video/nv/nv_base.c | 2 +- src/video/nv/nv_rivatimer.c | 248 ++++++++++++++++++ 29 files changed, 481 insertions(+), 29 deletions(-) create mode 100644 doc/RIVA fansites.txt create mode 100644 doc/Versions.txt create mode 100644 doc/gpucompanies.txt create mode 100644 src/include/86box/nv/vid_nv_rivatimer.h delete mode 100644 src/video/nv/nv3/nv3_interrupt.c create mode 100644 src/video/nv/nv_rivatimer.c diff --git a/doc/RIVA fansites.txt b/doc/RIVA fansites.txt new file mode 100644 index 000000000..5a3104837 --- /dev/null +++ b/doc/RIVA fansites.txt @@ -0,0 +1,24 @@ +RIVA fansites (?????): +RIVA User's Group http://tiger.tnstate.edu:8080/ +RIVA 128 Homepage http://pages.prodigy.net/babblin5/Main.html -> Riva3D (riva3d.com) +Zone 128 http://www.tc.umn.edu/~reda0003/zone128/ +RIVAZone https://web.archive.org/web/19981212032348/http://www.rivazone.com/ (Launched January 2, 1998) +Dimension 128 (early domain name that was never archived) -> d128.com (1999-2001) +Riva3D https://web.archive.org/web/20000525110305/http://riva3d.gxnetwork.com/s3.html +nVNews https://web.archive.org/web/20001205171202/http://www.nvnews.net/ (1999-2015) + +BluesNews has stuff https://www.bluesnews.com/archives/ +(July 1996-present!) + +https://www.bluesnews.com/archives/july97-3.html July 21, 1997 ("unreleased nvidia RIVA chipset is indeed faster than 3dfx" (carmack) + +"However, using the nvidia RIVA 128 chip it runs 1 f/s faster" + +https://web.archive.org/web/19980615024744/http://www.ogr.com/columns/techtalk/technology_talk_0611_2.shtml First mention of NV10 (June 1998) + +"RIVA 128 Turbo" early ZX name +"Riva4" early TNT name + +https://web.archive.org/web/20001002193706/http://www.rivazone.com/files/rivalog.txt + +https://web.archive.org/web/20010422044820/http://www.rivazone.com/finger/finger.cgi?nick@finger.nvidia.com nvidia .plan files \ No newline at end of file diff --git a/doc/Versions.txt b/doc/Versions.txt new file mode 100644 index 000000000..052de854f --- /dev/null +++ b/doc/Versions.txt @@ -0,0 +1,8 @@ +Name Base Version Notes Date API Support Platform +0.75_nt4 Version 0.75 No Resource Manager (miniport) 1997-08-15 GDI NT4.0 +0.77_win9x Version 0.77 Symbols (COFF/VXD) 1997-09-02 GDI, D3D5 +nv3quake.zip Version 1.21 OpenGL Alpha 1 1997-11-14 GDI, D3D5, OpenGL 1.1 (alpha; Build 151) Win9x +quakea2f.zip Version 1.21 OpenGL Alpha 2 1997-12-02 GDI, D3D5, OpenGL 1.1 (alpha; Build 258) Win9x +win95_131.zip Version 1.31 OpenGL Beta 1 1998-02-04 GDI, D3D5, OpenGL 1.1 (beta; Build 661) Win9x + + diff --git a/doc/gpucompanies.txt b/doc/gpucompanies.txt new file mode 100644 index 000000000..08a6c61c5 --- /dev/null +++ b/doc/gpucompanies.txt @@ -0,0 +1,63 @@ +GPU companies (the period they made gpus) + +DEC 19xx-1998 +HP 19xx-2000+ (possibly until present) +IBM 19xx-2002+ (possibly until present) +E&S 1968-2006 +Intergraph 1969-2000 +Apple 1976-present (some break) +Motorola 1977-1994+ (???) +TI 1979-1988+ +Matrox 1979-2014 +Hitachi 1981-1986 +SGI 1981-2009 +Intel 1983-present +Number Nine 1983-2000 +Tseng Labs 1983-1997 +Cirrus Logic 1984-1998 +Video 7 1985-1991 +C&T 1985-1999 +Imagination 1985-present +NCR 1986-1993 +Paradise/WD 1986-1996 +Gemini 1987-1990 +Genoa Systems 1987-1991 +Trident 1987-2003 +Oak Technology 1988-1997 +Realtek 1988-1997 +Compaq 1989-1991 +Sun 1989-2002(+?) +S3 Graphics 1989-2010 +Macronix 1989-1998 +Winbond 1989-1996+ +Tamarack 198x-1991+ +UMC 198x-1993 +Sigma Designs 198x-1996 +Acer 198x-1998 (roughly) +Fujitsu 198x-1998 +AMD 1991-present (really starting in 2006) +HMC 1991-1994 +Avance Logic 1991-1995 +Weitek <1991-1996 +Bitboys 1991-2006 +IIT 1992-1994 +Weitek 1992-1996(?) +Rendition 1993-1998 +ARK Logic 1993-1999 +S-MOS/Stellar 1994-1999 +Nvidia 1993-present +Alliance 1994-1997 +iGS 1994-1999 +3dfx 1994-2000 +3dlabs 1994-2006 +Silicon Motion 1995-200? +SiS 1995-2007 +Dynamic Pictures1996-1997 +NeoMagic 1996-2000 +GigaPixel 1997-2000 +Philips 1997-199x +Tatung 199x-199x +ASPEED 2004-present +Jingjia 2006-present + + diff --git a/src/86box.c b/src/86box.c index 5e1f58413..1bb24b055 100644 --- a/src/86box.c +++ b/src/86box.c @@ -103,6 +103,7 @@ #include <86box/machine_status.h> #include <86box/apm.h> #include <86box/acpi.h> +#include <86box/nv/vid_nv_rivatimer.h> // Disable c99-designator to avoid the warnings about int ng #ifdef __clang__ @@ -1421,6 +1422,9 @@ pc_run(void) pc_reset_hard_init(); } + /* Update the guest-CPU independent timer for devices with independent clock speed */ + rivatimer_update_all(); + /* Run a block of code. */ startblit(); cpu_exec((int32_t) cpu_s->rspeed / 100); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 48ba92526..7e8f6f7c1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -318,14 +318,30 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename #define NV3_PGRAPH_CONTEXT_CACHE(i) 0x4001A0+(i*4) // Context Cache #define NV3_PGRAPH_CONTEXT_CACHE_SIZE 8 -#define NV3_PGRAPH_SRC_CANVAS_MIN 0x440550 // Minimum Source Canvas for Blit, X,Y position -#define NV3_PGRAPH_SRC_CANVAS_MAX 0x440554 // Maximum Source Canvas for Blit, X,Y position -#define NV3_PGRAPH_DST_CANVAS_MIN 0x440558 // Minimum Destination Canvas for Blit, X,Y position -#define NV3_PGRAPH_DST_CANVAS_MAX 0x44055C // Maximum Destination Canvas for Blit, X,Y position -#define NV3_PGRAPH_CLIP0_MIN 0x440690 // Clip for Blitting 0 Min -#define NV3_PGRAPH_CLIP0_MAX 0x440694 // Clip for Blitting 0 Max -#define NV3_PGRAPH_CLIP1_MIN 0x440698 // Clip for Blitting 1 Min -#define NV3_PGRAPH_CLIP1_MAX 0x44069C // Clip for Blitting 1 Max +#define NV3_PGRAPH_ABS_UCLIP_XMIN 0x40053C // Clip X minimum +#define NV3_PGRAPH_ABS_UCLIP_XMAX 0x400540 // Clip X maximum +#define NV3_PGRAPH_ABS_UCLIP_YMIN 0x400544 // Clip Y minimum +#define NV3_PGRAPH_ABS_UCLIP_YMAX 0x400548 // Clip Y maximum +#define NV3_PGRAPH_SRC_CANVAS_MIN 0x400550 // Minimum Source Canvas for Blit, X,Y position +#define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, X,Y position +#define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, X,Y position +#define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, X,Y position +#define NV3_PGRAPH_PATTERN_COLOR_0_0 0x400600 +#define NV3_PGRAPH_PATTERN_COLOR_0_1 0x400604 +#define NV3_PGRAPH_PATTERN_COLOR_1_0 0x400608 +#define NV3_PGRAPH_PATTERN_COLOR_1_1 0x40060C // pattern color +#define NV3_PGRAPH_PATTERN_BITMAP_HIGH 0x400610 // pattern bitmap [31:0] +#define NV3_PGRAPH_PATTERN_BITMAP_LOW 0x400614 // pattern bitmap [63:32] +#define NV3_PGRAPH_PATTERN_SHAPE 0x400618 +#define NV3_PGRAPH_ROP3 0x400624 // ROP3 +#define NV3_PGRAPH_PLANE_MASK 0x400628 +#define NV3_PGRAPH_CHROMA_KEY 0x40062C +#define NV3_PGRAPH_BETA 0x400640 // Beta factor (30:23 fractional, 22:0 before fraction) +#define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH +#define NV3_PGRAPH_CLIP0_MIN 0x400690 // Clip for Blitting 0 Min +#define NV3_PGRAPH_CLIP0_MAX 0x400694 // Clip for Blitting 0 Max +#define NV3_PGRAPH_CLIP1_MIN 0x400698 // Clip for Blitting 1 Min +#define NV3_PGRAPH_CLIP1_MAX 0x40069C // Clip for Blitting 1 Max #define NV3_PGRAPH_FIFO_ACCESS 0x4006A4 // Is PGRAPH enabled? #define NV3_PGRAPH_FIFO_ACCESS_DISABLED 0x0 #define NV3_PGRAPH_FIFO_ACCESS_ENABLED 0x1 @@ -335,6 +351,8 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_TRAPPED_DATA 0x4006B8 #define NV3_PGRAPH_TRAPPED_INSTANCE 0x4006BC +#define NV3_PGRAPH_DMA_INTR_0 0x401000 // PGRAPH DMA Interrupt Status +#define NV3_PGRAPH_DMA_INTR_EN_0 0x401140 // PGRAPH DMA Interrupt Enable 0 // 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) diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h new file mode 100644 index 000000000..00e94cd97 --- /dev/null +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -0,0 +1,83 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Fast, high-frequency, guest CPU-independent timer for Riva emulation. + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +/* +RivaTimer + +This is a fast, high-frequency, guest CPU-independent timer. + +The main 86box timer is dependent on the TSC (time-stamp counter) register of the emulated CPU core. +This is fine for most purposes and has advantages in the fields of synchronisation and integrates neatly with +the clock dividers of the PC architecture, but in the case of the RIVA 128 it does not particularly suffice +(although it can be made to work with various techniques) since the clock source on the RIVA 128 is on the board itself +and the GPU has several different clocks that control different parts of the GPU (e.g., PTIMER runs on the memory clock but the core gpu is using the pixel clock). + +As faster graphics cards that offload more and more of the 3D graphics pipeline are emulated in the future, more and more work needs to be done by the emulator and +issues of synchronisation with a host CPU will simply make that work harder. Some features that are required for + +Architecture Brand Name 3D Features +NV1 (1995) NV1 Some weird URBS rectangle crap but feature set generally similar to nv3 but a bit worse +NV3 (1997) RIVA 128 (ZX) Triangle setup, edge-slope calculations, edge interpolation, span-slope calculations, span interpolation (Color-buffer, z-buffer, texture mapping, filtering) +NV4 (1998) RIVA TNT NV3 + 2x1 pixel pipelines + 32-bit colour + larger textures + trilinear + more ram (16mb) +NV5 (1999) RIVA TNT2 NV4 + higher clock speed +NV10 (1999) GeForce 256 NV5 + initial geometry transformation + lighting (8x lights) + MPEG-2 motion compensation + 4x1 pixel pipelines +NV15 (2000) GeForce 2 NV10 + First attempt at programmability + 4x2 pixel pipelines +NV20 (2001) GeForce 3 Programmable shaders! + +As you can see, the performance basically exponentially increases over a period of only 4 years. + +So I decided to create this timer that is completely separate from the CPU Core. +*/ + +#pragma once +#include +#include +#include +#include +#include <86Box\86box.h> + +#ifdef _WIN32 +#include +// Linux & MacOS should have the same API since OSX 10.12 +#else +#include +#endif + +typedef struct rivatimer_s +{ + struct rivatimer_s* prev; // Previous Rivatimer + double period; // Period in uS before firing + double value; // The current value of the rivatimer + bool running; // Is this RivaTimer running? + struct rivatimer_s* next; // Next RivaTimer + void (*callback)(); // Callback to call on fire + #ifdef _WIN32 + LARGE_INTEGER starting_time; // Starting time. + #else + struct timespec starting_time; // Starting time. + #endif + double time; // Accumulated time in uS. +} rivatimer_t; + +void rivatimer_init(); // Initialise the Rivatimer. +rivatimer_t* rivatimer_create(double period, void (*callback)()); +void rivatimer_destroy(rivatimer_t* rivatimer_ptr); + +void rivatimer_update_all(); +void rivatimer_start(rivatimer_t* rivatimer_ptr); +void rivatimer_stop(rivatimer_t* rivatimer_ptr); +double rivatimer_get_time(rivatimer_t* rivatimer_ptr); +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()); \ No newline at end of file diff --git a/src/timer.c b/src/timer.c index 6ddf8ebb5..2b92a1958 100644 --- a/src/timer.c +++ b/src/timer.c @@ -4,6 +4,7 @@ #include #include <86box/86box.h> #include <86box/timer.h> +#include <86Box/nv/vid_nv_rivatimer.h> uint64_t TIMER_USEC; uint32_t timer_target; @@ -168,6 +169,9 @@ timer_init(void) timer_target = 0ULL; tsc = 0; + /* Initialise the CPU-independent timer */ + rivatimer_init(); + timer_inited = 1; } diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index a7bc62326..4b927c9ea 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -28,8 +28,8 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.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 - nv/nv_base.c - nv/nv3/nv3_core.c nv/nv3/nv3_core_config.c nv/nv3/nv3_core_arbiter.c nv/nv3/nv3_interrupt.c + nv/nv_base.c nv/nv_rivatimer.c + nv/nv3/nv3_core.c nv/nv3/nv3_core_config.c nv/nv3/nv3_core_arbiter.c nv/nv3/subsystems/nv3_pramdac.c nv/nv3/subsystems/nv3_pfifo.c nv/nv3/subsystems/nv3_pgraph.c diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e78d624e1..f0ff0f028 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -13,7 +13,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include #include diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 41799c760..48c0c93db 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -16,7 +16,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ // STANDARD NV3 includes diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index d7944b69c..3bc8692b9 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -11,7 +11,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include #include diff --git a/src/video/nv/nv3/nv3_interrupt.c b/src/video/nv/nv3/nv3_interrupt.c deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index fddedd5cf..5d50bf2fb 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email dataess ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 825f326f2..8043fd758 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 761bacdb3..485763696 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -13,7 +13,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index e1d1c8352..9711f3c9a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 8f8abbac4..772839128 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index ac2970e35..b1696f950 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 379c4ed7c..f69b2b430 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index 9683ff5ab..8e0b7ddb5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 3dec7329a..ed89f9448 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ // nv3_pramdac.c: NV3 RAMDAC diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 9750e2f4c..7448f77c7 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -13,7 +13,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 9a397c8d7..0d141a222 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index baadb9666..5cb9e9d86 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 46c95deac..dd96adb40 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index ca86716c0..1eb53409a 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index 1aede7b0c..ea929ad2c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ #include diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index b67c65dc2..da776a721 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024 starfrost + * Copyright 2024-2025 starfrost */ // Common NV1/3/4... init diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c new file mode 100644 index 000000000..27c63088d --- /dev/null +++ b/src/video/nv/nv_rivatimer.c @@ -0,0 +1,248 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Fast, high-frequency, CPU-independent timer. + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +/* See vid_nv_rivatimer.h comments for rationale behind not using the regular timer system + +Notes applicable to this file: +Since Windows XP, QueryPerformanceCounter and QueryPerformanceFrequency cannot fail so they are not checked. + +*/ + +#include <86Box/nv/vid_nv_rivatimer.h> + +#ifdef _WIN32 +LARGE_INTEGER performance_frequency; +#endif + +rivatimer_t* rivatimer_head; // The head of the rivatimer list. +rivatimer_t* rivatimer_tail; // The tail of the rivatimer list. + +/* Functions only used in this translation unit */ +bool rivatimer_really_exists(rivatimer_t* rivatimer); // Determine if a rivatimer really exists in the linked list. + +void rivatimer_init() +{ + // Destroy all the rivatimers. + rivatimer_t* rivatimer_ptr = rivatimer_head; + + if (!rivatimer_ptr) + return; + + while (rivatimer_ptr->next) + rivatimer_destroy(rivatimer_ptr); + + #ifdef _WIN32 + // Query the performance frequency. + QueryPerformanceFrequency(&performance_frequency); + #endif +} + +// Creates a rivatimer. +rivatimer_t* rivatimer_create(double period, void (*callback)()) +{ + rivatimer_t* new_rivatimer = NULL; + + // See i + if (period <= 0 + || !callback) + { + fatal("Invalid rivatimer_create call: period <= 0 or no callback"); + } + + // If there are no rivatimers, create one + if (!rivatimer_head) + { + rivatimer_head = calloc(1, sizeof(rivatimer_t)); + rivatimer_head->prev = NULL; // indicate this is the first in the list even if we don't strictly need to + rivatimer_tail = rivatimer_head; + new_rivatimer = rivatimer_head; + } + else // Otherwise add a new one to the list + { + rivatimer_tail->next = calloc(1, sizeof(rivatimer_t)); + rivatimer_tail = rivatimer_tail->next; + new_rivatimer = rivatimer_tail; + } + + // sanity check + if (new_rivatimer) + { + new_rivatimer->running = false; + new_rivatimer->period = period; + new_rivatimer->next = NULL; // indicate this is the last in the list + new_rivatimer->callback = callback; + } + + return new_rivatimer; +} + +// Determines if a rivatimer really exists. +bool rivatimer_really_exists(rivatimer_t* rivatimer) +{ + rivatimer_t* current = rivatimer_head; + + if (!current) + return false; + + while (current->next != NULL) + { + if (current == rivatimer) + return true; + } + + return false; +} + +// Destroy a rivatimer. +void rivatimer_destroy(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place. Punch starfrost in the face"); + + // Case: We are destroying the head + if (rivatimer_ptr == rivatimer_head) + { + // This is the only rivatimer + if (rivatimer_ptr->next == NULL) + { + rivatimer_head = NULL; + rivatimer_tail = NULL; + } + // This is not the only rivatimer + else + { + rivatimer_head = rivatimer_ptr->next; + rivatimer_head->prev = NULL; + // This is the only rivatimer and now there is only one + if (!rivatimer_head->next) + rivatimer_tail = rivatimer_head; + } + } + // Case: We are destroying the tail + else if (rivatimer_ptr == rivatimer_tail) + { + // We already covered the case where there is only one item above + rivatimer_tail = rivatimer_ptr->prev; + rivatimer_tail->next = NULL; + } + // Case: This is not the first or last rivatimer, so we don't need to set the head or tail + else + { + // Fix the break in the chain that this + if (rivatimer_ptr->next) + rivatimer_ptr->prev->next = rivatimer_ptr->next; + if (rivatimer_ptr->prev) + rivatimer_ptr->next->prev = rivatimer_ptr->prev; + } + + free(rivatimer_ptr); + rivatimer_ptr = NULL; //explicitly set to null +} + +void rivatimer_update_all() +{ + rivatimer_t* rivatimer_ptr = rivatimer_head; + + if (!rivatimer_ptr) + return; + + while (rivatimer_ptr->next) + { + if (!rivatimer_ptr->running) + continue; + + #ifdef _WIN32 + LARGE_INTEGER current_time; + + QueryPerformanceCounter(¤t_time); + + double microseconds = ((double)current_time.QuadPart / 1000000.0) - (rivatimer_ptr->starting_time.QuadPart / 1000000.0); + #else + struct timespec current_time; + + clock_gettime(CLOCK_REALTIME, ¤t_time); + + double microseconds = ((double)current_time.tv_sec * 1000000.0) + ((double)current_time.tv_nsec / 1000.0); + #endif + + rivatimer_ptr->time += microseconds; + + // Reset the current time so we can actually restart + #ifdef _WIN32 + QueryPerformanceCounter(&rivatimer_ptr->starting_time); + #else + clock_gettime(CLOCK_REALTIME, &rivatimer_ptr->starting_time); + #endif + + // Time to fire + if (microseconds > rivatimer_ptr->period) + { + if (!rivatimer_ptr->callback) + { + pclog("Eh? No callback in RivaTimer?"); + continue; + } + + rivatimer_ptr->callback(); + } + } + +} + +void rivatimer_start(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + if (rivatimer_ptr->period <= 0) + fatal("rivatimer_start: Zero period!"); + + rivatimer_ptr->running = true; + + // Start off so rivatimer_update_all can actually update. + #ifdef _WIN32 + QueryPerformanceCounter(&rivatimer_ptr->starting_time); + #else + clock_gettime(CLOCK_REALTIME, &rivatimer_ptr->starting_time); + #endif +} + +void rivatimer_stop(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + rivatimer_ptr->running = false; +} + +// Get the current time value of a rivatimer +double rivatimer_get_time(rivatimer_t* rivatimer_ptr) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); +} + +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + if (!callback) + fatal("rivatimer_set_callback: No callback!"); + + rivatimer_ptr->callback = callback; +} \ No newline at end of file From 54cc969852c4039d4d1ac6eca74c451a6d94939c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 15:59:55 +0000 Subject: [PATCH 020/274] Implement rivatimer_get_time and reset time on rivatimer_stop. --- src/video/nv/nv_rivatimer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index 27c63088d..1ccde5a2f 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -227,6 +227,7 @@ void rivatimer_stop(rivatimer_t* rivatimer_ptr) fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); rivatimer_ptr->running = false; + rivatimer_ptr->time = 0; } // Get the current time value of a rivatimer @@ -234,6 +235,8 @@ double rivatimer_get_time(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + return rivatimer_ptr->time; } void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()) From 260323e9a6b8acbb5b3b6641142389e456f6affb Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 16:55:56 +0000 Subject: [PATCH 021/274] Architecture specific registers. Remove duplicate logging. Define the pgraph registers we have being written to so far. --- src/include/86box/nv/vid_nv.h | 384 ---------------------- src/include/86box/nv/vid_nv3.h | 397 ++++++++++++++++++++++- src/video/nv/nv3/nv3_core_arbiter.c | 3 - src/video/nv/nv3/subsystems/nv3_pgraph.c | 58 ++++ 4 files changed, 450 insertions(+), 392 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index b7c5fa29a..9a4a7c55b 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -127,389 +127,5 @@ typedef struct nv_register_s nv_register_t* nv_get_register(uint32_t address, nv_register_t* register_list, uint32_t num_regs); -// Default value for the boot information register. -// Depends on the chip -#define NV3_BOOT_REG_REV_A00 0x00030100 -#define NV3_BOOT_REG_REV_B00 0x00030110 -#define NV3_BOOT_REG_REV_C00 0x00030120 -// 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; - uint32_t config_0; - uint32_t config_1; -} 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; // PTIMER Interrupt status - uint32_t interrupt_enable; // PTIMER Interrupt enable - uint32_t clock_numerator; // PTIMER (tick?) numerator - uint32_t clock_denominator; // PTIMER (tick?) denominator - uint64_t time; // time - uint32_t alarm; // The value of time when there should be an alarm -} 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(); -void nv3_pgraph_vblank_start(svga_t* svga); - -// 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(); -void nv3_pramdac_pixel_clock_poll(void* priv); -void nv3_pramdac_memory_clock_poll(void* priv); - -// NV3 PTIMER -void nv3_ptimer_init(); -void nv3_ptimer_tick(); - -// 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 index 7e8f6f7c1..db9474602 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -19,7 +19,6 @@ // Last updated 2 December 2024 // The GPU base structure -extern nv3_t* nv3; extern const device_config_t nv3_config[]; #define NV3_MMIO_SIZE 0x1000000 // Max MMIO size @@ -31,6 +30,11 @@ extern const device_config_t nv3_config[]; #define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1000 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. +// Default value for the boot information register. +// Depends on the chip +#define NV3_BOOT_REG_REV_A00 0x00030100 +#define NV3_BOOT_REG_REV_B00 0x00030110 +#define NV3_BOOT_REG_REV_C00 0x00030120 // various vbioses for testing // Coming soon: MIROmagic Premium BIOS (when I get mine dumped) @@ -318,14 +322,15 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename #define NV3_PGRAPH_CONTEXT_CACHE(i) 0x4001A0+(i*4) // Context Cache #define NV3_PGRAPH_CONTEXT_CACHE_SIZE 8 +// TODO: CLIP0/CLIP1 (8 clips min/max in 32bits) #define NV3_PGRAPH_ABS_UCLIP_XMIN 0x40053C // Clip X minimum #define NV3_PGRAPH_ABS_UCLIP_XMAX 0x400540 // Clip X maximum #define NV3_PGRAPH_ABS_UCLIP_YMIN 0x400544 // Clip Y minimum #define NV3_PGRAPH_ABS_UCLIP_YMAX 0x400548 // Clip Y maximum -#define NV3_PGRAPH_SRC_CANVAS_MIN 0x400550 // Minimum Source Canvas for Blit, X,Y position -#define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, X,Y position -#define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, X,Y position -#define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, X,Y position +#define NV3_PGRAPH_SRC_CANVAS_MIN 0x400550 // Minimum Source Canvas for Blit, Y=30:16, X=10:0 +#define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, Y=30:16, X=10:0 +#define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, Y=30:16, X=10:0 +#define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, Y=30:16, X=10:0 #define NV3_PGRAPH_PATTERN_COLOR_0_0 0x400600 #define NV3_PGRAPH_PATTERN_COLOR_0_1 0x400604 #define NV3_PGRAPH_PATTERN_COLOR_1_0 0x400608 @@ -575,5 +580,387 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F +/* + STRUCTURES FOR THE GPU START HERE +*/ + //todo: pixel format +// 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; + uint32_t config_0; + uint32_t config_1; +} 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 + + uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache +} 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; // PTIMER Interrupt status + uint32_t interrupt_enable; // PTIMER Interrupt enable + uint32_t clock_numerator; // PTIMER (tick?) numerator + uint32_t clock_denominator; // PTIMER (tick?) denominator + uint64_t time; // time + uint32_t alarm; // The value of time when there should be an alarm +} 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; + +// 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(); +void nv3_pgraph_vblank_start(svga_t* svga); + +// 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 (Final presentation) +void nv3_pramdac_init(); +void nv3_pramdac_set_vram_clock(); +void nv3_pramdac_set_pixel_clock(); +void nv3_pramdac_pixel_clock_poll(void* priv); +void nv3_pramdac_memory_clock_poll(void* priv); + +// NV3 PTIMER +void nv3_ptimer_init(); +void nv3_ptimer_tick(); + +// NV3 PVIDEO +void nv3_pvideo_init(); + +// NV3 PMEDIA (Mediaport) +void nv3_pmedia_init(); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 48c0c93db..e74e6a93d 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -111,7 +111,6 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) return 0x00; } - nv_log("NV3: MMIO read, 0x%08x <- 0x%08x\n", ret, address); return ret; } @@ -171,8 +170,6 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) 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); } diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index b1696f950..9fa06d019 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -47,6 +47,39 @@ nv_register_t pgraph_registers[] = { { 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 }, + { NV3_PGRAPH_CONTEXT_SWITCH, "PGRAPH DMA Context Switch", NULL, NULL }, + { NV3_PGRAPH_CONTEXT_CONTROL, "PGRAPH DMA Context Control", NULL, NULL }, + { NV3_PGRAPH_CONTEXT_USER, "PGRAPH DMA Context User", NULL, NULL }, + //{ NV3_PGRAPH_CONTEXT_CACHE(0), "PGRAPH DMA Context Cache", NULL, NULL }, + { NV3_PGRAPH_ABS_UCLIP_XMIN, "PGRAPH Absolute Clip Minimum X [17:0]", NULL, NULL }, + { NV3_PGRAPH_ABS_UCLIP_XMAX, "PGRAPH Absolute Clip Maximum X [17:0]", NULL, NULL }, + { NV3_PGRAPH_ABS_UCLIP_YMIN, "PGRAPH Absolute Clip Minimum Y [17:0]", NULL, NULL }, + { NV3_PGRAPH_ABS_UCLIP_YMAX, "PGRAPH Absolute Clip Maximum Y [17:0]", NULL, NULL }, + { NV3_PGRAPH_SRC_CANVAS_MIN, "PGRAPH Source Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_SRC_CANVAS_MAX, "PGRAPH Source Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_DST_CANVAS_MIN, "PGRAPH Destination Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_DST_CANVAS_MAX, "PGRAPH Destination Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_PATTERN_COLOR_0_0, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_0_1, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_1_0, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_1_1, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_BITMAP_HIGH, "PGRAPH Pattern Bitmap (High 32bits)", NULL, NULL}, + { NV3_PGRAPH_PATTERN_BITMAP_LOW, "PGRAPH Pattern Bitmap (Low 32bits)", NULL, NULL}, + { NV3_PGRAPH_PATTERN_SHAPE, "PGRAPH Pattern Shape (1:0 - 0=8x8, 1=64x1, 2=1x64)", NULL, NULL}, + { NV3_PGRAPH_ROP3, "PGRAPH Render Operation ROP3 (2^3 bits = 256 possible operations)", NULL, NULL}, + { NV3_PGRAPH_PLANE_MASK, "PGRAPH Current Plane Mask (7:0)", NULL, NULL}, + { NV3_PGRAPH_CHROMA_KEY, "PGRAPH Chroma Key (17:0) (Bit 30 = Alpha, 29:20 = Red, 19:10 = Green, 9:0 = Blue)", NULL, NULL}, + { NV3_PGRAPH_NOTIFY, "PGRAPH Notifier (Wip...)", NULL, NULL}, + { NV3_PGRAPH_CLIP0_MIN, "PGRAPH Clip0 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_CLIP0_MAX, "PGRAPH Clip0 Max (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_CLIP1_MIN, "PGRAPH Clip1 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_CLIP1_MAX, "PGRAPH Clip1 Max (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, + { NV3_PGRAPH_FIFO_ACCESS, "PGRAPH - Can we access PFIFO?", NULL, NULL, }, + { NV3_PGRAPH_STATUS, "PGRAPH Status", NULL, NULL }, + { NV3_PGRAPH_TRAPPED_ADDRESS, "PGRAPH Trapped Address", NULL, NULL }, + { NV3_PGRAPH_TRAPPED_DATA, "PGRAPH Trapped Data", NULL, NULL }, + { NV3_PGRAPH_TRAPPED_INSTANCE, "PGRAPH Trapped Object Instance", NULL, NULL }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -95,6 +128,18 @@ uint32_t nv3_pgraph_read(uint32_t address) } } + else + { + /* Special exception for memory areas */ + if (address >= NV3_PGRAPH_CONTEXT_CACHE(0) + && address <= NV3_PGRAPH_CONTEXT_CACHE(NV3_PGRAPH_CONTEXT_CACHE_SIZE)) + { + // Addresses should be aligned to 4 bytes. + uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); + + nv_log("NV3: PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); + } + } return 0x0; } @@ -152,6 +197,19 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) } } } + else + { + /* Special exception for memory areas */ + if (address >= NV3_PGRAPH_CONTEXT_CACHE(0) + && address <= NV3_PGRAPH_CONTEXT_CACHE(NV3_PGRAPH_CONTEXT_CACHE_SIZE)) + { + // Addresses should be aligned to 4 bytes. + uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); + + nv_log("NV3: PGRAPH Context Cache Write (Entry=%04x Value=%04x)\n", entry, value); + nv3->pgraph.context_cache[entry] = value; + } + } } // Fire a VALID Pgraph interrupt: num is the bit# of the interrupt in the GPU subsystem INTR_EN register. From 378af452d654fdca740a64524a717f6eaf787e4d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 17:18:14 +0000 Subject: [PATCH 022/274] Refactor the read functions --- src/video/nv/nv3/subsystems/nv3_pbus.c | 25 ++++++---- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 1 - src/video/nv/nv3/subsystems/nv3_pextdev.c | 23 +++++---- src/video/nv/nv3/subsystems/nv3_pfb.c | 27 +++++++---- src/video/nv/nv3/subsystems/nv3_pfifo.c | 26 ++++++---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 27 ++++++----- src/video/nv/nv3/subsystems/nv3_pmc.c | 30 +++++++----- src/video/nv/nv3/subsystems/nv3_pme.c | 19 ++++++-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 47 ++++++++++++------- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 1 - .../nv/nv3/subsystems/nv3_pramin_ramht.c | 1 - .../nv/nv3/subsystems/nv3_pramin_ramro.c | 1 - src/video/nv/nv3/subsystems/nv3_ptimer.c | 29 ++++++++---- src/video/nv/nv3/subsystems/nv3_pvideo.c | 20 ++++++-- 14 files changed, 175 insertions(+), 102 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 5d50bf2fb..b61c36e52 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -49,6 +49,8 @@ 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])); + uint32_t ret = 0x00; + // todo: friendly logging nv_log("NV3: PBUS Read from 0x%08x", address); @@ -56,30 +58,33 @@ uint32_t nv3_pbus_read(uint32_t 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(); + ret = reg->on_read(); else { switch (reg->address) { case NV3_PBUS_INTR: - return nv3->pbus.interrupt_status; + ret = nv3->pbus.interrupt_status; break; case NV3_PBUS_INTR_EN: - return nv3->pbus.interrupt_enable; + ret = nv3->pbus.interrupt_enable; break; } - } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pbus_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 8043fd758..1ce59171a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -28,4 +28,3 @@ #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 index 485763696..452550ad9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -84,7 +84,7 @@ 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 + uint32_t ret = 0x00; // special consideration for straps if (address == NV3_PSTRAPS) @@ -94,31 +94,34 @@ uint32_t nv3_pextdev_read(uint32_t address) 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(); + ret = reg->on_read(); else { switch (reg->address) { case NV3_PSTRAPS: - return nv3->pextdev.straps; + ret = nv3->pextdev.straps; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pextdev_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 9711f3c9a..02f3618ff 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -63,6 +63,8 @@ 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])); + uint32_t ret = 0x00; + // todo: friendly logging nv_log("NV3: PFB Read from 0x%08x", address); @@ -70,27 +72,32 @@ uint32_t nv3_pfb_read(uint32_t 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(); + ret = reg->on_read(); else { switch (reg->address) { case NV3_PFB_BOOT: - return nv3->pfb.boot; + ret = nv3->pfb.boot; + // Config 0 has a read/write function case NV3_PFB_CONFIG_1: - return nv3->pfb.config_1; + ret = nv3->pfb.config_1; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pfb_write(uint32_t address, uint32_t value) @@ -114,6 +121,7 @@ void nv3_pfb_write(uint32_t address, uint32_t value) { switch (reg->address) { + // Config 0 has a read/write function case NV3_PFB_CONFIG_1: // Config Register 1 nv3->pfb.config_1 = value; } @@ -124,7 +132,6 @@ void nv3_pfb_write(uint32_t address, uint32_t value) uint32_t nv3_pfb_config0_read() { return nv3->pfb.config_0; - } void nv3_pfb_config0_write(uint32_t val) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 772839128..e5453a348 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -28,7 +28,7 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> -// Single unified write function... + // // ****** pfifo register list START ****** @@ -59,6 +59,8 @@ uint32_t nv3_pfifo_read(uint32_t address) return 0x00; } + uint32_t ret = 0x00; + nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); // todo: friendly logging @@ -68,14 +70,10 @@ uint32_t nv3_pfifo_read(uint32_t 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(); + ret = reg->on_read(); else { // Interrupt state: @@ -87,13 +85,23 @@ uint32_t nv3_pfifo_read(uint32_t address) switch (reg->address) { case NV3_PFIFO_INTR: - return nv3->pfifo.interrupt_status; + ret = nv3->pfifo.interrupt_status; case NV3_PFIFO_INTR_EN: - return nv3->pfifo.interrupt_enable; + ret = nv3->pfifo.interrupt_enable; } } + + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); } - return 0x0; + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + } + + return ret; } void nv3_pfifo_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 9fa06d019..68ba20509 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -94,6 +94,8 @@ uint32_t nv3_pgraph_read(uint32_t address) return 0x00; } + uint32_t ret = 0x00; + nv_register_t* reg = nv_get_register(address, pgraph_registers, sizeof(pgraph_registers)/sizeof(pgraph_registers[0])); // todo: friendly logging @@ -103,30 +105,29 @@ uint32_t nv3_pgraph_read(uint32_t 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(); + ret = reg->on_read(); else { switch (reg->address) { //interrupt status and enable regs case NV3_PGRAPH_INTR_0: - return nv3->pgraph.interrupt_status_0; + ret = nv3->pgraph.interrupt_status_0; case NV3_PGRAPH_INTR_1: - return nv3->pgraph.interrupt_status_1; + ret = nv3->pgraph.interrupt_status_1; case NV3_PGRAPH_INTR_EN_0: - return nv3->pgraph.interrupt_enable_0; + ret = nv3->pgraph.interrupt_enable_0; case NV3_PGRAPH_INTR_EN_1: - return nv3->pgraph.interrupt_enable_1; + ret = nv3->pgraph.interrupt_enable_1; } } + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); } else { @@ -139,9 +140,13 @@ uint32_t nv3_pgraph_read(uint32_t address) nv_log("NV3: PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); } + else /* Completely unknown */ + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + } } - return 0x0; + return ret; } void nv3_pgraph_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index f69b2b430..e8c3fe0e1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -28,7 +28,7 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> -// Single unified write function... + void nv3_pmc_init() { @@ -172,41 +172,47 @@ 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])); + uint32_t ret = 0x00; + // 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(); + ret = reg->on_read(); else { switch (reg->address) { case NV3_PMC_BOOT: - return nv3->pmc.boot; + ret = nv3->pmc.boot; case NV3_PMC_INTERRUPT_STATUS: nv3_pmc_clear_interrupts(); - return nv3_pmc_handle_interrupts(false); + ret = nv3_pmc_handle_interrupts(false); case NV3_PMC_INTERRUPT_ENABLE: //TODO: ACTUALLY CHANGE THE INTERRUPT STATE - return nv3->pmc.interrupt_enable; + ret = nv3->pmc.interrupt_enable; case NV3_PMC_ENABLE: - return nv3->pmc.enable; + ret = nv3->pmc.enable; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pmc_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index 8e0b7ddb5..b55ed4df0 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -45,6 +45,8 @@ 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])); + uint32_t ret = 0x00; + // todo: friendly logging nv_log("NV3: PME Read from 0x%08x", address); @@ -59,7 +61,7 @@ uint32_t nv3_pme_read(uint32_t address) // on-read function if (reg->on_read) - return reg->on_read(); + ret = reg->on_read(); else { // Interrupt state: @@ -71,14 +73,23 @@ uint32_t nv3_pme_read(uint32_t address) switch (reg->address) { case NV3_PME_INTR: - return nv3->pme.interrupt_status; + ret = nv3->pme.interrupt_status; case NV3_PME_INTR_EN: - return nv3->pme.interrupt_enable; + ret = nv3->pme.interrupt_enable; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pme_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index ed89f9448..46ccde970 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -210,6 +210,8 @@ 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])); + uint32_t ret = 0x00; + // todo: friendly logging nv_log("NV3: PRAMDAC Read from 0x%08x", address); @@ -224,49 +226,58 @@ uint32_t nv3_pramdac_read(uint32_t address) // on-read function if (reg->on_read) - return reg->on_read(); + ret = reg->on_read(); else { //s hould be pretty easy to understand switch (reg->address) { case NV3_PRAMDAC_COEFF_SELECT: - return nv3->pramdac.coeff_select; + ret = nv3->pramdac.coeff_select; case NV3_PRAMDAC_GENERAL_CONTROL: - return nv3->pramdac.general_control; + ret = nv3->pramdac.general_control; case NV3_PRAMDAC_VSERR_WIDTH: - return nv3->pramdac.vserr_width; + ret = nv3->pramdac.vserr_width; case NV3_PRAMDAC_VBBLANK_END: - return nv3->pramdac.vbblank_end; + ret = nv3->pramdac.vbblank_end; case NV3_PRAMDAC_VBLANK_END: - return nv3->pramdac.vblank_end; + ret = nv3->pramdac.vblank_end; case NV3_PRAMDAC_VBLANK_START: - return nv3->pramdac.vblank_start; + ret = nv3->pramdac.vblank_start; case NV3_PRAMDAC_VEQU_START: - return nv3->pramdac.vequ_start; + ret = nv3->pramdac.vequ_start; case NV3_PRAMDAC_VTOTAL: - return nv3->pramdac.vtotal; + ret = nv3->pramdac.vtotal; case NV3_PRAMDAC_HSYNC_WIDTH: - return nv3->pramdac.hsync_width; + ret = nv3->pramdac.hsync_width; case NV3_PRAMDAC_HBURST_START: - return nv3->pramdac.hburst_start; + ret = nv3->pramdac.hburst_start; case NV3_PRAMDAC_HBURST_END: - return nv3->pramdac.hburst_end; + ret = nv3->pramdac.hburst_end; case NV3_PRAMDAC_HBLANK_START: - return nv3->pramdac.hblank_start; + ret = nv3->pramdac.hblank_start; case NV3_PRAMDAC_HBLANK_END: - return nv3->pramdac.hblank_end; + ret = nv3->pramdac.hblank_end; case NV3_PRAMDAC_HTOTAL: - return nv3->pramdac.htotal; + ret = nv3->pramdac.htotal; case NV3_PRAMDAC_HEQU_WIDTH: - return nv3->pramdac.hequ_width; + ret = nv3->pramdac.hequ_width; case NV3_PRAMDAC_HSERR_WIDTH: - return nv3->pramdac.hserr_width; + ret = nv3->pramdac.hserr_width; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pramdac_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 0d141a222..5c6d3a54c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -28,4 +28,3 @@ #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 index 5cb9e9d86..7f80a1035 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -28,4 +28,3 @@ #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 index dd96adb40..865bc46ec 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -28,4 +28,3 @@ #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 index 1eb53409a..2a722e4e1 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -87,6 +87,8 @@ uint32_t nv3_ptimer_read(uint32_t address) nv_log("NV3: PTIMER Read from 0x%08x", address); + uint32_t ret = 0x00; + // if the register actually exists if (reg) { @@ -97,7 +99,7 @@ uint32_t nv3_ptimer_read(uint32_t address) // on-read function if (reg->on_read) - return reg->on_read(); + ret = reg->on_read(); else { // Interrupt state: @@ -106,27 +108,36 @@ uint32_t nv3_ptimer_read(uint32_t address) switch (reg->address) { case NV3_PTIMER_INTR: - return nv3->ptimer.interrupt_status; + ret = nv3->ptimer.interrupt_status; case NV3_PTIMER_INTR_EN: - return nv3->ptimer.interrupt_enable; + ret = nv3->ptimer.interrupt_enable; case NV3_PTIMER_NUMERATOR: - return nv3->ptimer.clock_numerator; // 15:0 + ret = nv3->ptimer.clock_numerator; // 15:0 case NV3_PTIMER_DENOMINATOR: - return nv3->ptimer.clock_denominator ; //15:0 + ret = nv3->ptimer.clock_denominator ; //15:0 // 64-bit value // High part case NV3_PTIMER_TIME_0_NSEC: - return nv3->ptimer.time & 0xFFFFFFFF; //28:0 + ret = nv3->ptimer.time & 0xFFFFFFFF; //28:0 // Low part case NV3_PTIMER_TIME_1_NSEC: - return nv3->ptimer.time >> 32; // 31:5 + ret = nv3->ptimer.time >> 32; // 31:5 case NV3_PTIMER_ALARM_NSEC: - return nv3->ptimer.alarm; // 31:5 + ret = nv3->ptimer.alarm; // 31:5 } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x00; + return ret; } void nv3_ptimer_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index ea929ad2c..6e0a725a4 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -48,7 +48,8 @@ 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])); - + uint32_t ret = 0x00; + // todo: friendly logging nv_log("NV3: PVIDEO Read from 0x%08x", address); @@ -63,7 +64,7 @@ uint32_t nv3_pvideo_read(uint32_t address) // on-read function if (reg->on_read) - return reg->on_read(); + ret = reg->on_read(); else { // Interrupt state: @@ -72,14 +73,23 @@ uint32_t nv3_pvideo_read(uint32_t address) switch (reg->address) { case NV3_PVIDEO_INTR: - return nv3->pvideo.interrupt_status; + ret = nv3->pvideo.interrupt_status; case NV3_PVIDEO_INTR_EN: - return nv3->pvideo.interrupt_enable; + ret = nv3->pvideo.interrupt_enable; } } + + if (reg->friendly_name) + nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } + else + { + nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); } - return 0x0; + return ret; } void nv3_pvideo_write(uint32_t address, uint32_t value) From 88cc5f07f7b8d1937f69002fb99c9bfa6db6212f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 17:24:17 +0000 Subject: [PATCH 023/274] sort out the logging but there are still regressions (driver fail to boot) --- src/video/nv/nv3/nv3_core.c | 2 ++ src/video/nv/nv3/subsystems/nv3_pbus.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pextdev.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pfb.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pmc.c | 7 ++++--- src/video/nv/nv3/subsystems/nv3_pme.c | 9 ++------- src/video/nv/nv3/subsystems/nv3_pramdac.c | 11 +++-------- src/video/nv/nv3/subsystems/nv3_ptimer.c | 11 +++-------- src/video/nv/nv3/subsystems/nv3_pvideo.c | 4 ++-- 11 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index f0ff0f028..dde1043c4 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -842,8 +842,10 @@ void* nv3_init(const device_t *info) // These only get turned on when the requisite registers are twiddled timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pramdac_pixel_clock_poll, nv3, false); nv_log("NV3: Pixel clock OK. Will be started when the VBIOS tells us to start\n"); + timer_add(&nv3->nvbase.memory_clock_timer, nv3_pramdac_memory_clock_poll, nv3, false); nv_log("NV3: Memory clock OK. Will be started when the VBIOS tells us to start\n"); + return nv3; } diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index b61c36e52..84df73169 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -75,13 +75,13 @@ uint32_t nv3_pbus_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 452550ad9..d4c1ce353 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -112,13 +112,13 @@ uint32_t nv3_pextdev_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 02f3618ff..3172f30d3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -88,13 +88,13 @@ uint32_t nv3_pfb_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index e5453a348..6b6ab2aaf 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -98,7 +98,7 @@ uint32_t nv3_pfifo_read(uint32_t address) } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 68ba20509..070eabd62 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -125,7 +125,7 @@ uint32_t nv3_pgraph_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } @@ -142,7 +142,7 @@ uint32_t nv3_pgraph_read(uint32_t address) } else /* Completely unknown */ { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index e8c3fe0e1..d94798152 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -190,9 +190,10 @@ uint32_t nv3_pmc_read(uint32_t address) case NV3_PMC_BOOT: ret = nv3->pmc.boot; case NV3_PMC_INTERRUPT_STATUS: + nv_log("\n"); // clear_interrupts logs nv3_pmc_clear_interrupts(); - ret = nv3_pmc_handle_interrupts(false); + ret = nv3_pmc_handle_interrupts(false); case NV3_PMC_INTERRUPT_ENABLE: //TODO: ACTUALLY CHANGE THE INTERRUPT STATE ret = nv3->pmc.interrupt_enable; @@ -203,13 +204,13 @@ uint32_t nv3_pmc_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index b55ed4df0..11b84a040 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -54,11 +54,6 @@ uint32_t nv3_pme_read(uint32_t 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) ret = reg->on_read(); @@ -80,13 +75,13 @@ uint32_t nv3_pme_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 46ccde970..8d162fe2b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -211,7 +211,7 @@ 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])); uint32_t ret = 0x00; - + // todo: friendly logging nv_log("NV3: PRAMDAC Read from 0x%08x", address); @@ -219,11 +219,6 @@ uint32_t nv3_pramdac_read(uint32_t 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) ret = reg->on_read(); @@ -268,13 +263,13 @@ uint32_t nv3_pramdac_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 2a722e4e1..1d714222a 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -88,15 +88,10 @@ uint32_t nv3_ptimer_read(uint32_t address) nv_log("NV3: PTIMER Read from 0x%08x", address); uint32_t ret = 0x00; - + // 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) ret = reg->on_read(); @@ -128,13 +123,13 @@ uint32_t nv3_ptimer_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index 6e0a725a4..f5e1ee4e8 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -80,13 +80,13 @@ uint32_t nv3_pvideo_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = %04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); } return ret; From 3914a00f9b7d5a92f0649ba13c1b94e29a62b87c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 17:58:02 +0000 Subject: [PATCH 024/274] Local idiot forgets break; wonders why code does not work? --- src/video/nv/nv3/subsystems/nv3_pbus.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pextdev.c | 5 +++-- src/video/nv/nv3/subsystems/nv3_pfb.c | 6 ++++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 4 +++- src/video/nv/nv3/subsystems/nv3_pgraph.c | 8 ++++++-- src/video/nv/nv3/subsystems/nv3_pmc.c | 12 ++++++++---- src/video/nv/nv3/subsystems/nv3_pme.c | 6 ++++-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 20 ++++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_ptimer.c | 12 ++++++++++-- src/video/nv/nv3/subsystems/nv3_pvideo.c | 6 ++++-- 10 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 84df73169..cf5b698a1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -75,13 +75,13 @@ uint32_t nv3_pbus_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index d4c1ce353..2fbbd3489 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -108,17 +108,18 @@ uint32_t nv3_pextdev_read(uint32_t address) { case NV3_PSTRAPS: ret = nv3->pextdev.straps; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 3172f30d3..4de4d5069 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -81,20 +81,22 @@ uint32_t nv3_pfb_read(uint32_t address) { case NV3_PFB_BOOT: ret = nv3->pfb.boot; + break; // Config 0 has a read/write function case NV3_PFB_CONFIG_1: ret = nv3->pfb.config_1; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 6b6ab2aaf..c5868ef10 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -86,8 +86,10 @@ uint32_t nv3_pfifo_read(uint32_t address) { case NV3_PFIFO_INTR: ret = nv3->pfifo.interrupt_status; + break; case NV3_PFIFO_INTR_EN: ret = nv3->pfifo.interrupt_enable; + break; } } @@ -98,7 +100,7 @@ uint32_t nv3_pfifo_read(uint32_t address) } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 070eabd62..d7b7bfb01 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -115,17 +115,21 @@ uint32_t nv3_pgraph_read(uint32_t address) //interrupt status and enable regs case NV3_PGRAPH_INTR_0: ret = nv3->pgraph.interrupt_status_0; + break; case NV3_PGRAPH_INTR_1: ret = nv3->pgraph.interrupt_status_1; + break; case NV3_PGRAPH_INTR_EN_0: ret = nv3->pgraph.interrupt_enable_0; + break; case NV3_PGRAPH_INTR_EN_1: ret = nv3->pgraph.interrupt_enable_1; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } @@ -142,7 +146,7 @@ uint32_t nv3_pgraph_read(uint32_t address) } else /* Completely unknown */ { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index d94798152..6b2b51b09 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -189,28 +189,32 @@ uint32_t nv3_pmc_read(uint32_t address) { case NV3_PMC_BOOT: ret = nv3->pmc.boot; + break; case NV3_PMC_INTERRUPT_STATUS: nv_log("\n"); // clear_interrupts logs nv3_pmc_clear_interrupts(); ret = nv3_pmc_handle_interrupts(false); + break; case NV3_PMC_INTERRUPT_ENABLE: //TODO: ACTUALLY CHANGE THE INTERRUPT STATE - ret = nv3->pmc.interrupt_enable; + ret = nv3->pmc.interrupt_enable; + break; case NV3_PMC_ENABLE: - ret = nv3->pmc.enable; + ret = nv3->pmc.enable; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index 11b84a040..2ba4af426 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -69,19 +69,21 @@ uint32_t nv3_pme_read(uint32_t address) { case NV3_PME_INTR: ret = nv3->pme.interrupt_status; + break; case NV3_PME_INTR_EN: ret = nv3->pme.interrupt_enable; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 8d162fe2b..d30900d43 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -229,47 +229,63 @@ uint32_t nv3_pramdac_read(uint32_t address) { case NV3_PRAMDAC_COEFF_SELECT: ret = nv3->pramdac.coeff_select; + break; case NV3_PRAMDAC_GENERAL_CONTROL: ret = nv3->pramdac.general_control; + break; case NV3_PRAMDAC_VSERR_WIDTH: ret = nv3->pramdac.vserr_width; + break; case NV3_PRAMDAC_VBBLANK_END: ret = nv3->pramdac.vbblank_end; + break; case NV3_PRAMDAC_VBLANK_END: ret = nv3->pramdac.vblank_end; + break; case NV3_PRAMDAC_VBLANK_START: ret = nv3->pramdac.vblank_start; + break; case NV3_PRAMDAC_VEQU_START: ret = nv3->pramdac.vequ_start; + break; case NV3_PRAMDAC_VTOTAL: ret = nv3->pramdac.vtotal; + break; case NV3_PRAMDAC_HSYNC_WIDTH: ret = nv3->pramdac.hsync_width; + break; case NV3_PRAMDAC_HBURST_START: ret = nv3->pramdac.hburst_start; + break; case NV3_PRAMDAC_HBURST_END: ret = nv3->pramdac.hburst_end; + break; case NV3_PRAMDAC_HBLANK_START: ret = nv3->pramdac.hblank_start; + break; case NV3_PRAMDAC_HBLANK_END: ret = nv3->pramdac.hblank_end; + break; case NV3_PRAMDAC_HTOTAL: ret = nv3->pramdac.htotal; + break; case NV3_PRAMDAC_HEQU_WIDTH: ret = nv3->pramdac.hequ_width; + break; case NV3_PRAMDAC_HSERR_WIDTH: ret = nv3->pramdac.hserr_width; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 1d714222a..7e718264a 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -104,32 +104,40 @@ uint32_t nv3_ptimer_read(uint32_t address) { case NV3_PTIMER_INTR: ret = nv3->ptimer.interrupt_status; + break; case NV3_PTIMER_INTR_EN: ret = nv3->ptimer.interrupt_enable; + break; case NV3_PTIMER_NUMERATOR: ret = nv3->ptimer.clock_numerator; // 15:0 + break; case NV3_PTIMER_DENOMINATOR: ret = nv3->ptimer.clock_denominator ; //15:0 + break; // 64-bit value // High part case NV3_PTIMER_TIME_0_NSEC: ret = nv3->ptimer.time & 0xFFFFFFFF; //28:0 + break; // Low part case NV3_PTIMER_TIME_1_NSEC: ret = nv3->ptimer.time >> 32; // 31:5 + break; case NV3_PTIMER_ALARM_NSEC: ret = nv3->ptimer.alarm; // 31:5 + break; } + } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index f5e1ee4e8..dc8651fb8 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -74,19 +74,21 @@ uint32_t nv3_pvideo_read(uint32_t address) { case NV3_PVIDEO_INTR: ret = nv3->pvideo.interrupt_status; + break; case NV3_PVIDEO_INTR_EN: ret = nv3->pvideo.interrupt_enable; + break; } } if (reg->friendly_name) - nv_log(": %s (value = 0x%04x)\n", reg->friendly_name, ret); + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); else nv_log("\n"); } else { - nv_log(": Unknown register read (address=0x%04x), returning 0x00\n", address); + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); } return ret; From ef8e4e95c1b0b055e5c9969840de0c91b4b22027 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 18:43:15 +0000 Subject: [PATCH 025/274] actually fire the rivatimer --- src/include/86box/nv/vid_nv.h | 7 +++-- src/include/86box/nv/vid_nv3.h | 6 ++-- src/include/86box/nv/vid_nv_rivatimer.h | 3 +- src/video/nv/nv3/nv3_core.c | 8 +++++- src/video/nv/nv3/subsystems/nv3_pramdac.c | 34 ++++++++++++++++++----- src/video/nv/nv_rivatimer.c | 29 +++++++++++++++++-- 6 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 9a4a7c55b..0666d8f35 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -28,6 +28,7 @@ #include <86box/timer.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> +#include <86box/nv/vid_nv_rivatimer.h> void nv_log(const char *fmt, ...); @@ -99,10 +100,12 @@ typedef struct nv_base_s nv_bus_generation bus_generation; // current bus (see nv_bus_generation documentation) uint32_t gpu_revision; // GPU Stepping double pixel_clock_period; // Period in seconds for pixel clock - pc_timer_t pixel_clock_timer; // Pixel Clock Timer + //pc_timer_t pixel_clock_timer; // Pixel Clock Timer + rivatimer_t* pixel_clock_timer; bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_period; // Period in seconds for pixel clock - pc_timer_t memory_clock_timer; // Memory Clock Timer + //pc_timer_t memory_clock_timer; // Memory Clock Timer + rivatimer_t* memory_clock_timer; bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times } nv_base_t; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index db9474602..4a93ebddb 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -28,7 +28,7 @@ extern const device_config_t nv3_config[]; #define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start #define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN -#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1000 // The amount by which we have to ration out the memory clock because it's not fast enough... +#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 10 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. // Default value for the boot information register. // Depends on the chip @@ -952,8 +952,8 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val); void nv3_pramdac_init(); void nv3_pramdac_set_vram_clock(); void nv3_pramdac_set_pixel_clock(); -void nv3_pramdac_pixel_clock_poll(void* priv); -void nv3_pramdac_memory_clock_poll(void* priv); +void nv3_pramdac_pixel_clock_poll(/*void* priv*/); +void nv3_pramdac_memory_clock_poll(/*void* priv*/); // NV3 PTIMER void nv3_ptimer_init(); diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index 00e94cd97..31483992e 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -80,4 +80,5 @@ void rivatimer_update_all(); void rivatimer_start(rivatimer_t* rivatimer_ptr); void rivatimer_stop(rivatimer_t* rivatimer_ptr); double rivatimer_get_time(rivatimer_t* rivatimer_ptr); -void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()); \ No newline at end of file +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()); +void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index dde1043c4..c7a4407f2 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -350,6 +350,8 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) void nv3_close(void* priv) { + rivatimer_destroy(nv3->nvbase.pixel_clock_timer); + rivatimer_destroy(nv3->nvbase.memory_clock_timer); svga_close(&nv3->nvbase.svga); free(nv3); nv3 = NULL; @@ -839,12 +841,16 @@ void* nv3_init(const device_t *info) nv_log("NV3: Initialising timers...\n"); + /* // These only get turned on when the requisite registers are twiddled timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pramdac_pixel_clock_poll, nv3, false); nv_log("NV3: Pixel clock OK. Will be started when the VBIOS tells us to start\n"); - + timer_add(&nv3->nvbase.memory_clock_timer, nv3_pramdac_memory_clock_poll, nv3, false); nv_log("NV3: Memory clock OK. Will be started when the VBIOS tells us to start\n"); + */ + + return nv3; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index d30900d43..4dabc0a47 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -42,7 +42,6 @@ void nv3_pramdac_init() 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(); @@ -51,22 +50,24 @@ void nv3_pramdac_init() // Polls the pixel clock. // This updates the 2D/3D engine PGRAPH -void nv3_pramdac_pixel_clock_poll(void* priv) +void nv3_pramdac_pixel_clock_poll(/*void* priv*/) { + /* nv3_t* nv3_poll = (nv3_t*)priv; timer_on_auto(&nv3_poll->nvbase.pixel_clock_timer, nv3_poll->nvbase.pixel_clock_period); + */ } // Polls the memory clock. -void nv3_pramdac_memory_clock_poll(void* priv) +void nv3_pramdac_memory_clock_poll(/*void* priv*/) { - nv3_t* nv3_poll = (nv3_t*)priv; + //nv3_t* nv3_poll = (nv3_t*)priv; // Let's hope qeeg was right here. nv3_ptimer_tick(); - timer_on_auto(&nv3_poll->nvbase.memory_clock_timer, nv3_poll->nvbase.memory_clock_period); + //timer_on_auto(&nv3_poll->nvbase.memory_clock_timer, nv3_poll->nvbase.memory_clock_period); } // Gets the vram clock register. @@ -118,6 +119,7 @@ void nv3_pramdac_set_vram_clock() if (nv3->pramdac.memory_clock_n == 0) nv3->pramdac.memory_clock_n = 1; + // Convert to microseconds frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box @@ -126,8 +128,16 @@ void nv3_pramdac_set_vram_clock() nv3->nvbase.memory_clock_period = time; - timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); + //timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); + // Create and start if it it's not running. + if (!nv3->nvbase.memory_clock_timer) + { + nv3->nvbase.memory_clock_timer = rivatimer_create(time, nv3_pramdac_pixel_clock_poll); + rivatimer_start(nv3->nvbase.memory_clock_timer); + } + + rivatimer_set_period(nv3->nvbase.memory_clock_timer, time); //Breaks everything? //timer_set_delay_u64(&nv3->nvbase.memory_clock_timer, time * TIMER_USEC); // do we need to decrease } @@ -170,7 +180,17 @@ void nv3_pramdac_set_pixel_clock() nv3->nvbase.pixel_clock_period = time; - timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); + // Create and start if it it's not running. + if (!nv3->nvbase.pixel_clock_timer) + { + nv3->nvbase.pixel_clock_timer = rivatimer_create(time, nv3_pramdac_pixel_clock_poll); + rivatimer_start(nv3->nvbase.pixel_clock_timer); + } + + rivatimer_set_period(nv3->nvbase.pixel_clock_timer, time); + + + //timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); //Breaks everything? //timer_set_delay_u64(&nv3->nvbase.pixel_clock_timer, time * TIMER_USEC); // do we need to decrease } diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index 1ccde5a2f..c31ee11fe 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -42,8 +42,15 @@ void rivatimer_init() if (!rivatimer_ptr) return; - while (rivatimer_ptr->next) + while (rivatimer_ptr) + { + // since we are destroing it + rivatimer_t* old_next = rivatimer_ptr->next; rivatimer_destroy(rivatimer_ptr); + + rivatimer_ptr = old_next; + } + #ifdef _WIN32 // Query the performance frequency. @@ -98,10 +105,12 @@ bool rivatimer_really_exists(rivatimer_t* rivatimer) if (!current) return false; - while (current->next != NULL) + while (current) { if (current == rivatimer) return true; + + current = current->next; } return false; @@ -160,10 +169,14 @@ void rivatimer_update_all() if (!rivatimer_ptr) return; - while (rivatimer_ptr->next) + while (rivatimer_ptr) { + // if it's not running skip it if (!rivatimer_ptr->running) + { + rivatimer_ptr = rivatimer_ptr->next; continue; + } #ifdef _WIN32 LARGE_INTEGER current_time; @@ -199,6 +212,8 @@ void rivatimer_update_all() rivatimer_ptr->callback(); } + + rivatimer_ptr = rivatimer_ptr->next; } } @@ -248,4 +263,12 @@ void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()) fatal("rivatimer_set_callback: No callback!"); rivatimer_ptr->callback = callback; +} + +void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period) +{ + if (!rivatimer_really_exists(rivatimer_ptr)) + fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + + rivatimer_ptr->period = period; } \ No newline at end of file From fd99d6e71199e4489f12d6cfa0927c9249922afe Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 18:48:31 +0000 Subject: [PATCH 026/274] cleanup legacy code --- src/include/86box/nv/vid_nv.h | 2 -- src/include/86box/nv/vid_nv3.h | 4 ++-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 17 +++-------------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 0666d8f35..b7450338b 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -100,11 +100,9 @@ typedef struct nv_base_s nv_bus_generation bus_generation; // current bus (see nv_bus_generation documentation) uint32_t gpu_revision; // GPU Stepping double pixel_clock_period; // Period in seconds for pixel clock - //pc_timer_t pixel_clock_timer; // Pixel Clock Timer rivatimer_t* pixel_clock_timer; bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_period; // Period in seconds for pixel clock - //pc_timer_t memory_clock_timer; // Memory Clock Timer rivatimer_t* memory_clock_timer; bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times } nv_base_t; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 4a93ebddb..328d8335e 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -952,8 +952,8 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val); void nv3_pramdac_init(); void nv3_pramdac_set_vram_clock(); void nv3_pramdac_set_pixel_clock(); -void nv3_pramdac_pixel_clock_poll(/*void* priv*/); -void nv3_pramdac_memory_clock_poll(/*void* priv*/); +void nv3_pramdac_pixel_clock_poll(); +void nv3_pramdac_memory_clock_poll(); // NV3 PTIMER void nv3_ptimer_init(); diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 4dabc0a47..2d4b4fbcb 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -50,17 +50,13 @@ void nv3_pramdac_init() // Polls the pixel clock. // This updates the 2D/3D engine PGRAPH -void nv3_pramdac_pixel_clock_poll(/*void* priv*/) +void nv3_pramdac_pixel_clock_poll() { - /* - nv3_t* nv3_poll = (nv3_t*)priv; - - timer_on_auto(&nv3_poll->nvbase.pixel_clock_timer, nv3_poll->nvbase.pixel_clock_period); - */ + // TODO: UPDATE PGRAPH! } // Polls the memory clock. -void nv3_pramdac_memory_clock_poll(/*void* priv*/) +void nv3_pramdac_memory_clock_poll() { //nv3_t* nv3_poll = (nv3_t*)priv; @@ -138,8 +134,6 @@ void nv3_pramdac_set_vram_clock() } rivatimer_set_period(nv3->nvbase.memory_clock_timer, time); - //Breaks everything? - //timer_set_delay_u64(&nv3->nvbase.memory_clock_timer, time * TIMER_USEC); // do we need to decrease } void nv3_pramdac_set_pixel_clock() @@ -188,11 +182,6 @@ void nv3_pramdac_set_pixel_clock() } rivatimer_set_period(nv3->nvbase.pixel_clock_timer, time); - - - //timer_on_auto(&nv3->nvbase.pixel_clock_timer, nv3->nvbase.pixel_clock_period); - //Breaks everything? - //timer_set_delay_u64(&nv3->nvbase.pixel_clock_timer, time * TIMER_USEC); // do we need to decrease } // From caa33123c698110b7614275d4e91a8c95f5e9f5d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 19:29:27 +0000 Subject: [PATCH 027/274] add prmvio --- src/include/86box/nv/vid_nv3.h | 6 +- src/video/nv/nv3/nv3_core.c | 97 +++++++++++++++++++++++- src/video/nv/nv3/nv3_core_arbiter.c | 5 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 11 ++- 4 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 328d8335e..d185cd408 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -212,8 +212,8 @@ extern const device_config_t nv3_config[]; #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_PRMVIO_START NV3_VGA_START // VGA stuff written from main GPU +#define NV3_PRMVIO_END 0xC0400 #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 @@ -342,11 +342,13 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_PLANE_MASK 0x400628 #define NV3_PGRAPH_CHROMA_KEY 0x40062C #define NV3_PGRAPH_BETA 0x400640 // Beta factor (30:23 fractional, 22:0 before fraction) +#define NV3_PGRAPH_DMA 0x400680 #define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH #define NV3_PGRAPH_CLIP0_MIN 0x400690 // Clip for Blitting 0 Min #define NV3_PGRAPH_CLIP0_MAX 0x400694 // Clip for Blitting 0 Max #define NV3_PGRAPH_CLIP1_MIN 0x400698 // Clip for Blitting 1 Min #define NV3_PGRAPH_CLIP1_MAX 0x40069C // Clip for Blitting 1 Max +#define NV3_PGRAPH_CLIP_MISC 0x4006A0 // Regions/Render/Complex mode #define NV3_PGRAPH_FIFO_ACCESS 0x4006A4 // Is PGRAPH enabled? #define NV3_PGRAPH_FIFO_ACCESS_DISABLED 0x0 #define NV3_PGRAPH_FIFO_ACCESS_ENABLED 0x1 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index c7a4407f2..77abd7a00 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -46,27 +46,92 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); // Read 8-bit MMIO uint8_t nv3_mmio_read8(uint32_t addr, void* priv) { + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv3_svga_in(real_address, nv3); + + return ret; + } + // see if unaligned reads are a problem - uint32_t ret = nv3_mmio_read32(addr, priv); + 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); + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv3_svga_in(real_address, nv3) + | (nv3_svga_in(real_address + 1, nv3) << 8); + + return ret; + } + + 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) { + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv3_svga_in(real_address, nv3) + | (nv3_svga_in(real_address + 1, nv3) << 8) + | (nv3_svga_in(real_address + 2, nv3) << 16) + | (nv3_svga_in(real_address + 3, nv3) << 24); + + return ret; + } + + return nv3_mmio_arbitrate_read(addr); } // Write 8-bit MMIO void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) { + // This is weitek vga stuff + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv3_svga_out(real_address, val & 0xFF, nv3); + + return; + } + // overwrite first 8bits of a 32 bit value uint32_t new_val = nv3_mmio_read32(addr, NULL); @@ -79,6 +144,19 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // Write 16-bit MMIO void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) { + // This is weitek vga stuff + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv3_svga_out(real_address, val & 0xFF, nv3); + nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); + + return; + } + // overwrite first 16bits of a 32 bit value uint32_t new_val = nv3_mmio_read32(addr, NULL); @@ -91,6 +169,21 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // Write 32-bit MMIO void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) { + // This is weitek vga stuff + if (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv3_svga_out(real_address, val & 0xFF, nv3); + nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); + nv3_svga_out(real_address + 2, (val >> 16) & 0xFF, nv3); + nv3_svga_out(real_address + 3, (val >> 24) & 0xFF, nv3); + + return; + } + nv3_mmio_arbitrate_write(addr, val); } diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index e74e6a93d..ee8fe048d 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -57,7 +57,7 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) return 0x00; 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; @@ -120,6 +120,9 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) if (!nv3) return; + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + address &= 0xFFFFFF; + // 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; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index d7b7bfb01..9dfca4f00 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -43,6 +43,10 @@ void nv3_pgraph_init() // nv_register_t pgraph_registers[] = { + { NV3_PGRAPH_DEBUG_0, "PGRAPH Debug 0", NULL, NULL }, + { NV3_PGRAPH_DEBUG_1, "PGRAPH Debug 1", NULL, NULL }, + { NV3_PGRAPH_DEBUG_2, "PGRAPH Debug 2", NULL, NULL }, + { NV3_PGRAPH_DEBUG_3, "PGRAPH Debug 3", NULL, NULL }, { 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 }, @@ -69,7 +73,10 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_ROP3, "PGRAPH Render Operation ROP3 (2^3 bits = 256 possible operations)", NULL, NULL}, { NV3_PGRAPH_PLANE_MASK, "PGRAPH Current Plane Mask (7:0)", NULL, NULL}, { NV3_PGRAPH_CHROMA_KEY, "PGRAPH Chroma Key (17:0) (Bit 30 = Alpha, 29:20 = Red, 19:10 = Green, 9:0 = Blue)", NULL, NULL}, - { NV3_PGRAPH_NOTIFY, "PGRAPH Notifier (Wip...)", NULL, NULL}, + { NV3_PGRAPH_BETA, "PGRAPH Beta factor", NULL, NULL }, + { NV3_PGRAPH_DMA, "PGRAPH DMA", NULL, NULL }, + { NV3_PGRAPH_CLIP_MISC, "PGRAPH Clipping Miscellaneous Settings", NULL, NULL }, + { NV3_PGRAPH_NOTIFY, "PGRAPH Notifier (Wip...)", NULL, NULL }, { NV3_PGRAPH_CLIP0_MIN, "PGRAPH Clip0 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_CLIP0_MAX, "PGRAPH Clip0 Max (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_CLIP1_MIN, "PGRAPH Clip1 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, @@ -164,7 +171,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) 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); + nv_log("NV3: PGRAPH Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) From 56a5522aa61325d2f6b15280302deae34eea5629 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 29 Dec 2024 19:37:19 +0000 Subject: [PATCH 028/274] fix ptimer --- src/video/nv/nv3/nv3_core.c | 11 ----------- src/video/nv/nv3/subsystems/nv3_pramdac.c | 4 +--- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 77abd7a00..0636a3c91 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -934,17 +934,6 @@ void* nv3_init(const device_t *info) nv_log("NV3: Initialising timers...\n"); - /* - // These only get turned on when the requisite registers are twiddled - timer_add(&nv3->nvbase.pixel_clock_timer, nv3_pramdac_pixel_clock_poll, nv3, false); - nv_log("NV3: Pixel clock OK. Will be started when the VBIOS tells us to start\n"); - - timer_add(&nv3->nvbase.memory_clock_timer, nv3_pramdac_memory_clock_poll, nv3, false); - nv_log("NV3: Memory clock OK. Will be started when the VBIOS tells us to start\n"); - */ - - - return nv3; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 2d4b4fbcb..0c57b71cb 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -62,8 +62,6 @@ void nv3_pramdac_memory_clock_poll() // Let's hope qeeg was right here. nv3_ptimer_tick(); - - //timer_on_auto(&nv3_poll->nvbase.memory_clock_timer, nv3_poll->nvbase.memory_clock_period); } // Gets the vram clock register. @@ -129,7 +127,7 @@ void nv3_pramdac_set_vram_clock() // Create and start if it it's not running. if (!nv3->nvbase.memory_clock_timer) { - nv3->nvbase.memory_clock_timer = rivatimer_create(time, nv3_pramdac_pixel_clock_poll); + nv3->nvbase.memory_clock_timer = rivatimer_create(time, nv3_pramdac_memory_clock_poll); rivatimer_start(nv3->nvbase.memory_clock_timer); } From bdc47bf3eef66ede54d7e6b1d33ea2ad914a477d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 00:38:06 +0000 Subject: [PATCH 029/274] start working on the actual graphics objects. Since we figured out the pfifo/ramht/object class crap. --- doc/Memory map (RIVA 128).txt | 81 +++++++++ doc/PFIFO RAMHT RAMRO.txt | 38 +++++ doc/nv3 driver init status.txt | 1 + doc/nv3_object_classes.txt | 37 ++++ doc/nv3_object_classes_driver.txt | 57 +++++++ doc/nv3d3d_t.txt | 161 ++++++++++++++++++ .../86box/nv/classes/vid_nv3_classes.h | 66 +++++++ src/include/86box/nv/vid_nv.h | 21 ++- src/include/86box/nv/vid_nv3.h | 70 +++++++- src/video/CMakeLists.txt | 2 + src/video/nv/nv3/classes/nv3_class_names.c | 63 +++++++ src/video/nv/nv3/nv3_core.c | 1 - src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 1 + src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 10 ++ 15 files changed, 591 insertions(+), 20 deletions(-) create mode 100644 doc/Memory map (RIVA 128).txt create mode 100644 doc/PFIFO RAMHT RAMRO.txt create mode 100644 doc/nv3_object_classes.txt create mode 100644 doc/nv3_object_classes_driver.txt create mode 100644 doc/nv3d3d_t.txt create mode 100644 src/include/86box/nv/classes/vid_nv3_classes.h create mode 100644 src/video/nv/nv3/classes/nv3_class_names.c diff --git a/doc/Memory map (RIVA 128).txt b/doc/Memory map (RIVA 128).txt new file mode 100644 index 000000000..fbfe9074f --- /dev/null +++ b/doc/Memory map (RIVA 128).txt @@ -0,0 +1,81 @@ +Memory map (RIVA 128) + +32 megabytes of MMIO starting at 0x0000000-0x01FFFFFF + +MC (Master Control) 0x00000000-0x00000FFF + Config/Boot 0x00000000-0x000000FF + Master Config 0x00000100-0x00000FFF + Also used to define DMA channel IDs? + +MPU-401 I/O 0x00000330-0x00000331 Probably NV1 leftover, NV1 has mpu401 emulation -- no audio in nv3 +VGA emulated ports 0x000003B0-0x000003DF Emulated I/O ports for VGA + +Bus Control (PBUS) 0x00001000-0x00001FFF +FIFO (PFIFO) 0x00002000-0x00003FFF Submit starting at 0x00800000. Used to configure RAMHT,RAMFC,RAMRO structures & cache + +PRM 0x00004000-0x00005FFF Realmode DOS device support + +PRMIO 0x00007000-0x00007FFF Realmode access to PCI BAR (Base Address Register) + PCI I/O +PTIMER 0x00009000-0x00009FFF Timer + +VGA emulation 0x0000A000-0x0000BFFF + +VGA vram emulation 0x000A0000-0x000BFFFF (PRMVGA) +VGA 0x000C0000-0x000C7FFF VGA sequencer + graph controller registers (PRMVIO) + +(All of this up to 0x0000FFFF can be traced) + +PFB (Framebuffer) 0x00100000-0x00100FFF Interface to vram +PEXTDEV 0x00101000-0x00101FFF External device interface - contains straps + Straps 0x00101000 11 bits of cfg + +ROM (VBIOS?) 0x00110000-0x0011FFFF +PALT (?) 0x00120000-0x00120FFF + +PEXTDEV 0x00101000-0x00101FFF External Devices + +Media Engine (PME) 0x00200000-0x00200FFF Allows for external video capture according to envytools? + +PGRAPH (3D rendering) 0x00400000-0x00401FFF + +PGRAPH objects (using RAMHT???) +*i assume that when you submit an object these are the registers used to actually draw the current object + +Beta blending factor 0x00410000-0x00411FFF +ROP 0x00420000-0x00421FFF Global bitwise operation (Render OPeration) for filtering the final pixel +Color Key 0x00430000-0x00431FFF +Plane Switch 0x00440000-0x00441FFF Something to do with color formats and objects? +Clipping 0x00450000-0x00451FFF +Blend Pattern 0x00460000-0x00461FFF Used for specific blending modes +Quad [OBSOLETE] 0x00470000-0x00471FFF A rectangle. NV1 LEFTOVER, OBSOLETE +Point 0x00480000-0x00481FFF A single point +Line 0x00490000-0x00491FFF A line (with an optional colour). Can also draw a polygon made out of lines - polyline +Lin 0x004A0000-0x004A1FFF A line, without starting or ending pixel (with an optional colour). Can also draw a "polylin" +Triangle [OBSOLETE] 0x004B0000-0x004B1FFF A triangle. NV1 LEFTOVER, OBSOLETE? +Win95 GDI text 0x004C0000-0x004C1FFF Win95 text acceleration +Memory to memory xfer 0x004D0000-0x004D1FFF Represents a memory to memory transfer +Scaled image from vram 0x004E0000-0x004E1FFF Scaled image from GPU VRAM +Image blit from vram 0x00500000-0x00501FFF Image from GPU VRAM +Image blit from cpu 0x00510000-0x00511FFF Image from CPU +Bitmap from cpu 0x00520000-0x00521FFF Bitmap from CPU +Image to memory 0x00540000-0x00541FFF Image to GPU VRAM +Stretch image from cpu 0x00550000-0x00551FFF Stretched image from CPU +Direct3D 5.0 triangle 0x00570000-0x00571FFF A triangle optimised explicitly for directx3/directx5 rendering - supercedes UTRI +PointZ 0x00580000-0x00581FFF A single point with "zeta factor" (not sure what this is yet) +Image in memory 0x005C0000-0x005C0FFF Image in vram(?) + +PVIDEO (Video Control) 0x00680000-0x006802FF +External DAC 0x00680000-0x006800FF + +PRMCIO 0x00601000-0x00601FFF VGA emulation - CRTC + attribute controller + +PRAMDAC 0x00680300-0x00680FFF (used for color lookup tables, hardware cursor, video overlay, PLL for clocking and pixel generation) +"USER" DAC 0x00681200-0x00681FFF + +DMA submission w/FIFO 0x00800000-0x00FFFFFF NV_USER - uses FIFO buffer + +PNVM / PDFB / VRAM 0x01000000-0x017FFFFF (actual VRAM amount depends on card, but max is 8MB) + +RAMIN 0x01C00000-0x01FFFFFF (note that this is actually mapped in the last 1mb of vram) + +contains ramht that has obj parameters for submission, configurable \ No newline at end of file diff --git a/doc/PFIFO RAMHT RAMRO.txt b/doc/PFIFO RAMHT RAMRO.txt new file mode 100644 index 000000000..9b4c94718 --- /dev/null +++ b/doc/PFIFO RAMHT RAMRO.txt @@ -0,0 +1,38 @@ +PIO VS dma + +driver is DIRECTLY modifying pfifo + +8X8 channel setup + +Names are 32-bit integers >4096 + +RAMFC - DMA Context object 0,0 to 8,8 + +context = channel, render object, object type, offset in instance memory + +for a rectangle (type 0x47), object render = 1, channel 0, at 0x0400 in the ramht memory + +=0x00c70400 as the context + +the ramht hash : + +xor every byte of the hash individually and then xor that with the channel number + +so obj id 01020304 in channel 0 is 1 xor 2 xor 3 xor 4 xor 0 + +Store in RAMHT at + 4*16 = name +Store in RAMHT at + 4*16 + 4 = context + +then the ramin stuff starts at *0xc04000 since c00000 is the start of ramin [PCI BAR1] which is where you put the contents of the class struct + +nv_user + +Consider the 8x8 channels as 64 subchannels. +Now you can do: + +They seem to end at 0x880000 + +(0x880000)/64 = 0x2000 for each channel + +FAILURE -> RAMRO! + diff --git a/doc/nv3 driver init status.txt b/doc/nv3 driver init status.txt index 6ac794588..025e781a2 100644 --- a/doc/nv3 driver init status.txt +++ b/doc/nv3 driver init status.txt @@ -105,3 +105,4 @@ NvFindAdapter -> RmInitNvMapping Success 19:18 26/11/2024 NvFindAdapter -> RmPostNvDevice Success 17:32 27/11/2024 NvFindAdapter -> NVGetNVInfo Success 17:32 27/11/2024 NvFindAdapter -> NVMapFrameBuffer Success 17:32 27/11/2024 + diff --git a/doc/nv3_object_classes.txt b/doc/nv3_object_classes.txt new file mode 100644 index 000000000..3cb99882b --- /dev/null +++ b/doc/nv3_object_classes.txt @@ -0,0 +1,37 @@ +Object classes as understood by the GPU. + +(May be represented, in RAMFC, as 0x40+ 22:16) + +0x00 = Invalid +0x01 = beta factor +0x02 = ROP5 operation +0x03 = Chroma key +0x04 = Plane mask +0x05 = Clipping rectangle +0x06 = Pattern +0x07 = Rectangle +0x08 = Point +0x09 = Line +0x0A = Lin (line without starting or ending pixel) +0x0B = Triangle +0x0C = Windows 95 GDI text acceleration +0x0D = Memory to memory format +0x0E = Scaled image from memory +0x0F = INVALID +0x10 = Blit +0x11 = Image +0x12 = Bitmap +0x13 = INVALID +0x14 = Transfer to Memory +0x15 = Stretched image from CPU +0x16 = INVALID +0x17 = Direct3D 5.0 accelerated textured triangle w/zeta buffer +0x18 = INVALID +0x19 = INVALID +0x1A = INVALID +0x1B = INVALID +0x1C = Image in memory +0x1D = INVALID +0x1E = INVALID +0x1F = INVALID + \ No newline at end of file diff --git a/doc/nv3_object_classes_driver.txt b/doc/nv3_object_classes_driver.txt new file mode 100644 index 000000000..83b421335 --- /dev/null +++ b/doc/nv3_object_classes_driver.txt @@ -0,0 +1,57 @@ +Object classes + +NOT represented by the GPU. These are the ones the drivers recognise. Most of them are faked. + +Version 0.77_win95 (!!!) + +object class 1 = "general" methods shared across all objects +object class 2 = DMA from memory +object class 3 = DMA to memory +object class 4 = timer +object class 5 = chip ID +object class 6 = "context ordinal" +object class 10/0Ah = patchcord (links objects together) +object class 11/0Bh = video sink +object class 12/0Ch = video switch +object class 13/0Dh = video colormap +object class 14/0Eh = patchcord...again +object class 15/0Fh = image to video +object class 16/10h = image stencil buffer +object class 17/11h = image blending methods +object class 18/12h = beta solid +object class 19/13h = image -> render operation +object class 20/14h = ROP5 (2^5 = 32bit rop) +object class 21/15h = color key +object class 22/16h = plane switch +object class 23/17h = "solid image" +object class 24/18h = pattern +object class 25/19h = black rectangle +object class 26/1Ah = solid point +object class 27/1Bh = solid line +object class 28/1Ch = solid lin (line without its ending or starting pixel) +object class 29/1Dh = triangle +object class 30/1Eh = solid rectangle +object class 31/1Fh = blit image +object class 33/21h = get image from cpu +object class 34/22h = get image from cpu, monochrome +object class 37/25h = get image from local GPU memory +object class 49/31h and 51/33h = patchcord, again +object class 54/36h = get image from cpu, stretched +object class 55/37h = get image from vram, scaled +object class 56/38h = get image from vram, scaled, YUV 420 color space (probably internal colour space used for rendering) +object class 57/39h = convert image(?) between formats in place in vram, allows you to set an in and out buffer and all that jazz +object class 61/3Dh = In-Memory DMA (vram to vram?) +object class 62/3Eh = get image from vram +object class 63/3Fh = get video from vram +object class 64/40h = video scaler +object class 65/41h = video color key (as opposed to image) +object class 66/42h = capture video to memory +object class 67/43h = Solid ROP5 +object class 68/44h = zeta buffer (something to do with how the gpu internally renders 3d stuff) from CPU(?) +object class 69/45h = zeta buffer in VRAM +object class 70/46h = zeta buffer patchcord +object class 71/47h = render solid point into zeta buffer (Also rectangle0 +object class 72/48h = render Direct3D 5.x ("D3D0") accelerated triangle into zeta buffer +object class 73/49h = render Direct3D 5.x ("D3D0") accelerated triangle +object class 74/4Ah = video source +object class 75/4Bh = render Windows GDI accelerated rectangle and text \ No newline at end of file diff --git a/doc/nv3d3d_t.txt b/doc/nv3d3d_t.txt new file mode 100644 index 000000000..c8579fd6c --- /dev/null +++ b/doc/nv3d3d_t.txt @@ -0,0 +1,161 @@ +12=unk_int12 +16=unk_short16 +22=unk_short22 (seems to determine if nv_sys_ptr is valid) +24=unk_short24 +26=unk_short26 +44=unk_int44 +56=unk_int56 +60=unk_int60 +64=unk_short64 +66=unk_short66 + +82=unk_short82 + +362..822: + big_struct + (weird alignment?) + 477 + 489 + 505 + +1140=nv_sys_ptr + + +1156=unk_int1156 [start of structure] +1160=unk_int1160 + +1168=unk_int1168 [possibly a byte array of up to 16 bytes) + +1184=unk_int1184 + +1208=unk_int1208 +1212=unk_int1212 +1216=unk_int1216 +1220=unk_int1220 +1224=unk_int1224 + +1236=unk_int1236 +1240=unk_int1240 +1244=unk_int1244 +1248=unk_int1248 + +1256=unk_int1256 +1260=fog_table_enable +1264=unk_int1264 +1270=unk_int1270 +1272=unk_byte1272 +1273=unk_byte1273 +1274=unk_byte1274 (?) +1275=unk_byte1275 + +1316=unk_int1316 +1320=unk_int1320 + +1332=unk_int1332 + +1340=unk_int1340 +1344=unk_int1344 + +1356=unk_int1356 +1360=d3d_clear_enabled +1364=texture_enabled +1368=mipmap_size_max +1372=mipmap_levels +1376=user_mipmaps +1380=zoh_mode (bool) +1384=tex_heap +1388=text_size +1392=video_texture +1396=min_video_tex_size +1400=draw_prim +1404=spread_x +1408=spread_y +1412=size_adj +1416=turbo_adj +1420=dma_min_push_count +1424=dma_push_enable + +1436=unk_int1436 +1440=unk_int1440 + +1448=unk_int1448 +1452=unk_int1452 (set to value of unk_int1908) +1456=unk_int1456 (set to value of unk_int1956) +1460=unk_int1460 (set to value of unk_int2020) + +1516=unk_int1516 +1520=unk_int1520 +1524=unk_int1524 +1528=unk_int1528 +1532=unk_int1532 + +1548=unk_int1548 +1552=unk_int1552 +1556=unk_int1556 +1560=unk_int1560 +1564=unk_int1564 + +1600=unk_int1600 + +1612=unk_int1612 +1616=unk_int1616 +1620=unk_int1620 + +1632=unk_int1632 + +1640=unk_int1640 +1644=unk_ptr1644 + +1676=unk_int1676 +1680=unk_int1680 +1684=unk_int1684 +1688=unk_int1688 +1692=unk_int1692 +1696=unk_int1696 +1700=unk_int1700 + +1716=unk_int1716 +1720=unk_int1720 +1724=unk_int1724 +1728=unk_ptr1728 + +1848=unk_int1848 +1852=unk_int1852 +1856=unk_int1856 (unk_int1552 | 0x800) +1864=unk_func_ptr1864 +1868=unk_int1868 + +1884=unk_int1884 +1888=ptr_to_start_of_structure? +1892=hInstDll +1896=unk_int1896 +1900=unk_int1900 + +1908=unk_int1908 +1912=unk_int1912 +1916=unk_func_ptr1916 +1920=unk_func_ptr1920 + +1932=unk_func_ptr1932 +1936=unk_func_ptr1936 +1944=unk_func_ptr1944 + +1956..2020: Set of function pointrs + 1956=unk_int1956 + 1960=unk_int1960 + 1960=unk_func_ptr1960 + 1964=unk_func_ptr1964 + 1968=unk_func_ptr1968 + 1976=unk_func_ptr1976 + 1980=unk_func_ptr1980 + 1988=unk_func_ptr1988 + 1996=unk_func_ptr1996 + 2000=unk_func_ptr2000 + 2004=unk_func_ptr2004 + 2008=unk_func_ptr2008 + + 2020=unk_int2020 + +2024=unk_int2024 +2028=unk_int2028 +2032=unk_int2032 diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h new file mode 100644 index 000000000..e47b5eb71 --- /dev/null +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -0,0 +1,66 @@ +/* + * 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. + * + * Defines graphics objects for Nvidia NV3 architecture-based GPU (RIVA 128/RIVA 128 ZX), + * as well as for later GPUs if they use the same objects. + * + * + * + * Authors: Connor Hyde + * + * Copyright 2024-2025 Connor Hyde + */ + +#pragma once +#include +#include +#include +#include + +/* + Note: These uint32_ts are basically object methods that are being submitted + They have different names so the user can use them more easily but different versions of the same class can be distinguished + + ALL of these structures HAVE to be a size of exactly 0x2000 bytes because that's what the hashtable expects. + + Also, these class IDs don't relate to the internal architecture of the GPU. + Effectively, the NVIDIA drivers are faking shit. There are only 16 classes but the drivers recognise many more. See nv3_object_classes_driver.txt for the list of + classes recognised by the driver. + + The 3-bit DMA SUBCHANNEL is combined with a 4-bit CLASS ID to get the REAL CLASS ID. There are 32 CLASSES per subchannel and 8 SUBCHANNELS. + + This is why the Class IDs you see here are not the same as you may see in other places. +*/ + +extern const char* nv3_class_names[]; + +/* +Object Class 0x07 (real hardware) + 0x1E (drivers) +Also 0x47 in context IDs +A rectangle. Wahey! +*/ +typedef struct nv_object_class_007 +{ + uint8_t reserved[0xFF]; // Required for NV_CLASS Core Functionality + uint32_t set_notify_ctx_dma; // Set notifier context for DMA + uint32_t set_notify; // Set notifier + uint32_t set_image_output; // Set the image output type + uint8_t reserved2[0xF5]; // up to 0x200 + uint32_t set_zeta_output; // Zeta buffer input + uint32_t set_zeta_input; // Zeta buffer input + uint32_t set_color_format; // Color format: 0x100000=15bpp. + uint8_t reserved3[0xF5]; // up to 0x300 + + /* THESE ARE ALL THE SAME METHOD */ + uint32_t color_zeta32; // 32-bit zeta buffer color (?) + uint32_t point; // Draw a point i guess + uint8_t reserved4[0x4F3]; // up to 0x7fc + uint32_t control_out; // 7fd-7ff + uint8_t reserved5[0x1800]; // up to 0x2000 +} nv3_rectangle_t; diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index b7450338b..39e9a68d0 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -6,20 +6,23 @@ * * This file is part of the 86Box distribution. * - * JENSEN HUANG APPROVED !!!! + * Shared implementation file for all NVIDIA GPUs (hopefully to be) emulated by 86box. * * Credit to: * - * - fuel (PCBox developer) - * - Marcelina Kościelnicka (envytools) - * - nouveau developers - * - Utah GLX developers - * - XFree86 developers - * - xemu developers - * + * - Marcelina Kościelnicka (envytools) https://envytools.readthedocs.io/en/latest/ + * - fuel (PCBox developer) https://github.com/PCBox/PCBox + * - nouveau developers https://nouveau.freedesktop.org/ + * - Utah GLX developers https://utah-glx.sourceforge.net/ + * - XFree86 developers https://www.xfree86.org/ + * - xemu developers https://github.com/xemu-project/xemu + * - RivaTV developers https://rivatv.sourceforge.net (esp. https://rivatv.sourceforge.net/stuff/riva128.txt) + * - Nvidia for leaking their driver symbols numerous times ;^) https://nvidia.com + * - People who prevented me from giving up (various) + * * Authors: Connor Hyde / starfrost * - * Copyright 2024 Connor Hyde + * Copyright 2024-2025 Connor Hyde */ #ifdef EMU_DEVICE_H // what diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index d185cd408..b17423305 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -12,11 +12,11 @@ * * Authors: Connor Hyde * - * Copyright 2024 Connor Hyde + * Copyright 2024-2025 Connor Hyde */ // vid_nv3.h: NV3 Architecture Hardware Reference (open-source) -// Last updated 2 December 2024 +// Last updated 30 December 2024 // The GPU base structure extern const device_config_t nv3_config[]; @@ -28,13 +28,20 @@ extern const device_config_t nv3_config[]; #define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start #define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN +// DMA channels are basically the number of contexts that the gpu can deal with at once. +// Channel 0 is always taken up by NV drivers. + +// Subchannels deal with specific parts of the GPU and are manipulated by the driver to manipulate the gpu. +#define NV3_DMA_CHANNELS 8 +#define NV3_DMA_SUBCHANNELS_PER_CHANNEL 8 + #define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 10 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. // Default value for the boot information register. // Depends on the chip -#define NV3_BOOT_REG_REV_A00 0x00030100 -#define NV3_BOOT_REG_REV_B00 0x00030110 -#define NV3_BOOT_REG_REV_C00 0x00030120 +#define NV3_BOOT_REG_REV_A00 0x00030100 +#define NV3_BOOT_REG_REV_B00 0x00030110 +#define NV3_BOOT_REG_REV_C00 0x00030120 // various vbioses for testing // Coming soon: MIROmagic Premium BIOS (when I get mine dumped) @@ -297,9 +304,9 @@ extern const device_config_t nv3_config[]; #define NV3_PSTRAPS_OVERWRITE_ENABLED 0x1 #define NV3_PEXTDEV_END 0x101FFF #define NV3_PROM_START 0x110000 // VBIOS? -#define NV3_PROM_END 0x110FFF +#define NV3_PROM_END 0x11FFFF #define NV3_PALT_START 0x120000 // ??? but it exists -#define NV3_PALT_END 0x120FFF +#define NV3_PALT_END 0x12FFFF #define NV3_PME_START 0x200000 // Mediaport #define NV3_PME_INTR 0x200100 // Mediaport: Interrupt Pending? #define NV3_PME_INTR_EN 0x200140 // Mediaport: Interrupt Enable @@ -459,7 +466,7 @@ extern const device_config_t nv3_config[]; #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_START 0x800000 // Mapping for the area where objects are submitted into the FIFO (up to 0x880000?) #define NV3_USER_END 0xFFFFFF // easier name @@ -478,6 +485,8 @@ extern const device_config_t nv3_config[]; // 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 @@ -651,8 +660,15 @@ typedef struct nv_pfifo_s { uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable + uint32_t ramht_config; // RAMHT config + uint32_t ramfc_config; // RAMFC config + uint32_t ramro_config; // RAMRO config + uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? + } nv3_pfifo_t; +// create_object(uint32_t type) here + // RAMDAC typedef struct nv3_pramdac_s { @@ -761,12 +777,48 @@ typedef struct nv3_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; +typedef struct nv3_pramin_name_s +{ + union + { + uint32_t name; + uint8_t byte_high; + uint8_t byte_mid2; + uint8_t byte_mid1; + uint8_t byte_low; + }; +} nv3_pramin_name_t; + +typedef struct nv3_pramin_context_s +{ + union + { + uint32_t context; + uint8_t dma_channel; + uint8_t render_object; //0=sw, 1=render + uint8_t class_id; + uint8_t ramin_offset; //find + }; +} nv3_pramin_context_t; + +// Graphics object hashtable for specific DMA [channel, subchannel] pair +typedef struct nv3_pramin_ramht_subchannel_s +{ + nv3_pramin_name_t name; // must be >4096 + + // Contextual information. + // See the above union. + nv3_pramin_context_t context; +} nv3_pramin_ramht_subchannel_t; + // Graphics object hashtable typedef struct nv3_pramin_ramht_s { - + nv3_pramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; } nv3_pramin_ramht_t; +uint32_t nv3_pramin_ramht_hash(nv3_pramin_name_t name, uint32_t channel); + // Anti-fuckup device typedef struct nv3_pramin_ramro_s { diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 4b927c9ea..b99f4fc4b 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -41,6 +41,8 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.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 + + nv/nv3/classes/nv3_class_names.c ) if(G100) diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c new file mode 100644 index 000000000..48b85cab4 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -0,0 +1,63 @@ +/* + * 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: Defines core class names for debugging purposes + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +/* Taken from Win9x drivers 0.77, these had the best reversing potential */ + +#include <86Box/nv/classes/vid_nv3_classes.h> + + +/* These are the object classes AS RECOGNISED BY THE GRAPHICS HARDWARE. */ +/* The drivers implement a COMPLETELY DIFFERENT SET OF CLASSES. */ + +/* THERE CAN ONLY BE 32 CLASSES IN NV3 BECAUSE THE CLASS ID PART OF THE CONTEXT OF A GRAPHICS OBJECT IN PFIFO RAM HASH TABLE IS ONLY 5 BITS LONG! */ + +const char* nv3_class_names[] = +{ + "NV3 INVALID class 0x00", + "NV3 class 0x01: Beta factor", + "NV3 class 0x02: ROP5 (32-bit) operation", + "NV3 class 0x03: Chroma key", + "NV3 class 0x04: Plane mask", + "NV3 class 0x05: Clipping rectangle", + "NV3 class 0x06: Pattern", + "NV3 class 0x07: Rectangle", + "NV3 class 0x08: Point", + "NV3 class 0x09: Line", + "NV3 class 0x0A: Lin (line without starting or ending pixel)", + "NV3 class 0x0B: Triangle", + "NV3 class 0x0C: Windows 95 GDI text acceleration", + "NV3 class 0x0D: Memory to memory format", + "NV3 class 0x0E: Scaled image from memory", + "NV3 INVALID class 0x0F", + "NV3 class 0x10: Blit", + "NV3 class 0x11: Image", + "NV3 class 0x12: Bitmap", + "NV3 INVALID class 0x13", + "NV3 class 0x14: Transfer to Memory", + "NV3 class 0x15: Stretched image from CPU", + "NV3 INVALID class 0x16", + "NV3 class 0x17: Direct3D 5.0 accelerated textured triangle w/zeta buffer", + "NV3 INVALID class 0x18", + "NV3 INVALID class 0x19", + "NV3 INVALID class 0x1A", + "NV3 INVALID class 0x1B", + "NV3 class 0x1C: Image in Memory", + "NV3 INVALID class 0x1D", + "NV3 INVALID class 0x1E", + "NV3 INVALID class 0x1F", + +}; diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 0636a3c91..e736e0c79 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -614,7 +614,6 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) uint8_t old_value; // todo: - // RMA // Pixel formats (8bit vs 555 vs 565) // VBE 3.0? diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 1ce59171a..57d19fc42 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -28,3 +28,4 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> +/* Nvidia DMA Engine */ \ 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 index 7448f77c7..08a2b48ff 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -115,7 +115,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) // Write 16-bit ramin void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) { - addr &= (nv3->nvbase.svga.vram_max- 1); + addr &= (nv3->nvbase.svga.vram_max - 1); // why does this not work in one line svga_t* svga = &nv3->nvbase.svga; diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index 7f80a1035..b77f18729 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -28,3 +28,13 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> +/* This implements the hash that all the objects are stored within. +It is used to get the offset within RAMHT of a graphics object. + */ + +uint32_t nv3_pramin_ramht_hash(nv3_pramin_name_t name, uint32_t channel) +{ + uint32_t hash = (name.byte_high ^ name.byte_mid2 ^ name.byte_mid1 ^ name.byte_low ^ (uint8_t)channel); + nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)", name, channel); + return hash; +} \ No newline at end of file From 54bafba20cefaaeb878eda5f165dcb4559302a18 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 16:14:52 +0000 Subject: [PATCH 030/274] Suppress logging of ptimer time unless it changes to massively reduce logspam --- src/video/nv/nv3/subsystems/nv3_ptimer.c | 29 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 7e718264a..c0d68a24c 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -69,12 +69,19 @@ void nv3_ptimer_tick() // to the state. double current_time = ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? + // Logging is suppressed when reading this register because it is read many times + // So we only log when we update. + // truncate it nv3->ptimer.time += (uint64_t)current_time; + + nv_log("PTIMER time ticked (The value is now 0x%08x)\n", nv3->ptimer.time); + // Check if the alarm has actually triggered... if (nv3->ptimer.time >= nv3->ptimer.alarm) { + nv_log("PTIMER alarm interrupt fired because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); } } @@ -85,7 +92,13 @@ uint32_t nv3_ptimer_read(uint32_t address) nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); - nv_log("NV3: PTIMER Read from 0x%08x", address); + // Only log these when tehy actually tick + if (address != NV3_PTIMER_TIME_0_NSEC + && address != NV3_PTIMER_TIME_1_NSEC) + { + nv_log("NV3: PTIMER Read from 0x%08x", address); + } + uint32_t ret = 0x00; @@ -129,11 +142,15 @@ uint32_t nv3_ptimer_read(uint32_t address) } } - - if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); - else - nv_log("\n"); + //TIME0 and TIME1 produce too much log spam that slows everything down + if (reg->address != NV3_PTIMER_TIME_0_NSEC + && reg->address != NV3_PTIMER_TIME_1_NSEC) + { + if (reg->friendly_name) + nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + else + nv_log("\n"); + } } else { From 713ef4585b362f90cd41f6b5e81d4722f98d157c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 16:36:24 +0000 Subject: [PATCH 031/274] PRMVIO and interrupt fixes. --- src/video/nv/nv3/nv3_core.c | 6 ++++++ src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 +- src/video/nv/nv3/subsystems/nv3_pmc.c | 7 +++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e736e0c79..a7caa0b2b 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -120,6 +120,8 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) // Write 8-bit MMIO void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) { + addr &= 0xFFFFFF; + // This is weitek vga stuff if (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) @@ -144,6 +146,8 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // Write 16-bit MMIO void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) { + addr &= 0xFFFFFF; + // This is weitek vga stuff if (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) @@ -169,6 +173,8 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // Write 32-bit MMIO void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) { + addr &= 0xFFFFFF; + // This is weitek vga stuff if (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 9dfca4f00..1d44a3f94 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -231,7 +231,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // Fire a VALID Pgraph interrupt: num is the bit# of the interrupt in the GPU subsystem INTR_EN register. void nv3_pgraph_interrupt_valid(uint32_t num) { - nv3->pgraph.interrupt_enable_0 |= (1 << num); + nv3->pgraph.interrupt_status_0 |= (1 << num); nv3_pmc_handle_interrupts(true); } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 6b2b51b09..1cc28140c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -89,9 +89,12 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) 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)) + if (nv3->pgraph.interrupt_status_0 & (1 << 8) + && nv3->pgraph.interrupt_enable_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 + + if (nv3->pgraph.interrupt_status_0 & ~(1 << 8) + && nv3->pgraph.interrupt_enable_0 & ~(1 << 8)) // otherwise PGRAPH-0 interurpt new_intr_value |= (NV3_PMC_INTERRUPT_PGRAPH0_PENDING << NV3_PMC_INTERRUPT_PGRAPH0); // Check second pgraph interrupt register From 9d97e3c7bae50950056671ff258f5975bd708bd8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 16:50:47 +0000 Subject: [PATCH 032/274] More anti-stupidity measures. CLEAR interrupts...WHEN WE CLEAR THEM...AND DON'T SET SPURIOUS INTERRUPTS! --- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 1cc28140c..d32738a5e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -61,7 +61,7 @@ nv_register_t pmc_registers[] = { 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); + pci_clear_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); } // Handle hardware interrupts From ec0cf28d25dfc7c71228d4fd52bb7885db3a7644 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 19:59:33 +0000 Subject: [PATCH 033/274] Implement some basic i2c shit and more SVGA shit, so that the drivers don't magically explode. LOL --- src/include/86box/nv/vid_nv.h | 17 ++++-- src/include/86box/nv/vid_nv3.h | 5 +- src/video/nv/nv3/nv3_core.c | 84 +++++++++++++++++++++++---- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- 4 files changed, 90 insertions(+), 18 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 39e9a68d0..06c545fc7 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -28,6 +28,8 @@ //TODO: split this all into nv1, nv3, nv4... +#include <86box/i2c.h> +#include <86box/vid_ddc.h> #include <86box/timer.h> #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> @@ -68,16 +70,19 @@ void nv_log(const char *fmt, ...); typedef enum nv_bus_generation_e { + // NV1 - Prototype version + nv_bus_vlb = 0, + // NV1 // NV3 - nv_bus_pci = 0, + nv_bus_pci = 1, // NV3 - nv_bus_agp_1x = 1, + nv_bus_agp_1x = 2, // NV3T // NV4 - nv_bus_agp_2x = 2, + nv_bus_agp_2x = 3, } nv_bus_generation; @@ -103,11 +108,13 @@ typedef struct nv_base_s nv_bus_generation bus_generation; // current bus (see nv_bus_generation documentation) uint32_t gpu_revision; // GPU Stepping double pixel_clock_period; // Period in seconds for pixel clock - rivatimer_t* pixel_clock_timer; + rivatimer_t* pixel_clock_timer; // Timer for measuring pixel clock bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_period; // Period in seconds for pixel clock - rivatimer_t* memory_clock_timer; + rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times + void* i2c; // I2C for monitor EDID + void* ddc; } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index b17423305..5aa0e6c79 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -568,7 +568,10 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_PIXELMODE_16BPP 0x02 #define NV3_CRTC_REGISTER_PIXELMODE_32BPP 0x03 -#define NV3_CRTC_REGISTER_RMA 0x38 // REAL MODE ACCESS! +#define NV3_CRTC_REGISTER_RL0 0x34 +#define NV3_CRTC_REGISTER_RL1 0x35 +#define NV3_CRTC_REGISTER_RMA 0x38 // REAL MODE ACCESS! +#define NV3_CRTC_REGISTER_I2C 0x3F // where the fuck is GDC? #define NV3_CRTC_BANKED_128K_A0000 0x00 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index a7caa0b2b..cfbb0c324 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -243,7 +243,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) 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 + ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_H] & NV3_PCI_COMMAND_H_FAST_BACK2BACK; // always enable fast back2back break; // pci status register @@ -447,15 +447,6 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) } } -void nv3_close(void* priv) -{ - rivatimer_destroy(nv3->nvbase.pixel_clock_timer); - rivatimer_destroy(nv3->nvbase.memory_clock_timer); - svga_close(&nv3->nvbase.svga); - free(nv3); - nv3 = NULL; -} - // // SVGA functions @@ -575,6 +566,18 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) // Support the extended NVIDIA CRTC register range switch (nv3->nvbase.svga.crtcreg) { + case NV3_CRTC_REGISTER_RL0: + ret = nv3->nvbase.svga.displine & 0xFF; + break; + /* Is rl1?*/ + case NV3_CRTC_REGISTER_RL1: + ret = (nv3->nvbase.svga.displine >> 8) & 7; + break; + case NV3_CRTC_REGISTER_I2C: + ret = i2c_gpio_get_sda((nv3->nvbase.i2c) << 3) + | i2c_gpio_get_scl((nv3->nvbase.i2c) << 2); + + break; default: ret = nv3->nvbase.svga.crtc[nv3->nvbase.svga.crtcreg]; } @@ -641,6 +644,8 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) val = (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_OVERFLOW] & ~0x10) | (val & 0x10); // set the register value... + uint8_t old_value = nv3->nvbase.svga.crtc[crtcreg]; + nv3->nvbase.svga.crtc[crtcreg] = val; // ...now act on it @@ -664,6 +669,27 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) case NV3_CRTC_REGISTER_RMA: nv3->pbus.rma.mode = val & NV3_CRTC_REGISTER_RMA_MODE_MAX; break; + case NV3_CRTC_REGISTER_I2C: + uint8_t scl = !!(val & 0x20); + uint8_t sda = !!(val & 0x10); + // Set an I2C GPIO register + i2c_gpio_set(nv3->nvbase.i2c, scl, sda); + break; + } + + /* Recalculate the timings if we actually changed them + Additionally only do it if the value actually changed*/ + if (old_value != val) + { + // Thx to Fuel who basically wrote all the SVGA compatibility code already (although I fixed some issues), because VGA is boring + // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. + // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" + if (nv3->nvbase.svga.crtcreg < 0xE + && nv3->nvbase.svga.crtcreg > 0x10) + { + nv3->nvbase.svga.fullchange = changeframecount; + svga_recalctimings(&nv3->nvbase.svga); + } } break; @@ -937,7 +963,9 @@ void* nv3_init(const device_t *info) nv3_ptimer_init(); // Initialise programmable interval timer nv3_pvideo_init(); // Initialise video overlay engine - nv_log("NV3: Initialising timers...\n"); + nv_log("NV3: Initialising I2C..."); + nv3->nvbase.i2c = i2c_gpio_init("nv3_i2c"); + nv3->nvbase.ddc = ddc_init(i2c_gpio_get_bus(nv3->nvbase.i2c)); return nv3; } @@ -958,6 +986,38 @@ void* nv3_init_agp(const device_t* info) nv3_init(info); } +void nv3_close(void* priv) +{ + // Shut down I2C and the DDC + i2c_gpio_close(nv3->nvbase.i2c); + ddc_close(nv3->nvbase.ddc); + + // Destroy the Rivatimers. (It doesn't matter if they are running.) + rivatimer_destroy(nv3->nvbase.pixel_clock_timer); + rivatimer_destroy(nv3->nvbase.memory_clock_timer); + + // Shut down SVGA + svga_close(&nv3->nvbase.svga); + free(nv3); + nv3 = NULL; +} + +// See if the bios rom is available. +void nv3_available() +{ + return rom_present(NV3_VBIOS_ASUS_V3000_V151) + || rom_present(NV3_VBIOS_DIAMOND_V330_V162) + || rom_present(NV3_VBIOS_ERAZOR_V14700) + || rom_present(NV3_VBIOS_ERAZOR_V15403) + || rom_present(NV3_VBIOS_ERAZOR_V15500) + || rom_present(NV3_VBIOS_STB_V128_V182) + || rom_present(NV3_VBIOS_STB_V128_V182) + || rom_present(NV3T_VBIOS_ASUS_V170) + || rom_present(NV3T_VBIOS_DIAMOND_V330_V182B) + || rom_present(NV3T_VBIOS_REFERENCE_CEK_V171) + || rom_present(NV3T_VBIOS_REFERENCE_CEK_V172); +} + // NV3 (RIVA 128) // PCI // 2MB or 4MB VRAM @@ -971,6 +1031,7 @@ const device_t nv3_device_pci = .close = nv3_close, .speed_changed = nv3_speed_changed, .force_redraw = nv3_force_redraw, + .available = nv3_available, .config = nv3_config, }; @@ -987,5 +1048,6 @@ const device_t nv3_device_agp = .close = nv3_close, .speed_changed = nv3_speed_changed, .force_redraw = nv3_force_redraw, + .available = nv3_available, .config = nv3_config, }; \ 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 index d32738a5e..7313a7554 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -72,7 +72,7 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) // PGRAPH DMA INTR_EN (there is no DMA engine yet) // PRM Real-Mode Compatibility Interrupts - uint32_t new_intr_value; + uint32_t new_intr_value = 0x00; // set the new interrupt value // PAUDIO not used From f6e1ce28df9bc4c5a7e55dafbd9a440f08ce5581 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 20:01:07 +0000 Subject: [PATCH 034/274] fix compile --- src/video/nv/nv3/nv3_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index cfbb0c324..ba0cd16ac 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -574,8 +574,8 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) ret = (nv3->nvbase.svga.displine >> 8) & 7; break; case NV3_CRTC_REGISTER_I2C: - ret = i2c_gpio_get_sda((nv3->nvbase.i2c) << 3) - | i2c_gpio_get_scl((nv3->nvbase.i2c) << 2); + ret = i2c_gpio_get_sda(nv3->nvbase.i2c) << 3 + | i2c_gpio_get_scl(nv3->nvbase.i2c) << 2; break; default: @@ -1003,7 +1003,7 @@ void nv3_close(void* priv) } // See if the bios rom is available. -void nv3_available() +int32_t nv3_available() { return rom_present(NV3_VBIOS_ASUS_V3000_V151) || rom_present(NV3_VBIOS_DIAMOND_V330_V162) From 486087face1c9bc7d2146ff9b680472ffb69de8b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 20:42:34 +0000 Subject: [PATCH 035/274] Actually send the reads and writes to the right place. Fix driver init infinite loop at stateNv -> stateGr(STATE_INIT) -> initGr phase. --- src/include/86box/nv/vid_nv3.h | 6 ++++ src/video/nv/nv3/nv3_core.c | 52 ++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 5aa0e6c79..80766be94 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -425,6 +425,12 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_REAL_END 0x5C1FFF #define NV3_PRMCIO_START 0x601000 +// Following four are CRTC+I2C access registers +// and get redirected to VGA +#define NV3_PRMCIO_CRX_MONO 0x6013B4 +#define NV3_PRMCIO_CR_MONO 0x6013B5 +#define NV3_PRMCIO_CRX_COLOR 0x6013D4 +#define NV3_PRMCIO_CR_COLOR 0x6013D5 #define NV3_PRMCIO_END 0x601FFF #define NV3_PDAC_START 0x680000 // OPTIONAL external DAC diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ba0cd16ac..cfbb282d5 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -52,12 +52,18 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) addr &= 0xFFFFFF; if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; ret = nv3_svga_in(real_address, nv3); + + nv_log("NV3: Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x", addr, ret); return ret; } @@ -76,7 +82,11 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) addr &= 0xFFFFFF; if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -84,6 +94,8 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); + nv_log("NV3: Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x", addr, ret); + return ret; } @@ -100,7 +112,11 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) addr &= 0xFFFFFF; if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -109,7 +125,9 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 1, nv3) << 8) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - + + nv_log("NV3: Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x", addr, ret); + return ret; } @@ -123,12 +141,18 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) addr &= 0xFFFFFF; // This is weitek vga stuff + // If we need to add more of these we can convert these to a switch statement if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; + nv_log("NV3: Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); return; @@ -150,11 +174,16 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // This is weitek vga stuff if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; + nv_log("NV3: Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -177,11 +206,17 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // This is weitek vga stuff if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END) + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; + nv_log("NV3: Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x", addr, val); + nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); nv3_svga_out(real_address + 2, (val >> 16) & 0xFF, nv3); @@ -989,8 +1024,9 @@ void* nv3_init_agp(const device_t* info) void nv3_close(void* priv) { // Shut down I2C and the DDC - i2c_gpio_close(nv3->nvbase.i2c); ddc_close(nv3->nvbase.ddc); + i2c_gpio_close(nv3->nvbase.i2c); + // Destroy the Rivatimers. (It doesn't matter if they are running.) rivatimer_destroy(nv3->nvbase.pixel_clock_timer); From f7ccbc4e718351d500461483f5ceeca2671a3bda Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 22:03:06 +0000 Subject: [PATCH 036/274] Run PTIMER at a reasonable speed. Fix I2C register.s This means we actually for real get beyond stateGr and now try to accelerate rendering --- src/include/86box/nv/vid_nv.h | 4 ++-- src/include/86box/nv/vid_nv3.h | 23 ++++++++++++----------- src/include/86box/nv/vid_nv_rivatimer.h | 4 ++-- src/video/nv/nv3/nv3_core.c | 16 ++++++++-------- src/video/nv/nv3/subsystems/nv3_pramdac.c | 18 +++++++----------- src/video/nv/nv3/subsystems/nv3_ptimer.c | 11 +++++++++-- src/video/nv/nv_rivatimer.c | 6 +++--- 7 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 06c545fc7..d067c0148 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -107,10 +107,10 @@ typedef struct nv_base_s 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) uint32_t gpu_revision; // GPU Stepping - double pixel_clock_period; // Period in seconds for pixel clock + double pixel_clock_frequency; // Frequency used for pixel clock rivatimer_t* pixel_clock_timer; // Timer for measuring pixel clock bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times - double memory_clock_period; // Period in seconds for pixel clock + double memory_clock_frequency; // Source Frequency for PTIMER rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 80766be94..a404df979 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -577,7 +577,8 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_RL0 0x34 #define NV3_CRTC_REGISTER_RL1 0x35 #define NV3_CRTC_REGISTER_RMA 0x38 // REAL MODE ACCESS! -#define NV3_CRTC_REGISTER_I2C 0x3F +#define NV3_CRTC_REGISTER_I2C 0x3E +#define NV3_CRTC_REGISTER_I2C_GPIO 0x3F // where the fuck is GDC? #define NV3_CRTC_BANKED_128K_A0000 0x00 @@ -904,7 +905,7 @@ void nv3_speed_changed(void *priv); void nv3_force_redraw(void* priv); // Memory Mapping -void nv3_update_mappings(); +void nv3_update_mappings(); // Update memory 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 @@ -917,12 +918,12 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); 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 +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 @@ -1015,12 +1016,12 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val); void nv3_pramdac_init(); void nv3_pramdac_set_vram_clock(); void nv3_pramdac_set_pixel_clock(); -void nv3_pramdac_pixel_clock_poll(); -void nv3_pramdac_memory_clock_poll(); +void nv3_pramdac_pixel_clock_poll(double real_time); +void nv3_pramdac_memory_clock_poll(double real_time); // NV3 PTIMER void nv3_ptimer_init(); -void nv3_ptimer_tick(); +void nv3_ptimer_tick(double real_time); // NV3 PVIDEO void nv3_pvideo_init(); diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index 31483992e..d5bb86b90 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -73,12 +73,12 @@ typedef struct rivatimer_s } rivatimer_t; void rivatimer_init(); // Initialise the Rivatimer. -rivatimer_t* rivatimer_create(double period, void (*callback)()); +rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)); void rivatimer_destroy(rivatimer_t* rivatimer_ptr); void rivatimer_update_all(); void rivatimer_start(rivatimer_t* rivatimer_ptr); void rivatimer_stop(rivatimer_t* rivatimer_ptr); double rivatimer_get_time(rivatimer_t* rivatimer_ptr); -void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()); +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)); void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index cfbb282d5..cd1614e8e 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -63,8 +63,8 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3); - nv_log("NV3: Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x", addr, ret); - + nv_log("NV3: Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x\n", addr, ret); + return ret; } @@ -94,7 +94,7 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); - nv_log("NV3: Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x", addr, ret); + nv_log("NV3: Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); return ret; } @@ -126,7 +126,7 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - nv_log("NV3: Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x", addr, ret); + nv_log("NV3: Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x\n", addr, ret); return ret; } @@ -152,7 +152,7 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x", addr, val); + nv_log("NV3: Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); return; @@ -183,7 +183,7 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x", addr, val); + nv_log("NV3: Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -215,7 +215,7 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x", addr, val); + nv_log("NV3: Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -704,7 +704,7 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) case NV3_CRTC_REGISTER_RMA: nv3->pbus.rma.mode = val & NV3_CRTC_REGISTER_RMA_MODE_MAX; break; - case NV3_CRTC_REGISTER_I2C: + case NV3_CRTC_REGISTER_I2C_GPIO: uint8_t scl = !!(val & 0x20); uint8_t sda = !!(val & 0x10); // Set an I2C GPIO register diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 0c57b71cb..9c44105b9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -50,18 +50,16 @@ void nv3_pramdac_init() // Polls the pixel clock. // This updates the 2D/3D engine PGRAPH -void nv3_pramdac_pixel_clock_poll() +void nv3_pramdac_pixel_clock_poll(double real_time) { - // TODO: UPDATE PGRAPH! + // TODO: ???? } // Polls the memory clock. -void nv3_pramdac_memory_clock_poll() +void nv3_pramdac_memory_clock_poll(double real_time) { - //nv3_t* nv3_poll = (nv3_t*)priv; - - // Let's hope qeeg was right here. - nv3_ptimer_tick(); + nv3_ptimer_tick(real_time); + // TODO: UPDATE PGRAPH! } // Gets the vram clock register. @@ -120,10 +118,8 @@ void nv3_pramdac_set_vram_clock() nv_log("NV3: Memory clock = %.2f MHz\n", frequency / 1000000.0f); - nv3->nvbase.memory_clock_period = time; + nv3->nvbase.memory_clock_frequency = frequency; - //timer_on_auto(&nv3->nvbase.memory_clock_timer, nv3->nvbase.memory_clock_period); - // Create and start if it it's not running. if (!nv3->nvbase.memory_clock_timer) { @@ -170,7 +166,7 @@ void nv3_pramdac_set_pixel_clock() nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); - nv3->nvbase.pixel_clock_period = time; + nv3->nvbase.pixel_clock_frequency = frequency; // Create and start if it it's not running. if (!nv3->nvbase.pixel_clock_timer) diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index c0d68a24c..ab19c1e61 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -57,7 +57,7 @@ void nv3_ptimer_interrupt(uint32_t num) } // Ticks the timer. -void nv3_ptimer_tick() +void nv3_ptimer_tick(double real_time) { // do not divide by zero if (nv3->ptimer.clock_numerator == 0 @@ -67,7 +67,14 @@ void nv3_ptimer_tick() // get the current time // Due to timer system limitations, the timer system is not capable of running at 100 megahertz. Therefore, we have to scale it down and then scale up the level of changes // to the state. - double current_time = ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? + + // See Envytools. We need to use the frequency as a source. + // But we need to figure out how many cycles actually occurred because this counts up every cycle... + + // Convert to microseconds + double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv3->nvbase.memory_clock_frequency); + + double current_time = freq_base * ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? // Logging is suppressed when reading this register because it is read many times // So we only log when we update. diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index c31ee11fe..343ec02ff 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -59,7 +59,7 @@ void rivatimer_init() } // Creates a rivatimer. -rivatimer_t* rivatimer_create(double period, void (*callback)()) +rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)) { rivatimer_t* new_rivatimer = NULL; @@ -210,7 +210,7 @@ void rivatimer_update_all() continue; } - rivatimer_ptr->callback(); + rivatimer_ptr->callback(microseconds); } rivatimer_ptr = rivatimer_ptr->next; @@ -254,7 +254,7 @@ double rivatimer_get_time(rivatimer_t* rivatimer_ptr) return rivatimer_ptr->time; } -void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)()) +void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)) { if (!rivatimer_really_exists(rivatimer_ptr)) fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); From d30307e2a5013f54610e7cc81e37e76882eaf5b0 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 22:10:33 +0000 Subject: [PATCH 037/274] More logging --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index c5868ef10..97d5e6f0e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -119,7 +119,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) 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); + nv_log("NV3: PFIFO Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 08a2b48ff..a6561be9d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -50,7 +50,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) 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); + nv_log("NV3: Read byte from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); return val; } @@ -70,7 +70,7 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) uint16_t val = vram_16bit[addr]; // what - nv_log("NV3: Read word from ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + nv_log("NV3: Read word from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); return val; } @@ -90,7 +90,7 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) uint32_t val = vram_32bit[addr]; - nv_log("NV3: Read dword from ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + nv_log("NV3: Read dword from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); return val; } @@ -109,7 +109,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) 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); + nv_log("NV3: Write byte to RAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); } // Write 16-bit ramin @@ -127,7 +127,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) vram_16bit[addr] = val; - nv_log("NV3: Write word to ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + nv_log("NV3: Write word to RAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); } // Write 32-bit ramin @@ -145,5 +145,5 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) vram_32bit[addr] = val; - nv_log("NV3: Write dword to ramin addr=0x%08x (raw address=0x%08x)", addr, raw_addr); + nv_log("NV3: Write dword to RAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } From fda2c3a3fe85c8d2c93caa82a3493147457b58f9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 31 Dec 2024 22:34:55 +0000 Subject: [PATCH 038/274] note current state of drivers --- doc/nv3 driver init status.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/nv3 driver init status.txt b/doc/nv3 driver init status.txt index 025e781a2..d0d13bacd 100644 --- a/doc/nv3 driver init status.txt +++ b/doc/nv3 driver init status.txt @@ -106,3 +106,7 @@ NvFindAdapter -> RmPostNvDevice Success 17:32 27/11/2024 NvFindAdapter -> NVGetNVInfo Success 17:32 27/11/2024 NvFindAdapter -> NVMapFrameBuffer Success 17:32 27/11/2024 +00:00 30/12/2024: stateGr -> i2c_Write +22:31 31/12/2024: fifoService + +fix ptimer issue \ No newline at end of file From dee308f4b5b05294af7ff597986fb7f005237843 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 1 Jan 2025 02:32:34 +0000 Subject: [PATCH 039/274] fix compiler warning --- src/include/86box/nv/classes/vid_nv3_classes.h | 9 +++++++++ src/include/86box/nv/vid_nv3.h | 6 +++--- src/video/nv/nv3/nv3_core.c | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index e47b5eb71..b5d4ff44a 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -39,6 +39,15 @@ extern const char* nv3_class_names[]; +/* Object Class 0x01 (real hardware) + Beta factor +*/ +typedef struct nv_object_class_001 +{ + uint8_t reserved[0xFF]; // Required for NV_CLASS Core Functionality + // Put the rest of it here +} nv3_beta_factor_t; + /* Object Class 0x07 (real hardware) 0x1E (drivers) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a404df979..3e69b57fd 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -6,7 +6,8 @@ * * This file is part of the 86Box distribution. * - * JENSEN HUANG APPROVED !!!! + * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) + * Last updated 1 January 2025 (STILL WORKING ON IT) * * * @@ -15,8 +16,7 @@ * Copyright 2024-2025 Connor Hyde */ -// vid_nv3.h: NV3 Architecture Hardware Reference (open-source) -// Last updated 30 December 2024 + // The GPU base structure extern const device_config_t nv3_config[]; diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index cd1614e8e..e69244209 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -679,7 +679,7 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) val = (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_OVERFLOW] & ~0x10) | (val & 0x10); // set the register value... - uint8_t old_value = nv3->nvbase.svga.crtc[crtcreg]; + old_value = nv3->nvbase.svga.crtc[crtcreg]; nv3->nvbase.svga.crtc[crtcreg] = val; // ...now act on it From 23467b07b8afc98b2b92c028ba4b01dcf425432b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 2 Jan 2025 11:35:41 +0000 Subject: [PATCH 040/274] fix compiler warning --- src/video/nv/nv3/subsystems/nv3_pbus.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index cf5b698a1..61385b8f2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -175,8 +175,6 @@ uint8_t nv3_pbus_rma_read(uint16_t addr) // 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; From 61042299f306c8d5816ae32251eedece1a9db6a1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 2 Jan 2025 21:55:01 +0000 Subject: [PATCH 041/274] eh 2nd time i need to merge today --- doc/old nouveau wiki.txt | 9 +++++++++ src/video/nv/nv3/subsystems/nv3_pgraph.c | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 doc/old nouveau wiki.txt diff --git a/doc/old nouveau wiki.txt b/doc/old nouveau wiki.txt new file mode 100644 index 000000000..c875954fd --- /dev/null +++ b/doc/old nouveau wiki.txt @@ -0,0 +1,9 @@ +old nouveau wiki: +real VRAM address = VRAM_size - (ramin_address - (ramin_address % reversal_unit_size)) - reversal_unit_size + (ramin_address % reversal_unit_size) + +nv3=16 bytes + +0x400000 - ((0x100000) - (0x100000 % 16)) - 16 + (0x100000 % 16) = 2ffff0 + + + \ 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 index 1d44a3f94..32f87ee30 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -86,7 +86,8 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_TRAPPED_ADDRESS, "PGRAPH Trapped Address", NULL, NULL }, { NV3_PGRAPH_TRAPPED_DATA, "PGRAPH Trapped Data", NULL, NULL }, { NV3_PGRAPH_TRAPPED_INSTANCE, "PGRAPH Trapped Object Instance", NULL, NULL }, - + { NV3_PGRAPH_DMA_INTR_0, "PGRAPH DMA Interrupt Status (unimplemented)", NULL, NULL }, + { NV3_PGRAPH_DMA_INTR_EN_0, "PGRAPH DMA Interrupt Enable (unimplemented)", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; From ee0dfda9eef1f323c4eff35247f8bab6fb39a25c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 2 Jan 2025 22:01:23 +0000 Subject: [PATCH 042/274] Some refactoring for the SVGA redirection --- src/video/nv/nv3/nv3_core.c | 51 +++++++++++++------------------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e69244209..6114749a6 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -34,12 +34,23 @@ 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(); +bool nv3_is_svga_redirect_address(uint32_t addr); uint8_t nv3_svga_in(uint16_t addr, void* priv); void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); +// Determine if this address needs to be redirected to the SVGA subsystem. + +bool nv3_is_svga_redirect_address(uint32_t addr) +{ + return (addr >= NV3_PRMVIO_START + && addr <= NV3_PRMVIO_END + || addr == NV3_PRMCIO_CR_COLOR + || addr == NV3_PRMCIO_CRX_COLOR + || addr == NV3_PRMCIO_CR_MONO + || addr == NV3_PRMCIO_CRX_MONO); +} + // All MMIO regs are 32-bit i believe internally // so we have to do some munging to get this to read @@ -51,12 +62,7 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. addr &= 0xFFFFFF; - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -81,12 +87,7 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. addr &= 0xFFFFFF; - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -111,12 +112,7 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. addr &= 0xFFFFFF; - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -142,12 +138,7 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // This is weitek vga stuff // If we need to add more of these we can convert these to a switch statement - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -205,12 +196,7 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) addr &= 0xFFFFFF; // This is weitek vga stuff - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; @@ -1027,7 +1013,6 @@ void nv3_close(void* priv) ddc_close(nv3->nvbase.ddc); i2c_gpio_close(nv3->nvbase.i2c); - // Destroy the Rivatimers. (It doesn't matter if they are running.) rivatimer_destroy(nv3->nvbase.pixel_clock_timer); rivatimer_destroy(nv3->nvbase.memory_clock_timer); From f83153a2ee97fe0ff625d3ff0700c3e6f7958f23 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 3 Jan 2025 01:05:49 +0000 Subject: [PATCH 043/274] The first half of the completely insane object class system's structs. --- .../Memory map (RIVA 128).txt | 0 doc/{ => nvidia_notes}/NV_PFB_CONFIG_0.txt | 0 doc/{ => nvidia_notes}/PFIFO RAMHT RAMRO.txt | 0 doc/{ => nvidia_notes}/RIVA fansites.txt | 0 doc/{ => nvidia_notes}/Versions.txt | 0 doc/{ => nvidia_notes}/gpucompanies.txt | 0 .../nv3 driver init status.txt | 0 doc/{ => nvidia_notes}/nv3_object_classes.txt | 0 .../nv3_object_classes_driver.txt | 0 doc/{ => nvidia_notes}/nv3d3d_t.txt | 0 doc/{ => nvidia_notes}/old nouveau wiki.txt | 0 doc/nvidia_notes/rivatv_riva128.txt | 2238 +++++++++++++++++ .../86box/nv/classes/vid_nv3_classes.h | 498 +++- src/include/86box/nv/vid_nv3.h | 4 +- 14 files changed, 2702 insertions(+), 38 deletions(-) rename doc/{ => nvidia_notes}/Memory map (RIVA 128).txt (100%) rename doc/{ => nvidia_notes}/NV_PFB_CONFIG_0.txt (100%) rename doc/{ => nvidia_notes}/PFIFO RAMHT RAMRO.txt (100%) rename doc/{ => nvidia_notes}/RIVA fansites.txt (100%) rename doc/{ => nvidia_notes}/Versions.txt (100%) rename doc/{ => nvidia_notes}/gpucompanies.txt (100%) rename doc/{ => nvidia_notes}/nv3 driver init status.txt (100%) rename doc/{ => nvidia_notes}/nv3_object_classes.txt (100%) rename doc/{ => nvidia_notes}/nv3_object_classes_driver.txt (100%) rename doc/{ => nvidia_notes}/nv3d3d_t.txt (100%) rename doc/{ => nvidia_notes}/old nouveau wiki.txt (100%) create mode 100644 doc/nvidia_notes/rivatv_riva128.txt diff --git a/doc/Memory map (RIVA 128).txt b/doc/nvidia_notes/Memory map (RIVA 128).txt similarity index 100% rename from doc/Memory map (RIVA 128).txt rename to doc/nvidia_notes/Memory map (RIVA 128).txt diff --git a/doc/NV_PFB_CONFIG_0.txt b/doc/nvidia_notes/NV_PFB_CONFIG_0.txt similarity index 100% rename from doc/NV_PFB_CONFIG_0.txt rename to doc/nvidia_notes/NV_PFB_CONFIG_0.txt diff --git a/doc/PFIFO RAMHT RAMRO.txt b/doc/nvidia_notes/PFIFO RAMHT RAMRO.txt similarity index 100% rename from doc/PFIFO RAMHT RAMRO.txt rename to doc/nvidia_notes/PFIFO RAMHT RAMRO.txt diff --git a/doc/RIVA fansites.txt b/doc/nvidia_notes/RIVA fansites.txt similarity index 100% rename from doc/RIVA fansites.txt rename to doc/nvidia_notes/RIVA fansites.txt diff --git a/doc/Versions.txt b/doc/nvidia_notes/Versions.txt similarity index 100% rename from doc/Versions.txt rename to doc/nvidia_notes/Versions.txt diff --git a/doc/gpucompanies.txt b/doc/nvidia_notes/gpucompanies.txt similarity index 100% rename from doc/gpucompanies.txt rename to doc/nvidia_notes/gpucompanies.txt diff --git a/doc/nv3 driver init status.txt b/doc/nvidia_notes/nv3 driver init status.txt similarity index 100% rename from doc/nv3 driver init status.txt rename to doc/nvidia_notes/nv3 driver init status.txt diff --git a/doc/nv3_object_classes.txt b/doc/nvidia_notes/nv3_object_classes.txt similarity index 100% rename from doc/nv3_object_classes.txt rename to doc/nvidia_notes/nv3_object_classes.txt diff --git a/doc/nv3_object_classes_driver.txt b/doc/nvidia_notes/nv3_object_classes_driver.txt similarity index 100% rename from doc/nv3_object_classes_driver.txt rename to doc/nvidia_notes/nv3_object_classes_driver.txt diff --git a/doc/nv3d3d_t.txt b/doc/nvidia_notes/nv3d3d_t.txt similarity index 100% rename from doc/nv3d3d_t.txt rename to doc/nvidia_notes/nv3d3d_t.txt diff --git a/doc/old nouveau wiki.txt b/doc/nvidia_notes/old nouveau wiki.txt similarity index 100% rename from doc/old nouveau wiki.txt rename to doc/nvidia_notes/old nouveau wiki.txt diff --git a/doc/nvidia_notes/rivatv_riva128.txt b/doc/nvidia_notes/rivatv_riva128.txt new file mode 100644 index 000000000..780ad5edd --- /dev/null +++ b/doc/nvidia_notes/rivatv_riva128.txt @@ -0,0 +1,2238 @@ + + +Riva 128 documentation +====================== + +This document is based on kernel sources, XFree86 sources, open sources +released by nVidia and pure deduction. To make nVidia happy: + + /***************************************************************************\ +|* *| +|* Copyright (c) 1996-1998 NVIDIA, Corp. All rights reserved. *| +|* *| +|* NOTICE TO USER: The source code is copyrighted under U.S. and *| +|* international laws. NVIDIA, Corp. of Sunnyvale, California owns *| +|* the copyright and as design patents pending on the design and *| +|* interface of the NV chips. Users and possessors of this source *| +|* code are hereby granted a nonexclusive, royalty-free copyright *| +|* and design patent license to use this code in individual and *| +|* commercial software. *| +|* *| +|* Any use of this source code must include, in the user documenta- *| +|* tion and internal comments to the code, notices to the end user *| +|* as follows: *| +|* *| +|* Copyright (c) 1996-1998 NVIDIA, Corp. NVIDIA design patents *| +|* pending in the U.S. and foreign countries. *| +|* *| +|* NVIDIA, CORP. MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF *| +|* THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT *| +|* EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORP. DISCLAIMS *| +|* ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, INCLUDING ALL *| +|* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *| +|* PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA, CORP. BE LIABLE *| +|* FOR ANY SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, *| +|* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR *| +|* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER *| +|* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR *| +|* PERFORMANCE OF THIS SOURCE CODE. *| +|* *| + \***************************************************************************/ + +Feel free to send me corrections, updates, suggestions about this document to: +fero@drama.obuda.kando.hu or rivatv-devel@lists.sourceforge.net +Any kind of info, hint or idea may be helpful. +Please visit the rivatv homepage at: +http://fero.koli.kando.hu/rivatv/ + + + +Rendering +========= + +DMA vs PIO +---------- + +Theese two methods are used to program the hardware. DMA method provides +better performance but it is harder to implement. Almost all of the released +sources uses PIO method. The rest of this document describes the PIO method. + + +Objects +------- + +The Riva rendering engine is an object-oriented hardware. (See the nVidia +docs at https://www.nvidia.com/nv/nvarch.nsf/Home?OpenView -> Documents -> +NV Programmer's Reference Manual) Do not take it serious, it's simplified +for the hardware, it is not like C++. +Each object has a name. The name is 32 bits long and should be unique. + + +FIFO Channels +------------- + +There are 8 channels for submitting graphical objects to the rendering engine. +Each channel contains 8 subchannels. The rules how channels and subchannels +are working is not clear for me yet. Channel 0 is usable without extra coding. +Other channels can not be used until the 'context switching' code is done. +FIFO channels are located in the CTRL memory at 0x00800000-0x0087FFFF. Each +channel is 64 kbytes long, each subchannel is 8 kbytes long. Registers of +subchannels are variable: their function depends on the type of the submitted +object. Registers are write only and 32 bits long except the 'Free' register. + +Offset Name(s) Valid object types +------ ----------------------------- ------------------ +0000 Object all +0010 Free (2 bytes, read only!) all +0100 Synchronize t V s3 +0100 NoOperation BKPCLTRbISMr DG +0104 StopAlarm t +0104 Notify BKPCLTRbISMr DG +0104 StopImage V +0108 StopCursor V +010c StopDac V +0180 DmaNotifies tBKPCLTRbISMrVDGs3 +0184 ClipRectangle LTR +0184 ColorKey bIS +0184 DmaImage M V +0184 DmaTexture D +0184 Pattern G +0184 DmaSource s +0184 DmaSurfaces 3 +0188 Pattern LTR SM +0188 ClipRectangle bI D +0188 Rop G +0188 DmaDestin s +018c Rop LTR SM +018c Pattern bI +018c DmaLut V +018c Surfaces3D D +018c Beta1 G +0190 Beta1 LTR SM +0190 Rop bI +0190 Surfaces G +0194 Surfaces LTR SM +0194 Beta1 bI +0194 DmaCursor V +0198 Surfaces I +019c Surfaces b +02fc Operation LTRbIS G +02fc Get V +0300 Time0 t +0300 Beta1d31 B +0300 ColorFormat KP LTR ISM G +0300 Point C +0300 ControlPointIn b +0300 Rop r +0300 Image V +0300 Format s +0300 Pitch 3 +0304 Time1 t +0304 Color K LTR +0304 MonochromeFormat P G +0304 Size C +0304 ControlPointOut b +0304 Point I +0304 SizeIn S +0304 TextureOffset D +0304 Pitch s +0304 OffsetColor 3 +0300 Operation M +0308 AlarmNotify t +0308 MonochromeShape P +0308 Size b +0308 SizeOut I +0308 DxDu S +0308 ClipPoint M +0308 TextureFormat D +0308 OffsetSource s +0308 OffsetZeta 3 +030c SizeIn I +030c DyDv S +030c ClipSize M +030c TextureFilter D +030c OffsetDestin s +0310 MonochromeColor0 P +0310 TrianglePoint0 T +0310 ClipPoint S +0310 ImageOutPoint M +0310 FogColor D +0314 MonochromeColor1 P +0314 TrianglePoint1 T +0314 ClipSize S +0314 ImageOutSize M +0314 Control0 D +0318 MonochromePattern0 P +0318 TrianglePoint2 T +0318 Point12d4 S +0318 DxDu M +0318 Control1 D +031c MonochromePattern1 P +031c DyDv M +0320 Triangle32Point0X T +0324 Triangle32Point0Y T +0328 Triangle32Point1X T +032c Triangle32Point1Y T +0330 Triangle32Point2X T +0334 Triangle32Point2Y T +0340 Cursor V +0358 CursorPointOutA V +0380 Dac V +03a0 PixelClock V +03fc Color1A G +0400 Lin L +0400 Trimesh T +0400 Rectangle R +0400 Color IS +0400 ImageInSize M +0400 UnclippedRectangle G +0404 ImageInFormat M +0408 ImageInOffset M +040c ImageInPoint M +0480 Lin32 L +0480 Trimesh32 T +0500 PolyLin L +0500 ColorTriangle T +0580 PolyLin32 L +0580 ColorTrimesh T +0600 ColorPolyLin L +07f4 ClipPoint0B G +07f8 ClipPoint0B G +07fc Color1B G +0800 ClippedRectangle G +0bec ClipPoint0C G +0bf0 ClipPoint1C G +0bf4 Color1C G +0bf8 SizeC G +0bfc PointC G +0c00 MonochromeColor1C G +0fe8 ClipPoint0D G +0fec ClipPoint1D G +0ff0 Color1D G +0ff4 SizeInD G +0ff8 SizeOutD G +0ffc PointD G +1000 Tlvertex D +1000 MonochromeColor1D G +13e4 ClipPoint0E G +13e8 ClipPoint1E G +13ec Color0E G +13f0 Color1E G +13f4 SizeInE G +13f8 SizeOutE G +13fc PointE G +1400 MonochromeColor01E G + +t=timer +B=beta1 (0x41) +K=color key (0x43) +P=pattern (0x46) +C=clip rectangle (0x45) +L=solid line (0x4a) +T=solid triangle (0x4b) +R=solid rectangle (0x47) +b=image blit (0x50) +I=image from CPU (0x51) +S=stretched image from CPU (0x55) +M=scaled image from memory (0x4e) +r=raster operation (0x42) +V=video lut cursor dac +D=DX3 textured triangle (0x57) +G=GDI rectangle text (0x4c?) +s=Surfaces 2D +3=Surfaces 3D + +Contexts +-------- + +Context is a 32 bit record of channel id, object type and pointer to the +object. + +Bits 24-30: channel id +Bit 23: rendering + 0x0 other object (DMA?) + 0x1 renderable object +Bits 16-22: object type + 0x41 beta1 + 0x42 raster operation + 0x43 color key + 0x44 plane + 0x45 clipping rectangle + 0x46 pattern + 0x47 solid rectangle + 0x49 unknown line + 0x4A solid line + 0x4B solid triangle + 0x4C GDI rectangle + 0x4E scaled image from memory + 0x50 blit + 0x51 image from CPU + 0x52 bitmap + 0x55 streched image from CPU + 0x57 DX3 textured triangle + 0x?? Surfaces 2D + 0x?? Surfaces 3D + 0x?? Timer + 0x?? Video lut cursor dac + and many more... +Bits 0-15: offset in instance memory (RAMIN) / 16 + +Each graphic object has a context. Context determines the type (triangle, +raster operation, bitmap, etc) of the object. Don't ask me why this thingy +is called context: I really don't know. + + +Hash table +---------- + +The hash table (HT) is located in the instance memory area (RAMIN). +See RAMHT register in section FIFO registers. +The HT contains 8 byte length records of object names and contexts. Each +record is placed in the table by it's hash key. The key is computed as +follows: each byte of the object's name and the channel id are XOR-ed +together, and this value is multiplied by the hash depth. More explanation +on hash tables can be found in database books. +The purpose of HT is to provide connection between object names and contexts +for the graphic hardware. But it may be useful for your software driver too. + + +FIFO context table +------------------ + +The FIFO context table (FC) is located in the instance memory area (RAMIN). +See RAMFC register in section FIFO registers. +FC contains the context values for each subchannel. E.g. if FC begins +at 0x1C00: +0x1C00 Context of object in channel 0, subchannel 0 +0x1C04 Context of object in channel 0, subchannel 1 +0x1C08 Context of object in channel 0, subchannel 2 +... +0x1C1C Context of object in channel 0, subchannel 7 +0x1C20 Context of object in channel 1, subchannel 0 +0x1C24 Context of object in channel 1, subchannel 1 +... +0x1CFC Context of object in channel 7, subchannel 7 + + +Example: How to render a solid rectangle? +----------------------------------------- + +- Choose a name for your rectangle. E.g.: 0x01020304 +- Select an unused area in the instance memory. Let it be 0x00c04000 +- Calculate the context: + Channel = 0x00 + Render object = 1 + Object type = 0x47 (rectangle) + Offset in intance memory = 0x4000 / 16 = 0x0400 + Context = 0x00c70400 +- Place the name - context pair in the hash table (HT begins at 0x00c00000): + Hash key = 01 xor 02 xor 03 xor 04 xor 00 = 0x04 + (each byte of the name) (channel id) + *(0x00c00000 + 0x04 * 16) = 0x00000001; // name + *(0x00c00000 + 0x04 * 16) + 4 = 0x00c70400; // context +- Fill in the instace ram: + *(0x00c04000) = 0x00100000; // 15bpp (TODO: decode this magic) + *(0x00c04004) = 0x00000000; // not used yet + *(0x00c04008) = 0x00000000; // not used yet + *(0x00c0400c) = 0x00000000; // not used yet +- At this point the FIFO channel is ready for submitting rectangles. +- Submit the first rectangle: + write the object's name to channel 0 (subchannel 0): + *(0x00800000) = 0x00000001; +- At this point subchannel 0 becomes a rectangle. +- Submit the attributes of the rectangle: + write x,y coordinates: + (0x00800400) = 0x01000200; // x=0x100 y=0x200 + size: + (0x00800404) = 0x00100020; // width=0x10 height=0x20 +- After size is submitted, our rectangle appears on the screen. +- Subsequent rectangles may be submitted by re-sending new attributes. + + +Speculation: How to make video in working? +------------------------------------------ + +V4L stuff and hardware rendered rectangles have some similarities: +- both have color keys +- both have clipping rectangles +- both are stretchable +I suppose video in feature is implemented by rendered objects. A chroma-keyed +and clipped rectangle with 'live texture' is blitted on the screen. +Thats all. :) + + + +Memory layout +============= + +Three memory regions are accessible via the PCI (or AGP) interface: +- CTRL region (PCI base 0) +- FB region (PCI base 1) +- BIOS ROM (PCI base 2) + + +CTRL region +----------- + +The CTRL region contains memory mapped IO ports. Most of them are 32-bit +registers. The registers are grouped by functionality: + +- MC: master control (0x00000000-0x00000FFF) +- BUS: (0x00001000-0x00001FFF) +- FIFO: for DMA and PIO stuff (0x00002000-0x00003FFF) +- TIMER: built-in timer (0x00009000-0x00009FFF) +- VIO: 8 bit SVGA (MISC, GRAPH, SEQ) registers (0x000C0000-0x00C00FFF) +- FB: framebuffer properties (0x00100000-0x00100FFF) +- EXTDEV: external devices (0x00101000-0x00101FFF) +- GRAPH: (0x00400000-0x00401FFF) +- PCIO: 8 bit SVGA (CRTC, ATTR) registers (0x00601000-0x00601FFF) +- RAMDAC: RAMDAC timings (0x00680000-0x00680FFF) +- PDIO: 8 bit registers (0x00681000-0x00681FFF) +- FIFO channels: (0x00800000-0x0087FFFF) +(List in not complete.) + + +FB region +--------- + +This region contains the video memory and the instance memory. +Video memory begins at offset 0x00000000. Instance memory (RAMIN) +begins at offset 0x00C00000. (Note: Riva TNT (and higher) cards have +their instance memory in the CTRL region.) + + + +Master control +============== + +The MC registers begin at 0x00000000 in the CTRL region. They control the +master IRQ. + +Interrupt 0 register +-------------------- +Name: INTR_0 +Offset: 0x00000100 +4 bytes, read-write + +Bit 0: PAUDIO (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 4: PMEDIA (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 8: PFIFO (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 12: PGRAPH0 (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 13: PGRAPH1 (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 16: PVIDEO (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 20: PTIMER (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 24: PFB (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 28: PBUS (-V, read-only) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + +Bit 31: SOFTWARE (IV, read-write) + NOT_PENDING 0x0 (default, read-write) + PENDING 0x1 (read-write) + +Interrupt 0 enable register +--------------------------- +Name: INTR_EN_0 +Offset: 0x00000140 +4 bytes, read-write + +Bits 0-1: INTA (IV, read-write) + DISABLED 0x0 (default, read-write) + HARDWARE 0x1 (read-write) + SOFTWARE 0x2 (read-write) + +Enable (what?) register +----------------------- +Name: ENABLE +Offset: 0x00000200 +4 bytes, read-write + + + +Bus +=== + +The BUS registers begin at 0x00001000 in the CTRL region. They are +undiscovered. + +Debug 0 register +---------------- +Name: DEBUG_0 +Offset: 0x00001080 +4 bytes, read-write + +Bit 0: MODE (IV, read-write) + MODE_DISABLED 0x0 (default, read-write) + MODE_ENABLED 0x1 (read-write) + +Bit 4: DESKEWER (IV, read-write) + ENABLED 0x0 (default, read-write) + +Bits 8-11: FBIO_SCLK_DELAY (IV, read-write) + 0x0 (default, read-write) + +Bits 12-15: FBIO_FBCLK_DELAY (IV, read-write) + 0x3 (default, read-write) + +Debug 1 register +---------------- +Name: DEBUG_1 +Offset: 0x00001084 +4 bytes, read-write + +Bit 0: PCIM_THROTTLE (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 1: PCIM_CMD (IV, read-write) + SIZE_BASED 0x0 (default, read-write) + MRL_ONLY 0x1 (read-write) + +Bit 2: PCIM_AGP (IV, read-write) + IS_AGP 0x0 (default, read-write) + IS_PCI 0x1 (read-write) + +Bits 3-4: AGPM_CMD (IV, read-write) + HP_ON_1ST 0x0 (read-write) + LP_ONLY 0x1 (default, read-write) + HP_ONLY 0x2 (read-write) + +Bit 5: PCIS_WRITE (IV, read-write) + 0_CYCLE 0x0 (read-write) + 1_CYCLE 0x1 (default, read-write) + +Bit 6: PCIS_2_1 (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 7: PCIS_RETRY (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 8: PCIS_RD_BURST (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 9: PCIS_WR_BURST (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 10: PCIS_EARLY_RTY (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 11: PCIS_RMAIO (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 12: PCIS_CPUQ (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +Bit 13: DPSH_PIPE (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 14: SPARE1 (IV, read-write) + ZERO 0x0 (default, read-write) + ONE 0x1 (read-write) + +Bit 15: SPARE2 (IV, read-write) + ZERO 0x0 (default, read-write) + ONE 0x1 (read-write) + +Bit 16: SPARE3 (IV, read-write) + ZERO 0x0 (default, read-write) + ONE 0x1 (read-write) + + + +FIFO +==== + +The BUS registers begin at 0x00002000 in the CTRL region. + +Interrupt 0 register +-------------------- + +Name: INTR_0 +Offset: 0x00002100 +4 bytes, read-write + +Bit 0: CACHE_ERROR (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 4: RUNOUT (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 8: RUNOUT_OVERFLOW (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 12: DMA_PUSHER (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 16: DMA_PTE (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Interrupt 0 enable register +--------------------------- + +Name: INTR_EN_0 +Offset: 0x00002140 +4 bytes, read-write + +Bit 0: CACHE_ERROR (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 4: RUNOUT (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 8: RUNOUT_OVERFLOW (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Config 0 register +----------------- + +Name: CONFIG_0 +Offset: 0x00002200 +4 bytes, read-write + +Hash table register +------------------- + +Hardware hash table offset (in instance memory) and size. + +Name: RAMHT +Offset: 0x00002210 +4 bytes, read-write + +Bits 12-15: BASE_ADDRESS (XV, read-write) + +Bits 16-17: SIZE (XV, read-write) + 4K 0x0 (default, read-write) + 8K 0x1 (read-write) + 16K 0x2 (read-write) + 32K 0x3 (read-write) + +FIFO context table offset +------------------------- + +Name: RAMFC +Offset: 0x00002214 +4 bytes, read-write + +Bits 9-15: BASE_ADDRESS (XV, read-write) + +Runout table register +--------------------- + +FIFO runout table offset and size. + +Name: RAMRO +Offset: 0x00002218 +4 bytes, read-write + +Bits 9-15: BASE_ADDRESS (XV, read-write) + +Bit 16: SIZE (XV, read-write) + 512 0x0 (default, read-write) + 8K 0x1 (read-write) + +Runout status register +---------------------- + +Name: RUNOUT_STATUS +Offset: 0x00002400 +4 bytes, read-only + +Runout put register +------------------- + +Index in runout table. + +Name: RUNOUT_PUT +Offset: 0x00002410 +4 bytes, read-write + +Runout get register +------------------- + +Index in runout table. + +Name: RUNOUT_GET +Offset: 0x00002420 +4 bytes, read-write + +Caches register +--------------- + +Name: CACHES +Offset: 0x00002500 +4 bytes, read-write + +Bit 0: REASSIGN (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Cache 0 push 0 register +----------------------- + +Name: CACHE0_PUSH0 +Offset: 0x00003000 +4 bytes, read-write + +Bit 0: ACCESS (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Cache 0 push 1 register +----------------------- + +Name: CACHE0_PUSH1 +Offset: 0x00003004 +4 bytes, read-write + +Bits 0-6: CHID (XU, read-write) + +Cache 0 pull 0 register +----------------------- + +Name: CACHE0_PULL0 +Offset: 0x00003040 +4 bytes, read-write + +Bit 0: ACCESS (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Cache 1 push 0 register +----------------------- + +Name: CACHE1_PUSH0 +Offset: 0x00003200 +4 bytes, read-write + +Bit 0: ACCESS (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Cache 1 push 1 register +----------------------- + +Name: CACHE1_PUSH1 +Offset: 0x00003204 +4 bytes, read-write + +Bits 0-6: CHID (XU, read-write) + +Cache 1 put register +-------------------- + +Name: CACHE1_PUT +Offset: 0x00003210 +4 bytes, read-write + +Bits 2-6: ADDRESS (XU, read-write) + +Cache 1 DMA 0 register +----------------------- + +Name: CACHE1_DMA0 +Offset: 0x00003220 +4 bytes, read-write + +Cache 1 DMA 1 register +----------------------- + +Name: CACHE1_DMA1 +Offset: 0x00003224 +4 bytes, read-write + +Cache 1 DMA 2 register +----------------------- + +Name: CACHE1_DMA2 +Offset: 0x00003228 +4 bytes, read-write + +Cache 1 pull 0 register +----------------------- + +Name: CACHE1_PULL0 +Offset: 0x00003240 +4 bytes, read-write + +Bit 0: ACCESS (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Cache 1 pull 1 register +----------------------- + +Name: CACHE1_PULL1 +Offset: 0x00003250 +4 bytes, read-write + +Bit 4: CTX (XV, read-write) + CLEAN 0x0 (read-write) + DIRTY 0x1 (read-write) + +Cache 1 get register +-------------------- + +Name: CACHE1_GET +Offset: 0x00003270 +4 bytes, read-write + +Bits 2-6: ADDRESS (XU, read-write) + +Cache 1 context registers +------------------------- + +8 context registers for each channel (I guess). + +Name: CACHE1_CTX +Offset: 0x00003280+i*16 (0 <= i < 8) +8*16(4?) bytes, read-write + + + +Framebuffer properties +====================== + +The FB registers begin at 0x00100000 in the CTRL region. + +Boot 0 register +--------------- + +Name: BOOT_0 +Offset: 0x00100000 +4 bytes, read-write + +Bits 0-1: RAM_AMOUNT (IV, read-write) + 1MB 0x0 (read-write) + 2MB 0x1 (read-write) + 4MB 0x2 (read-write) + 8MB 0x0 (read-write) + UNDEFINED 0x3 (read-write) + DEFAULT 0x2 (default, read-write) + +Bit 2: RAM_WIDTH_128 (-V, read-write) + OFF 0x0 (read-write) + ON 0x1 (read-write) + +Bit 3: RAM_BANKS (IV, read-write) + 2BANK 0x0 (default, read-write) + 4BANK 0x1 (read-write) + +Bit 4: RAMDATA_TWIDDLE (IV, read-write) + OFF 0x0 (default, read-write) + ON 0x1 (read-write) + +Bit 5: RAM_AMOUNT_EXTENSION (IV, read-write) + OFF 0x0 (default, read-write) + 8MB 0x1 (read-write) + +Delay 1 register +---------------- + +Name: DELAY_1 +Offset: 0x00100044 +4 bytes, read-write + +Bits 0-1: WRITE_ENABLE_RISE (IU, read-write) + WRITE_ENABLE_RISE_0 0x0 (default, read-write) + +Bits 4-5: WRITE_ENABLE_FALL (IU, read-write) + WRITE_ENABLE_FALL_0 0x0 (default, read-write) + +Bits 8-9: CAS_ENABLE_RISE (IU, read-write) + CAS_ENABLE_RISE_0 0x0 (default, read-write) + +Bits 12-13: CAS_ENABLE_FALL (IU, read-write) + CAS_ENABLE_FALL_0 0x0 (default, read-write) + +Bits 16-17: OUTPUT_DATA (IU, read-write) + OUTPUT_DATA_0 0x0 (default, read-write) + +Bits 20-21: RAS_ENABLE (IU, read-write) + RAS_ENABLE_0 0x0 (default, read-write) + +Debug 0 register +---------------- + +Name: DEBUG_0 +Offset: 0x00100080 +4 bytes, read-write + +Bit 4: REFRESH (IV, read-write) + ENABLED 0x0 (default, read-write) + DISABLED 0x1 (read-write) + +Green 0 register +---------------- + +Name: GREEN_0 +Offset: 0x001000C0 +4 bytes, read-write + +Bits 0-1: LEVEL (IV, read-write) + VIDEO_ENABLED 0x0 (read-write) + VIDEO_DISABLED 0x1 (read-write) + TIMING_DISABLED 0x2 (read-write) + MEMORY_DISABLED 0x3 (default, read-write) + +Config 0 register +----------------- + +Name: CONFIG_0 +Offset: 0x00100200 +4 bytes, read-write + +Bits 0-5: RESOLUTION (IV, read-write) + 320_PIXELS 0x0a (read-write) + 400_PIXELS 0x0d (read-write) + 480_PIXELS 0x0f (read-write) + 512_PIXELS 0x10 (read-write) + 640_PIXELS 0x14 (read-write) + 800_PIXELS 0x19 (read-write) + 960_PIXELS 0x1e (read-write) + 1024_PIXELS 0x20 (read-write) + 1152_PIXELS 0x24 (read-write) + 1280_PIXELS 0x28 (read-write) + 1600_PIXELS 0x32 (read-write) + DEFAULT 0x14 (default, read-write) + +Bits 8-9: PIXEL_DEPTH (IV, read-write) + 8_BITS 0x1 (read-write) + 16_BITS 0x2 (read-write) + 32_BITS 0x3 (read-write) + DEFAULT 0x1 (default, read-write) + +Bit 12: TILING (IV, read-write) + ENABLED 0x0 (read-write) + DISABLED 0x1 (default, read-write) + + +Bits 13-23: TILING_DEBUG (IV, read-write) + DISABLED 0x0 (read-write) + +RTL(?) register +--------------- + +Name: RTL +Offset: 0x00100??? +4 bytes, read-write + +Bits 0-1: S (IU, read-write) + DEFAULT 0x2 (default, read-write) + +Bits 4-5: V (IU, read-write) + DEFAULT 0x2 (default, read-write) + +Bits 8-9: M (IU, read-write) + DEFAULT 0x2 (default, read-write) + +Bits 12-13: H (IU, read-write) + DEFAULT 0x1 (default, read-write) + +Bits 16-17: A (IU, read-write) + DEFAULT 0x1 (default, read-write) + +Bits 20-21: G (IU, read-write) + DEFAULT 0x1 (default, read-write) + +Bit 24: ARB_GR_HI_PRIOR (IU, read-write) + DEFAULT 0x0 (default, read-write) + +Bit 28: ARB_MEDIA_HI_PRIOR (IU, read-write) + DEFAULT 0x0 (default, read-write) + + + +External devices +================ + +The EXTDEV registers begin at 0x00101000 in the CTRL region. + +Boot 0 register +--------------- + +Name: BOOT_0 +Offset: 0x00101000 +4 bytes, read-write + +Bit 2: STRAP_RAM_TYPE (XV, read-write) + SGRAM_8MBIT 0x1 (read-write) + SGRAM_16MBIT 0x0 (read-write) + +Bit 11: STRAP_OVERWRITE (IV, read-write) + ENABLED 0x1 (read-write) + +Bit 4: STRAP_RAM_WIDTH (XV, read-write) + 64 0x0 (read-write) + 128 0x1 (read-write) + + + +GRAPH +===== + +The GRAPH registers begin at 0x00400000 in the CTRL region. + +Debug 0 register +---------------- + +Name: DEBUG_0 +Offset: 0x00400080 +4 bytes, read-write + +Bit 0: STATE (-V, clear(check?)-write) + NORMAL 0x0 (clear(check?)-write) + RESET 0x1 (write-only) + +Bit 4: BULK_READS (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 20: WRITE_ONLY_ROPS_2D (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 24: DRAWDIR_AUTO (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Debug 1 register +---------------- + +Name: DEBUG_1 +Offset: 0x00400084 +4 bytes, read-write + +Bit 0: VOLATILE_RESET (IV, read-write) + NOT_LAST 0x0 (default, read-write) + LAST 0x1 (read-write) + +Bit 16: INSTANCE (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 20: CTX (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Debug 2 register +---------------- + +Name: DEBUG_2 +Offset: 0x00400088 +4 bytes, read-write + +Bit 0: AVOID_RMW_BLEND (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 8: DPWR_FIFO (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Bit 28: VOLATILE_RESET (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Debug 3 register +---------------- + +Name: DEBUG_3 +Offset: 0x0040008C +4 bytes, read-write + +Bit 24: HONOR_ALPHA (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Interrupt 0 register +-------------------- + +Name: INTR_0 +Offset: 0x00400100 +4 bytes, read-write + +Bit 0: RESERVED (-V, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 4: CONTEXT_SWITCH (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 8: VBLANK (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 12: RANGE (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 16: METHOD_COUNT (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 20: FORMAT (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 24: COMPLEX_CLIP (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 28: NOTIFY (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Interrupt 1 register +-------------------- + +Name: INTR_1 +Offset: 0x00400104 +4 bytes, read-write + +Bit 0: METHOD (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 4: DATA (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 12: DOUBLE_NOTIFY (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 16: CTXSW_NOTIFY (IV, read-write) + NOT_PENDING 0x0 (default, read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Interrupt 0 enable register +--------------------------- + +Name: INTR_EN_0 +Offset: 0x00400140 +4 bytes, read-write + +Interrupt 1 enable register +--------------------------- + +Name: INTR_EN_1 +Offset: 0x00400144 +4 bytes, read-write + +Context switch register +----------------------- + +Name: CTX_SWITCH +Offset: 0x00400180 +4 bytes, read-write + +Context control register +------------------------ + +Name: CTX_CONTROL +Offset: 0x00400190 +4 bytes, read-write + +Bits 0-1: MINIMUM_TIME (IV, read-write) + 33US 0x0 (default, read-write) + 262US 0x1 (read-write) + 2MS 0x2 (read-write) + 17MS 0x3 (read-write) + +Bit 8: TIME (IV, read-write) + EXPIRED 0x0 (default, read-write) + NOT_EXPIRED 0x1 (read-write) + +Bit 16: CHID (IV, read-write) + INVALID 0x0 (default, read-write) + VALID 0x1 (read-write) + +Bit 20: SWITCH (-V, read-only) + UNAVAILABLE 0x0 (read-only) + AVAILABLE 0x1 (read-only) + +Bit 24: SWITCHING (IV, read-write) + IDLE 0x0 (default, read-write) + BUSY 0x1 (read-write) + +Bit 28: DEVICE (IV, read-write) + DISABLED 0x0 (default, read-write) + ENABLED 0x1 (read-write) + +Context user register +--------------------- + +Name: CTX_USER +Offset: 0x00400194 +4 bytes, read-write + +Context cache registers +----------------------- + +8 context registers for each channel (I guess). + +Name: CTX_CACHE +Offset: 0x004001a0+i*4 (0 <= i 8) +8*4 bytes, read-write + +Absolute X RAM registers +------------------------ + +Name: ABS_X_RAM +Offset: 0x00400400+i*4 (0 <= i < 32) +32*4 bytes, read-write + +Absolute Y RAM registers +------------------------ + +Name: ABS_Y_RAM +Offset: 0x00400480+i*4 (0 <= i < 32) +32*4 bytes, read-write + +X misc register +--------------- + +Name: X_MISC +Offset: 0x00400500 +4 bytes, read-write + +Y misc register +--------------- + +Name: Y_MISC +Offset: 0x00400504 +4 bytes, read-write + +Exceptions register +------------------- + +Name: EXCEPTIONS +Offset: 0x00400508 +4 bytes, read-write + +Source color register +--------------------- + +Name: SOURCE_COLOR +Offset: 0x0040050C +4 bytes, read-write + +XY logic misc 0 register +------------------------ + +Name: XY_LOGIC_MISC0 +Offset: 0x00400514 +4 bytes, read-write + +XY logic misc 1 register +------------------------ + +Name: XY_LOGIC_MISC1 +Offset: 0x00400518 +4 bytes, read-write + + DVDY_VALUE 0x0 (default, read-write) (???) + +XY logic misc 2 register +------------------------ + +Name: XY_LOGIC_MISC2 +Offset: 0x0040051C +4 bytes, read-write + +XY logic misc 3 register +------------------------ + +Name: XY_LOGIC_MISC3 +Offset: 0x00400520 +4 bytes, read-write + +Clip X 0 register +----------------- + +Name: CLIPX_0 +Offset: 0x00400524 +4 bytes, read-write + +Clip X 1 register +----------------- + +Name: CLIPX_1 +Offset: 0x00400528 +4 bytes, read-write + +Clip Y 0 register +----------------- + +Name: CLIPY_0 +Offset: 0x0040052c +4 bytes, read-write + +Clip Y 1 register +----------------- + +Name: CLIPY_1 +Offset: 0x00400530 +4 bytes, read-write + +Absolute iclip X max. register +------------------------------ + +Name: ABS_ICLIP_XMAX +Offset: 0x00400534 +4 bytes, read-write + +Absolute iclip Y max. register +------------------------------ + +Name: ABS_ICLIP_YMAX +Offset: 0x00400538 +4 bytes, read-write + +Absolute uclip X min. register +------------------------------ + +Name: ABS_UCLIP_XMIN +Offset: 0x0040053C +4 bytes, read-write + +Absolute uclip Y min. register +------------------------------ + +Name: ABS_UCLIP_YMIN +Offset: 0x00400540 +4 bytes, read-write + +Absolute uclip X max. register +------------------------------ + +Name: ABS_UCLIP_XMAX +Offset: 0x00400544 +4 bytes, read-write + +Absolute uclip Y max. register +------------------------------ + +Name: ABS_UCLIP_YMAX +Offset: 0x00400548 +4 bytes, read-write + +Absolute uclipa X min. register +------------------------------ + +Name: ABS_UCLIPA_XMIN +Offset: 0x00400560 +4 bytes, read-write + +Absolute uclipa Y min. register +------------------------------ + +Name: ABS_UCLIPA_YMIN +Offset: 0x00400564 +4 bytes, read-write + +Absolute uclipa X max. register +------------------------------ + +Name: ABS_UCLIPA_XMAX +Offset: 0x00400568 +4 bytes, read-write + +Absolute uclipa Y max. register +------------------------------ + +Name: ABS_UCLIPA_YMAX +Offset: 0x0040056C +4 bytes, read-write + +Source canvas min. register +--------------------------- + +Name: SRC_CANVAS_MIN +Offset: 0x00400550 +4 bytes, read-write + +Source canvas max. register +--------------------------- + +Name: SRC_CANVAS_MAX +Offset: 0x00400554 +4 bytes, read-write + +Destination canvas min. register +-------------------------------- + +Name: DST_CANVAS_MIN +Offset: 0x00400558 +4 bytes, read-write + +Destination canvas max. register +-------------------------------- + +Name: DST_CANVAS_MAX +Offset: 0x0040055C +4 bytes, read-write + +Pattern color 0 0 register +-------------------------- + +Name: PATT_COLOR0_0 +Offset: 0x00400600 +4 bytes, read-write + +Pattern color 0 1 register +-------------------------- + +Name: PATT_COLOR0_1 +Offset: 0x00400604 +4 bytes, read-write + +Pattern color 1 0 register +-------------------------- + +Name: PATT_COLOR1_0 +Offset: 0x00400608 +4 bytes, read-write + +Pattern color 1 1 register +-------------------------- + +Name: PATT_COLOR1_1 +Offset: 0x0040060C +4 bytes, read-write + +Pattern registers +----------------- + +Name: PATTERN +Offset: 0x00400610+i*4 (0 <=i < 2) +2*4 bytes, read-write + +Pattern shape register +---------------------- + +Name: PATTERN_SHAPE +Offset: 0x00400618 +4 bytes, read-write + +Bits 0-1: VALUE (XV, read-write) + 8X8 0x0 (read-write) + 64X1 0x1 (read-write) + 1X64 0x2 (read-write) + +Monochrome color 0 register +--------------------------- + +Name: MONO_COLOR0 +Offset: 0x0040061C +4 bytes, read-write + +Raster operation register +------------------------- + +Name: ROP3 +Offset: 0x00400624 +4 bytes, read-write + +Plane mask register +------------------- + +Name: PLANE_MASK +Offset: 0x00400628 +4 bytes, read-write + +Chroma register +--------------- + +Name: CHROMA +Offset: 0x0040062C +4 bytes, read-write + +B(?) offset 0 register +---------------------- + +Name: BOFFSET0 +Offset: 0x00400630 +4 bytes, read-write + +B(?) offset 1 register +---------------------- + +Name: BOFFSET1 +Offset: 0x00400634 +4 bytes, read-write + +B(?) offset 2 register +---------------------- + +Name: BOFFSET2 +Offset: 0x00400638 +4 bytes, read-write + +B(?) offset 3 register +---------------------- + +Name: BOFFSET3 +Offset: 0x0040063C +4 bytes, read-write + +Beta register +------------- + +Name: BETA +Offset: 0x00400640 +4 bytes, read-write + +Control out register +-------------------- + +Name: CONTROL_OUT +Offset: 0x00400644 +4 bytes, read-write + +B(?) pitch 0 register +--------------------- + +Name: BPITCH0 +Offset: 0x00400650 +4 bytes, read-write + +B(?) pitch 1 register +--------------------- + +Name: BPITCH1 +Offset: 0x00400654 +4 bytes, read-write + +B(?) pitch 2 register +--------------------- + +Name: BPITCH2 +Offset: 0x00400658 +4 bytes, read-write + +B(?) pitch 3 register +--------------------- + +Name: BPITCH3 +Offset: 0x0040065C +4 bytes, read-write + +DMA register +------------ + +Name: DMA +Offset: 0x00400680 +4 bytes, read-write + +Notify register +--------------- + +Name: NOTIFY +Offset: 0x00400684 +4 bytes, read-write + +Bits 0-15: INST_MEM_LOC ??? + +Instance register +----------------- + +Name: INSTANCE +Offset: 0x00400688 +4 bytes, read-write + +Memory format register +---------------------- + +Name: MEMFMT +Offset: 0x0040068C +4 bytes, read-write + +Clip 0 min. register +-------------------- + +Name: CLIP0_MIN +Offset: 0x00400690 +4 bytes, read-write + +Clip 0 max. register +-------------------- + +Name: CLIP0_MAX +Offset: 0x00400694 +4 bytes, read-write + +Clip 1 min. register +-------------------- + +Name: CLIP1_MIN +Offset: 0x00400698 +4 bytes, read-write + +Clip 1 max. register +-------------------- + +Name: CLIP1_MAX +Offset: 0x0040069C +4 bytes, read-write + +Clip misc register +------------------ + +Name: CLIP_MISC +Offset: 0x004006A0 +4 bytes, read-write + +FIFO register +------------- + +Name: FIFO +Offset: 0x004006A4 +4 bytes, read-write + +Bit 0: ACCESS (IV, read-write) + DISABLED 0x0 (read-write) + ENABLED 0x1 (default, read-write) + +B(?) pixel register +------------------- + +Name: BPIXEL +Offset: 0x004006A8 +4 bytes, read-write + +Bits 0-1: DEPTH0_FMT (XV, read-write) + Y16_BITS 0x0 (read-write) + BITS_8 0x1 (read-write) + BITS_16 0x2 (read-write) + BITS_32 0x3 (read-write) + +Bit 2: DEPTH0 (XV, read-write) + NOT_VALID 0x0 (read-write) + VALID 0x1 (read-write) + +Bits 4-5: DEPTH1_FMT (XV, read-write) + Y16_BITS 0x0 (read-write) + BITS_8 0x1 (read-write) + BITS_16 0x2 (read-write) + BITS_32 0x3 (read-write) + +Bit 6: DEPTH1 (XV, read-write) + NOT_VALID 0x0 (read-write) + VALID 0x1 (read-write) + +Bits 8-9: DEPTH2_FMT (XV, read-write) + Y16_BITS 0x0 (read-write) + BITS_8 0x1 (read-write) + BITS_16 0x2 (read-write) + BITS_32 0x3 (read-write) + +Bit 10: DEPTH2 (XV, read-write) + NOT_VALID 0x0 (read-write) + VALID 0x1 (read-write) + +Bits 12-13: DEPTH3_FMT (XV, read-write) + Y16_BITS 0x0 (read-write) + BITS_8 0x1 (read-write) + BITS_16 0x2 (read-write) + BITS_32 0x3 (read-write) + +Bit 14: DEPTH3 (XV, read-write) + NOT_VALID 0x0 (read-write) + VALID 0x1 (read-write) + +Status register +--------------- + +Name: STATUS +Offset: 0x004006B0 +4 bytes, read-only + +DMA interrupt 0 register +------------------------ + +Name: DMA_INTR_0 +Offset: 0x00401100 +4 bytes, read-write + +Bit 0: INSTANCE (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 4: PRESENT (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 8: PROTECTION (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 12: LINEAR (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +Bit 16: NOTIFY (XV, read-write) + NOT_PENDING 0x0 (read-only) + PENDING 0x1 (read-only) + RESET 0x1 (write-only) + +DMA interrupt 0 enable register +------------------------------- + +Name: DMA_INTR_EN_0 +Offset: 0x00401140 +4 bytes, read-write + +DMA control register +-------------------- + +Name: DMA_CONTROL +Offset: 0x00401210 +4 bytes, read-write + + + +RAMDAC +====== + +The RAMDAC registers begin at 0x00680000 in the CTRL region. + +Graphic cursor starting position register +----------------------------------------- + +Name: GRCURSOR_START_POS +Offset: 0x00680300 +4 bytes, read-write + +Bits 0-11: X (XS, read-write) + +Bits 16-27: Y (XS, read-write) + +MPLL coeff register +------------------- + +Name: MPLL_COEFF +Offset: 0x00680504 +4 bytes, read-write + +Bits 0-7: MDIV (IU, read-write) + +Bits 8-15: NDIV (IU, read-write) + +Bits 16-18: PDIV (IV, read-write) + +VPLL coeff register +------------------- + +Name: VPLL_COEFF +Offset: 0x00680508 +4 bytes, read-write + +Bits 0-7: MDIV (IU, read-write) + +Bits 8-15: NDIV (IU, read-write) + +Bits 16-18: PDIV (IV, read-write) + +PLL coeff select register +------------------------- + +Name: PLL_COEFF_SELECT +Offset: 0x0068050C +4 bytes, read-write + +Bit 4: DLL_BYPASS (IV, read-write) + FALSE 0x0 (default, read-write) + TRUE 0x1 (read-write) + +Bit 8: MPLL_SOURCE (IV, read-write) + DEFAULT 0x0 (default, read-write) + PROG 0x1 (read-write) + +Bit 12: MPLL_BYPASS (IV, read-write) + FALSE 0x0 (default, read-write) + TRUE 0x1 (read-write) + +Bit 16: VPLL_SOURCE (IV, read-write) + DEFAULT 0x0 (default, read-write) + PROG 0x1 (read-write) + +Bit 20: VPLL_BYPASS (IV, read-write) + FALSE 0x0 (default, read-write) + TRUE 0x1 (read-write) + +Bits 24-25: PCLK_SOURCE (IV, read-write) + VPLL 0x0 (default, read-write) + VIP 0x1 (read-write) + XTALOSC 0x2 (read-write) + +Bit 28: VCLK_RATIO (IV, read-write) + DB1 0x0 (default, read-write) + DB2 0x1 (read-write) + +General control register +------------------------ + +Various flags for DAC. BPC controls the width of the palette. + +Name: GENERAL_CONTROL +Offset: 0x00680600 +4 bytes, read-write + +Bits 0-1: FF_COEFF (IV, read-write) + DEF 0x0 (default, read-write) + +Bit 4: IDC_MODE (IV, read-write) + GAMMA 0x0 (default, read-write) + INDEX 0x1 (read-write) + +Bit 8: VGA_STATE (IV, read-write) + NOTSEL 0x0 (default, read-write) + SEL 0x1 (read-write) + +Bit 12: 565_MODE (IV, read-write) + NOTSEL 0x0 (default, read-write) + SEL 0x1 (read-write) + +Bit 16: BLK_PEDSTL (IV, read-write) + OFF 0x0 (default, read-write) + ON 0x1 (read-write) + +Bit 17: TERMINATION (IV, read-write) + 37OHM 0x0 (default, read-write) + 75OHM 0x1 (read-write) + +Bit 20: BPC (IV, read-write) + 6BITS 0x0 (default, read-write) + 8BITS 0x1 (read-write) + +Bit 24: DAC_SLEEP (IV, read-write) + DIS 0x0 (default, read-write) + EN 0x1 (read-write) + +Bit 28: PALETTE_CLK (IV, read-write) + EN 0x0 (default, read-write) + DIS 0x1 (read-write) + +VSERR width register +-------------------- + +Name: VSERR_WIDTH +Offset: 0x00680700 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +VEQU end register +----------------- + +Name: VEQU_END +Offset: 0x00680704 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Vertical B(?) blank end register +-------------------------------- + +Name: VBBLANK_END +Offset: 0x00680708 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Vertical blank end register +--------------------------- + +Name: VBLANK_END +Offset: 0x0068070C +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Vertical blank start register +----------------------------- + +Name: VBLANK_START +Offset: 0x00680710 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Vertical (B?) blank start register +---------------------------------- + +Name: VBBLANK_START +Offset: 0x00680714 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +VEQU start register +------------------- + +Name: VEQU_START +Offset: 0x00680718 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Vertical total register +----------------------- + +Name: VTOTAL +Offset: 0x0068071C +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal sync width register +------------------------------ + +Name: HSYNC_WIDTH +Offset: 0x00680720 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal burst start register +------------------------------- + +Name: HBURST_START +Offset: 0x00680724 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal burst end register +----------------------------- + +Name: HBURST_END +Offset: 0x00680728 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal blank start register +------------------------------- + +Name: HBLANK_START +Offset: 0x0068072C +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal blank end register +----------------------------- + +Name: HBLANK_END +Offset: 0x00680730 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +Horizontal total register +------------------------- + +Name: HTOTAL +Offset: 0x00680734 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +HEQU width register +------------------- + +Name: HEQU_WIDTH +Offset: 0x00680738 +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + +HSERR width register +-------------------- + +Name: HSERR_WIDTH +Offset: 0x0068073C +4 bytes, read-write + +Bits 0-10: VAL (IV, read-write) + + + +'Standard' SVGA registers +========================= + +ATTR, CTRC, GRAPH, SEQ and MISC registers are available throught MMIO too. + +Name: ATTR_REG_INDEX +Offset: 0x006013c0 +1 byte, read-write + +Name: ATTR_REG_DATA +Offset: 0x006013c1 +1 byte, read-write + +Name: CRTC_REG_INDEX +Offset: 0x006013d4 +1 byte, read-write + +Name: CRTC_REG_DATA +Offset: 0x006013d5 +1 byte, read-write + +Name: GRA_REG_INDEX +Offset: 0x000C03ce +1 byte, read-write + +Name: GRA_REG_DATA +Offset: 0x000C03cf +1 byte, read-write + +Name: SEQ_REG_INDEX +Offset: 0x000C03c4 +1 byte, read-write + +Name: SEQ_REG_DATA +Offset: 0x000C03c5 +1 byte, read-write + +Name: MISC_REG +Offset: 0x000C03c2 +1 byte, read-write + + + +Extra CRTC registers +==================== + +Repaint 0 register +------------------ + +Extended offset and start address. + +Name: REPAINT0 +Index: 0x19 +1 byte + +Bits 0-4: START_ADDR_20_16 + +Bits 5-7: OFFSET_10_8 + +Repaint 1 register +------------------ + +Various flags. + +Name: REPAINT1 +Index: 0x1a +1 byte + +Bit 1: PALETTE_WIDTH + 8BITS 0x0 + 6BITS 0x1 + +Bit 2: LARGE_SCREEN + DISABLE 0x1 + ENABLE 0x0 ( >= 1280 ) + +Bit 4: COMPATIBLE_TEXT + ENABLE 0x1 + DISABLE 0x0 + +Bit 6: VSYNC + DISABLE 0x1 + ENABLE 0x0 + +Bit 7: HSYNC + DISABLE 0x1 + ENABLE 0x0 + +FIFO control register +--------------------- + +Controls how much data the refresh fifo requests. + +Name: FIFO_CONTROL +Index: 0x1b +1 byte + +Bits 0-2: BURST_LENGTH + BURST_LENGTH_8 0x0 + BURST_LENGTH_32 0x1 + BURST_LENGTH_64 0x2 + BURST_LENGTH_128 0x3 + BURST_LENGTH_256 0x4 + +Bit 7: UNDERFLOW_WARN + +FIFO register +------------- + +When the fifo occupancy falls below *twice* the watermark, +the refresh fifo will start to be refilled. If this value is +too low, you will get junk on the screen. Too high, and performance +will suffer. Watermark in units of 8 bytes. + +Name: FIFO +Index: 0x20 +1 byte + +Bits 0-5: WATERMARK + +Bit 7: RESET + +Extra register +-------------- + +Assorted extra bits. + +Name: EXTRA +Index: 0x25 +1 byte + +Bit 0: VERT_TOTAL_10 + +Bit 1: VERT_DISPLAY_END_10 + +Bit 2: VERT_RETRACE_START_10 + +Bit 3: VERT_BLANK_START_10 + +Bit 4: HORIZ_BLANK_END_6 + +Bit 5: OFFSET_11 + +Pixel register +-------------- + +Controls what the format of the framebuffer is. + +Name: PIXEL +Index: 0x28 +1 byte + +Bits 0-1: FORMAT + VGA 0x0 + 8BPP 0x1 + 16BPP 0x2 + 32BPP 0x3 + +Bits 3-5: TV_HORIZ_ADJUST + +Bit 6: TV_MODE + NTSC 0x0 + PAL 0x1 + +Bit 7: MODE + TV 0x1 + VGA 0x0 + +Horizontal extra register +------------------------- + +Horizonal extended bits. + +Name: HORIZ_EXTRA +Index: 0x2d +1 byte + +Bit 0: DISPLAY_TOTAL_8 + +Bit 1: DISPLAY_END_8 + +Bit 2: HORIZ_BLANK_START_8 + +Bit 3: HORIZ_RETRACE_START_8 + +Bit 4: INTER_HALF_START_8 + +Graphic cursor 0 register +------------------------- + +Name: GRCURSOR0 +Index: 0x30 +1 byte + +Bits 0-5: START_ADDR_21_16 + +Graphic cursor 1 register +------------------------- + +Name: GRCURSOR1 +Index: 0x31 +1 byte + +Bit 0: CURSOR + DISABLE 0x0 + ENABLE 0x1 + +Bit 1: SCAN_DBL + DISABLE 0x0 + ENABLE 0x1 + +Bits 3-7: START_ADDR_15_11 + +EOF diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index b5d4ff44a..89196d5e6 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -7,8 +7,18 @@ * This file is part of the 86Box distribution. * * Defines graphics objects for Nvidia NV3 architecture-based GPU (RIVA 128/RIVA 128 ZX), - * as well as for later GPUs if they use the same objects. + * as well as for later GPUs if they use the same objects. + * Note: These uint32_ts are basically object methods that are being submitted + * They have different names so the user can use them more easily but different versions of the same class can be distinguished + * ALL of these structures HAVE(?) to be a size of exactly 0x2000 bytes because that's what the hashtable expects and they need to actually map + * directly to the PHYSICAL PGRAPH REGISTERS while sitting in RAMHT!!!!. * + * Also, these class IDs don't relate to the internal architecture of the GPU. + * Effectively, the NVIDIA drivers are faking shit. There are only 16 classes but the drivers recognise many more. See nv3_object_classes_driver.txt for the list of + * classes recognised by the driver. + * This is why the Class IDs you see here are not the same as you may see in other places. + * + * Todo: Is reserved* actually needed? * * * Authors: Connor Hyde @@ -22,54 +32,468 @@ #include #include -/* - Note: These uint32_ts are basically object methods that are being submitted - They have different names so the user can use them more easily but different versions of the same class can be distinguished - - ALL of these structures HAVE to be a size of exactly 0x2000 bytes because that's what the hashtable expects. - - Also, these class IDs don't relate to the internal architecture of the GPU. - Effectively, the NVIDIA drivers are faking shit. There are only 16 classes but the drivers recognise many more. See nv3_object_classes_driver.txt for the list of - classes recognised by the driver. - - The 3-bit DMA SUBCHANNEL is combined with a 4-bit CLASS ID to get the REAL CLASS ID. There are 32 CLASSES per subchannel and 8 SUBCHANNELS. - - This is why the Class IDs you see here are not the same as you may see in other places. -*/ +// This is slower, but these need to map *****EXACTLY***** to the registers in PGRAPH, +// or everything FUCKS UP +// +// DO NOT REMOVE! DO NOT REMOVE! DO NOT REMOVE! +#pragma pack(push, 1) +// CLass names for debugging extern const char* nv3_class_names[]; -/* Object Class 0x01 (real hardware) +/* Class context switch method */ +typedef struct nv3_class_ctx_switch_method_s +{ + union + { + uint32_t data; + + uint16_t instance; + uint8_t channel_id : 6; + uint16_t reserved : 9; + bool reset_if_volatile; // ???? + } set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; // Required for NV_CLASS Core Functionality + uint32_t set_notify; + +} nv3_class_ctx_switch_method_t; + +/* 32-bit BGRA format colour for 2D acceleration */ +typedef struct nv3_color_32_s +{ + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t a; +} nv3_color_32_t; + +/* A4R4G4B4 */ +typedef struct nv3_color_16_a4r4g4b4_s +{ + uint8_t a : 4; + uint8_t r : 4; + uint8_t g : 4; + uint8_t b : 4; +} nv3_color_16_a4r4g4b4_t; + +/* A1R5G5B5 format */ +typedef struct nv3_color_16_a1r5g5b5_s; +{ + uint8_t a : 1; + uint8_t r : 5; + uint8_t g : 5; + uint8_t b : 5; +} nv3_color_16_a1r5g5b5_t; + +/* 565 format - NV3Tweak */ +typedef struct nv3_color_16_r5g6b5_s +{ + uint8_t r : 5; + uint8_t g : 6; + uint8_t b : 5; +} nv3_color_16_r5g6b5_t; + +/* Generic 16-bit position*/ +typedef struct nv3_position_16_s +{ + union + { + uint32_t pos; + + uint16_t y; + uint16_t x; + } position; +} nv3_position_16_t; + +/* Generic 16-bit size */ +typedef struct nv3_size_16_s +{ + union + { + uint32_t size; + + uint16_t h; + uint16_t w; + } size; +} nv3_size_16_t; + +/* Generic 32-bit colour + 16-bit position */ +typedef struct nv3_color_and_position_16_s +{ + uint32_t color; + nv3_position_16_t points; +} nv3_color_and_position_16_t; + +/* Generic 16-bit clip region */ +typedef struct nv3_clip_16_s +{ + // The bounds of the clipping area. + uint16_t left; + uint16_t top; + uint16_t right; + uint16_t bottom; +} nv3_clip_16_t; + +/* In case your positions weren't HIGH PRECISION enough */ +typedef struct nv3_position_32_s +{ + uint32_t x; + uint32_t y; +} nv3_position_32_t; + +// COLOUR FORMATS + +/* + Object Class 0x01 (real hardware, also 0x41) + 0x12 (drivers) Beta factor */ -typedef struct nv_object_class_001 +typedef struct nv3_object_class_001 { - uint8_t reserved[0xFF]; // Required for NV_CLASS Core Functionality + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; // Required for NV_CLASS Core Functionality + uint32_t set_notify; + uint8_t reserved2[0x1F8]; + uint8_t set_beta_factor_1d31; // 31:31 (?) value, 30:21 fraction + uint8_t reserved3[0x1CFB]; // needs to be 0x2000 bytes // Put the rest of it here } nv3_beta_factor_t; /* -Object Class 0x07 (real hardware) - 0x1E (drivers) -Also 0x47 in context IDs -A rectangle. Wahey! + Object class 0x02 (real hardware) + 0x14/0x43 (drivers) + Also 0x42 in context IDs + Render operation used for things like blending. Appears to be 8-bit i.e. a ROP3 with 256 possible operations. */ -typedef struct nv_object_class_007 +typedef struct nv3_object_class_002 { - uint8_t reserved[0xFF]; // Required for NV_CLASS Core Functionality - uint32_t set_notify_ctx_dma; // Set notifier context for DMA + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier - uint32_t set_image_output; // Set the image output type - uint8_t reserved2[0xF5]; // up to 0x200 - uint32_t set_zeta_output; // Zeta buffer input - uint32_t set_zeta_input; // Zeta buffer input - uint32_t set_color_format; // Color format: 0x100000=15bpp. - uint8_t reserved3[0xF5]; // up to 0x300 + uint8_t reserved2[0x1F8]; + uint8_t rop; // ROP3 (ID = ????????) + uint8_t reserved3[0x1CFB]; // needs to be 0x2000 bytes +} nv3_render_operation_t; - /* THESE ARE ALL THE SAME METHOD */ - uint32_t color_zeta32; // 32-bit zeta buffer color (?) - uint32_t point; // Draw a point i guess - uint8_t reserved4[0x4F3]; // up to 0x7fc - uint32_t control_out; // 7fd-7ff - uint8_t reserved5[0x1800]; // up to 0x2000 +/* + Object class 0x03 (real hardware) + 0x15 (drivers) + Also 0x43 in context IDs + A chroma/color key, like in video editing +*/ +typedef struct nv3_object_class_003 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0xFF]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1F8]; + uint8_t color; // ROP3 (ID = ????????) + uint8_t reserved3[0x1CFB]; // needs to be 0x2000 bytes +} nv3_chroma_key_t; + +/* + Object class 0x04 (real hardware) + 0x15 (drivers) + Also 0x44 in context IDs + Plane mask +*/ +typedef struct nv3_object_class_004 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0xFF]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1F8]; + uint8_t color; // ROP3 (ID = ????????) + uint8_t reserved3[0x1CFB]; // needs to be 0x2000 bytes +} nv3_plane_mask_t; + +/* + Object class 0x05 (real hardware) + 0x19/0x1E/0x47 (drivers) + Also 0x45 in context IDs + Clipping rectangle used for various blitting operations +*/ +typedef struct nv3_object_class_005 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0xFF]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1F4]; + + /* 16-bit precision */ + nv3_position_16_t position; + nv3_size_16_t size; + uint8_t reserved3[0x1CFB]; // needs to be 0x2000 bytes + +} nv3_clipping_rectangle_t; + +/* + Object Class 0x06 (real hardware) + 0x?? (drivers) + Also 0x46 in context IDs + A pattern used for blits. Wahey! +*/ +typedef struct nv3_object_class_006 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0xFF]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x200]; + uint32_t shape; // 0 = 8x8, 1 = 64x1, 2 = 1x64 + uint32_t color0; // Some 32-bit format (BGRA?) + uint32_t color1; // BGRA? + uint32_t pattern[2]; // BGRA? + uint8_t reserved3[0x1CDF]; // needs to be 0x2000 bytes +} nv3_pattern_t; + +/* + Object Class 0x07 (real hardware) + 0x1E (drivers) + Also 0x47 in context IDs + A rectangle. Wahey! +*/ +typedef struct nv3_object_class_007 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint8_t color; // ROP3 (ID = ????????) + uint8_t reserved3[0xF8]; + nv3_position_16_t position[16]; + nv3_size_16_t size[16]; + uint8_t reserved4[0x1B7F]; } nv3_rectangle_t; + + +/* In case your points weren't colourful enough */ +typedef struct nv3_object_class_008_cpoint_s +{ + uint32_t color; + nv3_position_16_t position; + +} nv3_object_class_008_cpoint_t; + +/* + Object Class 0x08 (real hardware) + 0x1A (drivers) + Also 0x48 in context IDs + A point: the revolutionary 3d graphics technique... +*/ +typedef struct nv3_object_class_008 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint32_t color; // BGRA? + nv3_position_16_t point[16]; // Boring points + nv3_position_32_t point32[16]; // Allows you to have points with full 32-bit precision + nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points! + uint8_t reserved3[0x1A7B]; +} nv3_point_t; + +/* Normal line... */ +typedef struct nv3_object_class_009_line_s +{ + nv3_position_16_t start; // presumably unless it's in reverse order...TODO: check the order + nv3_position_16_t end; + +} nv3_object_class_009_line_t; + +/* THIRTY TWO BIT PRECISION line */ +typedef struct nv3_object_class_009_line32_s +{ + uint32_t x0; + uint32_t x1; + uint32_t y0; + uint32_t y1; +} nv3_object_class_009_line32_t; + +/* nv3_object_class_009_polyline_t not implemented because it's just a duplicate of nv3_object_class_009_line */ +/* nv3_object_class_009_polyline32_t not implemented because it's just a duplicate of nv3_object_class_009_line32 */ + + +/* + Object Class 0x09 (real hardware) + 0x1B (drivers) + Also 0x49 in context IDs + It's a line, but also a polygon... +*/ +typedef struct nv3_object_class_009 +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint32_t color; // BGRA? + nv3_object_class_009_line_t line[16]; // List of line points (...) + nv3_object_class_009_line32_t line32[8]; + nv3_object_class_009_line_t polyline[32]; + nv3_object_class_009_line32_t polyline32[16]; + nv3_color_and_position_16_t cpolyline[16]; // List of line points and colours. + + uint8_t reserved3[0x197b]; +} nv3_line_t; + +/* + Object Class 0x0A (real hardware) + 0x1c (drivers) + Also 0x4a in context IDs + + This one is where nvidia reinvents the line, but without the starting or ending pixel. + Seriously. +*/ +typedef struct nv3_object_class_00A +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint32_t color; // BGRA? + nv3_object_class_009_line_t line[16]; // List of line points (...) + nv3_object_class_009_line32_t line32[8]; + nv3_object_class_009_line_t polyline[32]; + nv3_object_class_009_line32_t polyline32[16]; + nv3_color_and_position_16_t cpolyline[16]; // List of line points and colours. + + uint8_t reserved3[0x197b]; +} nv3_lin_t; + +/* + Object Class 0x0B (real hardware) + 0x?? (drivers) + Also 0x4b in context IDs. + + This is a triangle but seems to be obsolete. It's replaced with UD3D0Z / D3D5 Accelerated Triangle with Zeta Buffer. Does it even exist? +*/ +typedef struct nv3_object_class_00B +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint32_t color; // BGRA? + uint8_t reserved3[0x8]; + // The points of the triangle. + nv3_position_16_t points[3]; + + // Another way of filling out the points of the triangle + uint32_t x0; + uint32_t y0; + uint32_t x1; + uint32_t y1; + uint32_t y2; + uint32_t x2; + + nv3_position_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? + nv3_position_32_t mesh32[16]; + nv3_color_and_position_16_t ctriangle[3]; // Triangle with colour + nv3_color_and_position_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours + uint8_t reserved4[0x19FB]; +} nv3_triangle_t; + +typedef struct nv3_object_class_00C_nclip_s +{ + nv3_position_16_t position; + nv3_size_16_t size; +} nv3_object_class_00C_nclip_t; + +/* + Object Class 0x0C (real hardware) + 0x0C (drivers) + Also 0x4C in context IDs. + + GDI text acceleration for Windows 95. + How the fuck does this even work? +*/ +typedef struct nv3_object_class_00C +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x2F4]; + uint32_t color_a; // Color for Clip A + nv3_object_class_00C_nclip_t rect_nclip[64]; + uint8_t reserved3[0x1F0]; + nv3_clip_16_t clip_b; + uint32_t color_b; // Color for Clip B + nv3_clip_16_t rect_clip[64]; + uint8_t reserved4[0x1E8]; + nv3_clip_16_t clip_c; + uint32_t color1_c; + nv3_size_16_t size_c; + nv3_position_16_t point_c; + uint32_t color1_c_bitmap[128]; + uint8_t reserved5[0x368]; + nv3_clip_16_t clip_d; + uint32_t color1_d; + nv3_size_16_t size_in_d; + nv3_size_16_t size_out_d; + nv3_position_16_t point_d; + uint32_t mono_color1_d[128]; + uint8_t reserved6[0x364]; + nv3_clip_16_t clip_e; + uint32_t color0_e; + uint32_t color1_e; + nv3_size_16_t size_in_e; + nv3_size_16_t size_out_e; + nv3_position_16_t point_e; + uint32_t mono_color1_e[128]; + uint8_t reserved7[0xB7F]; +} nv3_win95_text_t; + + +/* + Object Class 0x0D (real hardware) + 0x?? (drivers) + Also 0x4D in context IDs. + + Represents reformatting of an image in memory. +*/ +typedef struct nv3_object_class_00D +{ + uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved2[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved3[0x204]; + uint32_t offset_in; + uint32_t offset_out; + uint32_t pitch_in; + uint32_t pitch_out; + uint32_t line_length_in; // Stride? + uint32_t line_count; + uint8_t format_input_bits; // 1 2 or 4 to increment by bits + uint8_t format_output_bits; // 1 2 to 4 to increment by bits + uint8_t reserved4[2]; + uint32_t buffer_notify; // Notify the Buffedr + uint8_t reserved5[0x1CD3]; +} nv3_memory_to_memory_format; + + + +/* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? + They are making it look like a bitfield but it's hex? + + THEY ARE ALL LITTLE ENDIAN +*/ +typedef enum nv3_object_class_01C_pixel_format_e +{ + // Y8P4 + // 12-bits (Y8 - Planar YUV 8 bits (Y value only), 4 bits of indexed colour too? + nv3_m2mt_pixel_format_le_y8_p4 = 0x1010000, + + // Y16P2 + // 16-bits (Y16) - Planar YUV 16 bits (Y value only), 2 bits of indexed colour too? + nv3_m2mt_pixel_format_le_y16_p2 = 0x1010101, + + /* 1 unused bit, 555 15-bit format, p2(?) + */ + nv3_m2mt_pixel_format_x1r5g5b5_p2 = 0x1000000, + + // X8G8B8R8, 24-bit colour (or 24-bit colour with alpha) + nv3_m2mt_pixel_format_x8g8b8r8 = 0x1, +} nv3_object_class_01C_pixel_format; + +// TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! +#pragma pack(pop) // return packing to whatever it was before this disaster \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 3e69b57fd..0b6e6b0eb 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -7,7 +7,7 @@ * This file is part of the 86Box distribution. * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated 1 January 2025 (STILL WORKING ON IT) + * Last updated 2 January 2025 (STILL WORKING ON IT) * * * @@ -603,8 +603,10 @@ extern const device_config_t nv3_config[]; /* STRUCTURES FOR THE GPU START HERE + OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H */ + //todo: pixel format // Master Control From da14dfb9700ea04de60b651536a69eb9dab6d76b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 3 Jan 2025 01:08:04 +0000 Subject: [PATCH 044/274] actually use the ctx_switch structure --- .../86box/nv/classes/vid_nv3_classes.h | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 89196d5e6..78d9a46a0 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -53,8 +53,6 @@ typedef struct nv3_class_ctx_switch_method_s uint16_t reserved : 9; bool reset_if_volatile; // ???? } set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0x100]; // Required for NV_CLASS Core Functionality - uint32_t set_notify; } nv3_class_ctx_switch_method_t; @@ -150,7 +148,7 @@ typedef struct nv3_position_32_s */ typedef struct nv3_object_class_001 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; // Required for NV_CLASS Core Functionality uint32_t set_notify; uint8_t reserved2[0x1F8]; @@ -167,7 +165,7 @@ typedef struct nv3_object_class_001 */ typedef struct nv3_object_class_002 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; @@ -183,7 +181,7 @@ typedef struct nv3_object_class_002 */ typedef struct nv3_object_class_003 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; @@ -199,7 +197,7 @@ typedef struct nv3_object_class_003 */ typedef struct nv3_object_class_004 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; @@ -215,7 +213,7 @@ typedef struct nv3_object_class_004 */ typedef struct nv3_object_class_005 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F4]; @@ -235,7 +233,7 @@ typedef struct nv3_object_class_005 */ typedef struct nv3_object_class_006 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0xFF]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x200]; @@ -254,7 +252,7 @@ typedef struct nv3_object_class_006 */ typedef struct nv3_object_class_007 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; @@ -282,7 +280,7 @@ typedef struct nv3_object_class_008_cpoint_s */ typedef struct nv3_object_class_008 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; @@ -322,7 +320,7 @@ typedef struct nv3_object_class_009_line32_s */ typedef struct nv3_object_class_009 { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; @@ -346,7 +344,7 @@ typedef struct nv3_object_class_009 */ typedef struct nv3_object_class_00A { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; @@ -369,7 +367,7 @@ typedef struct nv3_object_class_00A */ typedef struct nv3_object_class_00B { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; @@ -409,7 +407,7 @@ typedef struct nv3_object_class_00C_nclip_s */ typedef struct nv3_object_class_00C { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x2F4]; @@ -453,7 +451,7 @@ typedef struct nv3_object_class_00C */ typedef struct nv3_object_class_00D { - uint32_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved2[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved3[0x204]; From 98ec3823c63ff4c6ee1ca84217e9e671fa12ab2a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 3 Jan 2025 12:39:46 +0000 Subject: [PATCH 045/274] begni working on scaled image from memory def so that we can sync --- src/include/86box/nv/classes/vid_nv3_classes.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 78d9a46a0..d7086afc0 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -466,9 +466,19 @@ typedef struct nv3_object_class_00D uint8_t reserved4[2]; uint32_t buffer_notify; // Notify the Buffedr uint8_t reserved5[0x1CD3]; -} nv3_memory_to_memory_format; - +} nv3_memory_to_memory_format_t; +/* + Object Class 0x0E (real hardware) + 0x?? (drivers) + Also 0x4E in context IDs. + + Represents a scaled image coming from memory. +*/ +typedef struct nv3_object_class_00E +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; +} nv3_scaled_image_from_memory_t; /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? They are making it look like a bitfield but it's hex? From 324c25978d30556c203a349b3b6c8459050c92d5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 4 Jan 2025 02:01:23 +0000 Subject: [PATCH 046/274] fixes to class headers --- .../86box/nv/classes/vid_nv3_classes.h | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index d7086afc0..35b704caf 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -98,8 +98,13 @@ typedef struct nv3_position_16_s { uint32_t pos; - uint16_t y; - uint16_t x; + struct vid_nv3_classes + { + uint16_t y; + uint16_t x; + }; + + } position; } nv3_position_16_t; @@ -110,8 +115,13 @@ typedef struct nv3_size_16_s { uint32_t size; - uint16_t h; - uint16_t w; + struct + { + uint16_t h; + uint16_t w; + }; + + } size; } nv3_size_16_t; @@ -166,7 +176,7 @@ typedef struct nv3_object_class_001 typedef struct nv3_object_class_002 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0xFF]; + uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; uint8_t rop; // ROP3 (ID = ????????) @@ -182,7 +192,7 @@ typedef struct nv3_object_class_002 typedef struct nv3_object_class_003 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0xFF]; + uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; uint8_t color; // ROP3 (ID = ????????) @@ -198,7 +208,7 @@ typedef struct nv3_object_class_003 typedef struct nv3_object_class_004 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0xFF]; + uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F8]; uint8_t color; // ROP3 (ID = ????????) @@ -214,7 +224,7 @@ typedef struct nv3_object_class_004 typedef struct nv3_object_class_005 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0xFF]; + uint8_t reserved[0X100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1F4]; @@ -234,7 +244,7 @@ typedef struct nv3_object_class_005 typedef struct nv3_object_class_006 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved[0xFF]; + uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x200]; uint32_t shape; // 0 = 8x8, 1 = 64x1, 2 = 1x64 @@ -256,7 +266,7 @@ typedef struct nv3_object_class_007 uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint8_t color; // ROP3 (ID = ????????) + uint32_t color; // The colour uint8_t reserved3[0xF8]; nv3_position_16_t position[16]; nv3_size_16_t size[16]; @@ -452,9 +462,9 @@ typedef struct nv3_object_class_00C typedef struct nv3_object_class_00D { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint8_t reserved2[0x100]; + uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier - uint8_t reserved3[0x204]; + uint8_t reserved2[0x204]; uint32_t offset_in; uint32_t offset_out; uint32_t pitch_in; @@ -463,9 +473,9 @@ typedef struct nv3_object_class_00D uint32_t line_count; uint8_t format_input_bits; // 1 2 or 4 to increment by bits uint8_t format_output_bits; // 1 2 to 4 to increment by bits - uint8_t reserved4[2]; + uint8_t reserved3[2]; uint32_t buffer_notify; // Notify the Buffedr - uint8_t reserved5[0x1CD3]; + uint8_t reserved4[0x1CD3]; } nv3_memory_to_memory_format_t; /* @@ -478,6 +488,9 @@ typedef struct nv3_object_class_00D typedef struct nv3_object_class_00E { nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x200]; } nv3_scaled_image_from_memory_t; /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? From 6e18a53d27c6161b5bfadf4ea645cab4b965b11a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 4 Jan 2025 22:38:52 +0000 Subject: [PATCH 047/274] add uscaled --- .../86box/nv/classes/vid_nv3_classes.h | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 35b704caf..39aa600be 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -75,7 +75,7 @@ typedef struct nv3_color_16_a4r4g4b4_s } nv3_color_16_a4r4g4b4_t; /* A1R5G5B5 format */ -typedef struct nv3_color_16_a1r5g5b5_s; +typedef struct nv3_color_16_a1r5g5b5_s { uint8_t a : 1; uint8_t r : 5; @@ -491,6 +491,25 @@ typedef struct nv3_object_class_00E uint8_t reserved[0x100]; uint32_t set_notify; uint8_t reserved2[0x200]; + nv3_position_16_t clip_0; + nv3_size_16_t clip_1; + nv3_position_16_t rectangle_out_0; + nv3_size_16_t rectangle_out_1; + // Calculus in a graphics card + uint32_t delta_du_dx; + uint32_t delta_dv_dy; + uint8_t reserved3[0xE0]; + nv3_size_16_t size; // can be size_y if YUV420 + uint32_t pitch; + uint32_t offset; + uint32_t point; + // YUV420 stuff + uint32_t pitch_yuv420; + uint32_t offset_y; + uint32_t offset_u; + uint32_t offset_v; + uint32_t point_yuv420; + uint8_t reserved4[0x1BE7]; // pad to 0x2000 } nv3_scaled_image_from_memory_t; /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? From c6f0866509b3438cd9a1e7a791788f8d81fa3a5d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 5 Jan 2025 00:29:35 +0000 Subject: [PATCH 048/274] Define the rest of the object classes. --- .../86box/nv/classes/vid_nv3_classes.h | 563 +++++++++++++++++- src/include/86box/nv/vid_nv3.h | 3 +- 2 files changed, 548 insertions(+), 18 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 39aa600be..a22871beb 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -10,7 +10,8 @@ * as well as for later GPUs if they use the same objects. * Note: These uint32_ts are basically object methods that are being submitted * They have different names so the user can use them more easily but different versions of the same class can be distinguished - * ALL of these structures HAVE(?) to be a size of exactly 0x2000 bytes because that's what the hashtable expects and they need to actually map + * ALL of these structures HAVE(?) to be a size of exactly 0x2000 bytes because that's what the hashtable expects and they need to actually map into the vram address space + * (they are converted to pointers). * directly to the PHYSICAL PGRAPH REGISTERS while sitting in RAMHT!!!!. * * Also, these class IDs don't relate to the internal architecture of the GPU. @@ -57,13 +58,22 @@ typedef struct nv3_class_ctx_switch_method_s } nv3_class_ctx_switch_method_t; /* 32-bit BGRA format colour for 2D acceleration */ -typedef struct nv3_color_32_s +typedef struct nv3_color_bgra_32_s { uint8_t b; uint8_t g; uint8_t r; uint8_t a; -} nv3_color_32_t; +} nv3_color_bgra_32_t; + +/* 32-bit ARGB format colour for internal D3D5 stuff */ +typedef struct nv3_color_argb_32_s +{ + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} nv3_color_argb_32_t; /* A4R4G4B4 */ typedef struct nv3_color_16_a4r4g4b4_s @@ -128,7 +138,7 @@ typedef struct nv3_size_16_s /* Generic 32-bit colour + 16-bit position */ typedef struct nv3_color_and_position_16_s { - uint32_t color; + nv3_color_argb_32_t color; nv3_position_16_t points; } nv3_color_and_position_16_t; @@ -248,9 +258,9 @@ typedef struct nv3_object_class_006 uint32_t set_notify; // Set notifier uint8_t reserved2[0x200]; uint32_t shape; // 0 = 8x8, 1 = 64x1, 2 = 1x64 - uint32_t color0; // Some 32-bit format (BGRA?) - uint32_t color1; // BGRA? - uint32_t pattern[2]; // BGRA? + uint32_t color0; // Some 32-bit format (argb?) + uint32_t color1; // argb? + uint32_t pattern[2]; // argb? uint8_t reserved3[0x1CDF]; // needs to be 0x2000 bytes } nv3_pattern_t; @@ -262,11 +272,11 @@ typedef struct nv3_object_class_006 */ typedef struct nv3_object_class_007 { - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint8_t reserved[0x100]; - uint32_t set_notify; // Set notifier + uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint32_t color; // The colour + nv3_color_argb_32_t color; // The colour uint8_t reserved3[0xF8]; nv3_position_16_t position[16]; nv3_size_16_t size[16]; @@ -277,9 +287,8 @@ typedef struct nv3_object_class_007 /* In case your points weren't colourful enough */ typedef struct nv3_object_class_008_cpoint_s { - uint32_t color; - nv3_position_16_t position; - + nv3_color_argb_32_t color; // BGRA-format 32-bit color + nv3_position_16_t position; // } nv3_object_class_008_cpoint_t; /* @@ -294,7 +303,7 @@ typedef struct nv3_object_class_008 uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint32_t color; // BGRA? + nv3_color_argb_32_t color; // argb? nv3_position_16_t point[16]; // Boring points nv3_position_32_t point32[16]; // Allows you to have points with full 32-bit precision nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points! @@ -334,7 +343,7 @@ typedef struct nv3_object_class_009 uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint32_t color; // BGRA? + nv3_color_argb_32_t color; // argb? nv3_object_class_009_line_t line[16]; // List of line points (...) nv3_object_class_009_line32_t line32[8]; nv3_object_class_009_line_t polyline[32]; @@ -358,7 +367,7 @@ typedef struct nv3_object_class_00A uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint32_t color; // BGRA? + nv3_color_argb_32_t color; // argb? nv3_object_class_009_line_t line[16]; // List of line points (...) nv3_object_class_009_line32_t line32[8]; nv3_object_class_009_line_t polyline[32]; @@ -381,7 +390,7 @@ typedef struct nv3_object_class_00B uint8_t reserved[0x100]; uint32_t set_notify; // Set notifier uint8_t reserved2[0x1FC]; - uint32_t color; // BGRA? + nv3_color_argb_32_t color; // argb? uint8_t reserved3[0x8]; // The points of the triangle. nv3_position_16_t points[3]; @@ -512,6 +521,514 @@ typedef struct nv3_object_class_00E uint8_t reserved4[0x1BE7]; // pad to 0x2000 } nv3_scaled_image_from_memory_t; +// (0x0F does not exist) + +/* + Object Class 0x10 (real hardware) + 0x?? (drivers) + Also 0x50 in context IDs. + + Represents a blit. +*/ + +typedef struct nv3_object_class_010 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x1F8]; + nv3_position_16_t point_in; + nv3_position_16_t point_out; + nv3_size_16_t size; + uint8_t reserved3[0x1CF3]; +} nv3_blit_t; + +/* + Object Class 0x11 (real hardware) + 0x?? (drivers) + Also 0x51 in context IDs. + + Represents an image from the cpu (i guess from system memory) +*/ +typedef struct nv3_object_class_011 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x1FC]; + nv3_position_16_t point; + nv3_size_16_t size; + nv3_size_16_t size_in; + uint8_t reserved3[0xF0]; + nv3_color_argb_32_t color[32]; // The colour to use + uint8_t reserved4[0x1B7F]; +} nv3_image_t; + +/* + Object Class 0x12 (real hardware) + 0x?? (drivers) + Also 0x52 in context IDs. + + Bitmap from CPU. + It seems the difference is that an image is colour but a +*/ +typedef struct nv3_object_class_012 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x200]; + nv3_color_argb_32_t color_0; + nv3_color_argb_32_t color_1; + nv3_position_16_t point; // Top left(?) of the bitmap + nv3_size_16_t size; + nv3_size_16_t size_in; + uint32_t monochrome_bitmap[32]; + uint8_t reserved4[0x1B7F]; +} nv3_bitmap_t; + +// 0x13 doesn't exist + +/* + Object Class 0x14 (real hardware) + 0x?? (drivers) + Also 0x54 in context IDs. + + Image Transfer to Memory + It seems the difference is that an image is colour but a bitmap is b&w +*/ +typedef struct nv3_object_class_014 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x200]; + nv3_position_16_t point; + nv3_size_16_t size; + uint32_t image_pitch; // bytes per row + uint32_t image_start; + uint8_t reserved3[0x1C37]; +} nv3_image_to_memory_t; + +/* + Object Class 0x15 (real hardware) + 0x?? (drivers) + Also 0x55 in context IDs. + + Stretched Image from CPU + Seems to be, by the very large color array, the main class used in 2d acceleration. +*/ +typedef struct nv3_object_class_015 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x1FC]; + nv3_size_16_t size_in; + uint32_t delta_dx_du; + uint32_t delta_dy_dv; + nv3_position_16_t clip_0; + nv3_size_16_t clip_1; + uint32_t point12d4; /* todo: fraction struct */ + uint8_t reserved3[0xE4]; + uint32_t color[1792]; + // no reserve needed +} nv3_stretched_image_from_cpu_t; + +// 0x16 invalid + +/* + Object Class 0x17 (real hardware) + 0x?? (drivers) + Also 0x57 in context IDs. + + Direct3D 5.0 accelerated triangle with zeta buffer (Not the same as a Zbuffer...) + This is the final boss of this GPU. True horror stands below. +*/ + +// +// NV3 D3D5: TEXTURING & PIXEL FORMATS +// + +typedef enum nv3_d3d5_texture_pixel_format_e +{ + /* + 16-Bit Pixel Format + 5 bits red, 5 bits green, 5 bits alpha, "boolean" alpha + */ + nv3_d3d5_pixel_format_le_a1r5g5b5 = 0, + + /* + 15-Bit Pixel Format (represented as 16) + 1 bit irrelevant, 5 bits red, 5 bits green, 5 bits alpha + */ + nv3_d3d5_pixel_format_le_x1r5g5b5 = 1, + + /* + 16-Bit Pixel Format + 4 bits alpha, 4 bits red, 4 bits green, 4 bits blue + */ + nv3_d3d5_pixel_format_le_a4r4g4b4 = 2, + + /* + 16-Bit Pixel Format + 5 bits red, 6 bits green, 5 bits blue + (Required nv3tweak...) + */ + nv3_d3d5_pixel_format_le_r5g6b5 = 3, + +} nv3_d3d5_texture_pixel_format; + +/* Output format + + The output pixel format...I +*/ +typedef enum nv3_d3d5_output_pixel_format_e +{ + /* + 32-Bit Pixel Format + 8 bits irrelevant, 8 bits red, 8 bits green, 8 bits blue + */ + nv3_d3d5_output_pixel_format_le_x8r8g8b8 = 0, + + /* + 32-Bit Pixel Format + 8 bits irrelevant, 8 bits red, 8 bits green, 8 bits blue + Is this even used?? The riva can't even do 32 bit colour in 3d... + */ + nv3_d3d5_output_pixel_format_le_a8r8g8b8 = 1, + + +} nv3_d3d5_output_pixel_format; + + +/* Texture size + + NOTE: ONLY 256X256 OR LOWER ARE SUPPORTED! 2048X2048X16 TAKES UP ENTIRE VRAM OF RIVA 128 ZX... + I ASSUME THESE ARE INTERNALLY SCALED DOWN +*/ +typedef enum nv3_d3d5_texture_size_e +{ + nv3_d3d5_texture_size_1x1 = 0, + + nv3_d3d5_texture_size_2x2 = 1, + + nv3_d3d5_texture_size_4x4 = 2, + + nv3_d3d5_texture_size_8x8 = 3, + + nv3_d3d5_texture_size_16x16 = 4, + + nv3_d3d5_texture_size_32x32 = 5, + + nv3_d3d5_texture_size_64x64 = 6, + + nv3_d3d5_texture_size_128x128 = 7, + + // Highest size supported natively by hardware? + nv3_d3d5_texture_size_256x256 = 8, + + nv3_d3d5_texture_size_512x512 = 9, + + nv3_d3d5_texture_size_1024x1024 = 10, + + nv3_d3d5_texture_size_2048x2048 = 11, + + +} nv3_d3d5_texture_size; + +/* Texture Wrapping Mode for U/V Coordinate Overflow + +*/ +typedef enum nv3_d3d5_texture_wrap_mode_e +{ + // Map textures in a cylindrical fashion. + nv3_d3d5_texture_wrap_mode_cylindrical = 0, + + // Simply wrap back to the start. + nv3_d3d5_texture_wrap_mode_wrap = 1, + + // Mirror the texture. + nv3_d3d5_texture_wrap_mode_mirror = 2, + + // Clamp to the last border pixel. + nv3_d3d5_texture_wrap_mode_clamp = 3, +} nv3_d3d5_texture_wrap_mode; + + +/* This is blending but isn't really considered to be it in the GPU for some reason + What do we do with out input texel BEFORE processing it? + */ +typedef enum nv3_d3d5_dest_color_interpretation_e +{ + // Do nothing + nv3_d3d5_source_color_normal = 0, + + // Invert Colour + nv3_d3d5_source_color_inverse = 1, + + // Invert Alpha before Processing + nv3_d3d5_source_color_alpha_inverse = 2, + + // Take Alpha as 1 + nv3_d3d5_source_color_alpha_one = 3, + +} nv3_d3d5_dest_color_interpretation; + +// The full texture format structure +typedef struct nv3_d3d5_texture_format_s +{ + uint16_t color_key_color_mask; + bool color_key_enabled : 1; + nv3_d3d5_texture_pixel_format color_format : 2; + nv3_d3d5_texture_size size_min : 4; + nv3_d3d5_texture_size size_max : 4; +} nv3_d3d5_texture_format_t; + +// +// NV3 D3D5: INTERPOLATION +// + +/* + ?????? + Interpolating between mip levels? (or for texture blending) +*/ +typedef enum nv3_d3d5_interpolator_algorithm_e +{ + // Zero-order hold interpolation? + nv3_d3d5_interpolator_zoh = 0, + + // Zero-order hold (microsoft variant)? + nv3_d3d5_interpolator_zoh_ms = 1, + + // Full-order hold interpolation? + nv3_d3d5_interpolator_foh = 2, + +} nv3_d3d5_interpolator_algorithm; + +// +// NV3 D3D5: Z-BUFFER +// + +/* Probably the sorting algorithm */ +typedef enum nv3_d3d5_zbuffer_type_e +{ + // Sort based on linear distance + nv3_d3d5_zbuffer_linear = 0, + + // Sort based on distance from view frustum + nv3_d3d5_zbuffer_screen = 1, + +} nv3_d3d5_zbuffer_type; + +// NV3 D3D5: WRITE CONTROL (SHARED BETWEEN ZETA BUFFER AND ALPHA) +typedef enum nv3_d3d5_buffer_write_control_e +{ + // Never write. + nv3_d3d5_buffer_write_control_never = 0, + + // Only write the alpha. + nv3_d3d5_buffer_write_control_alpha = 1, + + // Write both alpha and the zeta-buffer. + nv3_d3d5_buffer_write_control_alpha_zeta = 2, + + // Write only the zeta-buffer + nv3_d3d5_buffer_write_control_zeta = 3, + + // Write everything (alpha+z+zeta?) + nv3_d3d5_buffer_write_control_always = 4, + + +} nv3_d3d5_buffer_write_control; + +// +// NV3 D3D5: BUFFER COMPARISON (SHARED BETWEEN ZETA BUFFER AND ALPHA CONTROL) +// + +// Todo: Which direction? (i.e. is less than p1 < p2 or p2 < p1!) +typedef enum nv3_d3d5_buffer_comparison_type_e +{ + // !!!ILLEGAL COMPARISON TYPE!!!! + nv3_d3d5_buffer_comparison_illegal = 0, + + // The (depth?) test always fails. + nv3_d3d5_buffer_comparison_always_false = 1, + + // True if less than fail. + nv3_d3d5_buffer_comparison_less_than = 2, + + // The test succeeds if equal. + nv3_d3d5_buffer_comparison_equal = 3, + + // The test succeeds if less or equal. + nv3_d3d5_buffer_comparison_less_or_equal = 4, + + // The test succeeds if greater. + nv3_d3d5_buffer_comparison_greater = 5, + + // The test succeeds if the two elements are equal. + nv3_d3d5_buffer_comparison_not_equal = 6, + + // The test succeeds if greater or equal + nv3_d3d5_buffer_comparison_greater_or_equal = 7, + + // The test always succeeds. + nv3_d3d5_buffer_comparison_always_true = 8, + +} nv3_d3d5_buffer_comparison_type; + +// +// NV3 D3D5: BLENDING +// + +/* Render Operation for Blending */ +typedef enum nv3_d3d5_blend_render_operation_e +{ + nv3_d3d5_blend_render_operation_and = 0, + + nv3_d3d5_blend_add_with_saturation = 1, + +} nv3_d3d5_blend_render_operation; + +typedef enum nv3_d3d5_blend_beta_factor_e +{ + nv3_d3d5_blend_beta_factor_srcalpha = 0, + + nv3_d3d5_blend_beta_factor_zero = 1, + +} nv3_d3d5_blend_beta_factor; + +typedef enum nv3_d3d5_blend_input0_e +{ + nv3_d3d5_blend_input0_srcalpha = 0, + + nv3_d3d5_blend_input0_zero = 1, + +} nv3_d3d5_blend_input0; + +typedef enum nv3_d3d5_blend_input1_e +{ + nv3_d3d5_blend_input1_destalpha = 0, + + nv3_d3d5_blend_input1_zero = 1, + +} nv3_d3d5_blend_input1; + +// +// NV3 D3D5: CULLING +// + +typedef enum nv3_d3d5_culling_algorithm_e +{ + // Don't cull + nv3_d3d5_culling_algorithm_none = 0, + + // Cull Clockwise around view frustum? + nv3_d3d5_culling_algorithm_clockwise = 1, + + // Cull counterclockwise around view frustum? + nv3_d3d5_culling_algorithm_counterclockwise = 2, + +} nv3_d3d5_culling_algorithm; + +/* Specular reflection parameters */ +typedef struct nv3_d3d5_specular_s +{ + uint8_t i0 : 4; + uint8_t i1 : 4; + uint8_t i2 : 4; + uint8_t i3 : 4; + uint8_t i4 : 4; + uint8_t i5 : 4; + uint8_t fog; //table fog emulation? +} nv3_d3d5_specular_t; + +// +// NV3 D3D5: MISC +// +typedef struct nv3_d3d5_texture_filter_s +{ + uint8_t spread_x; + uint8_t spread_y; + uint8_t mipmap; + uint8_t turbo; +} nv3_d3d5_texture_filter_t; + +// +// NV3 D3D5: OUTPUT CONTROL STRUCTURE +// + +/* Output Control for D3D5 Triangles */ +typedef struct nv3_d3d5_control_out_s +{ + nv3_d3d5_interpolator_algorithm ctrl_out_interpolator : 2; + uint8_t reserved : 2; + nv3_d3d5_texture_wrap_mode wrap_u : 2; // Controls wrapping mode of U texture coordinate + nv3_d3d5_texture_wrap_mode wrap_v : 2; // Controls wrapping move of V texture coordinate + nv3_d3d5_output_pixel_format output_pixel_format : 1; + bool reserved2 : 1; + nv3_d3d5_dest_color_interpretation dest_color_interpretation : 2; + nv3_d3d5_culling_algorithm culling_algorithm : 2; + bool reserved3 : 1; + nv3_d3d5_zbuffer_type zbuffer_type : 1; + nv3_d3d5_buffer_comparison_type zeta_buffer_compare : 4; + nv3_d3d5_buffer_write_control zeta_write : 3; + bool reserved4 : 1; + nv3_d3d5_buffer_write_control color_write : 3; + bool reserved5 : 1; + nv3_d3d5_blend_render_operation blend_rop : 1; + nv3_d3d5_blend_input0 blend_input0 : 1; + nv3_d3d5_blend_input1 blend_input1 : 1; +} nv3_d3d5_control_out_t; + +typedef struct nv3_d3d5_alpha_control_s +{ + uint8_t alpha_key; + nv3_d3d5_buffer_comparison_type zeta_buffer_compare : 4; + uint32_t reserved : 20; +} nv3_d3d5_alpha_control_t; + +// +// NV3 D3D5: Triangle Coordinates +// +typedef struct nv3_d3d5_coordinate_s +{ + nv3_d3d5_specular_t specular_reflection_parameters; + nv3_color_bgra_32_t color; // YOU HAVE TO FLIP THE ENDIANNESS. NVIDIA??? WHAT??? + + // Seems more plausible for these specifically to be floats. + // Also makes my life easier... + float x; // X coordinate in 3d space of the triangle + float y; // Y coordinate in 3d space of the triangle + float z; // Z coordinate in 3d space of the triangle + float m; // "Measurement dimension" apparently, allows for more precise measurements. And curves + float u; // U coordinate within texture for the (top left?) of the triangle where sampling starts. + float v; // V coordinate within texture for the (top left?) of the triangle where sampling starts. +} nv3_d3d5_coordinate_t; + +typedef struct nv3_object_class_017 +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1FC]; + uint32_t texture_offset; + nv3_d3d5_texture_format_t texture_format; + nv3_d3d5_texture_filter_t texture_filter; + nv3_color_argb_32_t fog_color; // Alpha is ignored here! + nv3_d3d5_control_out_t control_out; + nv3_d3d5_alpha_control_t alpha_control; + + uint8_t reserved3[0xCE4]; + nv3_d3d5_coordinate_t coordinate_points[128]; // The points wer are rendering. + /* No placeholder needed, it really is that long. */ +} nv3_d3d5_accelerated_triangle_with_zeta_buffer_t; + +/* 0x18, 0x19, 0x1A, 0x1B don't exist */ + + + /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? They are making it look like a bitfield but it's hex? @@ -535,5 +1052,17 @@ typedef enum nv3_object_class_01C_pixel_format_e nv3_m2mt_pixel_format_x8g8b8r8 = 0x1, } nv3_object_class_01C_pixel_format; +typedef struct nv3_object_class_01C +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint8_t reserved[0x100]; + uint32_t set_notify; // Set notifier + uint8_t reserved2[0x1F8]; + nv3_object_class_01C_pixel_format format; // Completely different from everything else + uint32_t pitch; // 16-bit + uint32_t linear_address; // 22-bit: Linear address in vram. + uint8_t reserved3[0x1C3F]; +} nv3_image_in_memory_t; + // TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! #pragma pack(pop) // return packing to whatever it was before this disaster \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 0b6e6b0eb..72076b64b 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -183,7 +183,6 @@ extern const device_config_t nv3_config[]; #define NV3_PMC_ENABLE_PVIDEO 28 #define NV3_PMC_ENABLE_PVIDEO_ENABLED 0x1 - #define NV3_PMC_END 0xfff // overlaps with CIO #define NV3_CIO_START 0x3b0 // Legacy SVGA Emulation Subsystem #define NV3_CIO_END 0x3df @@ -194,8 +193,10 @@ extern const device_config_t nv3_config[]; #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 From cdb8635e725828e91599b7def3e651572d9c7c95 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 6 Jan 2025 01:00:16 +0000 Subject: [PATCH 049/274] more defines... --- src/include/86box/nv/vid_nv3.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 72076b64b..5c7ad1d81 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -197,6 +197,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_INTR 0x2100 // FIFO - Interrupt Status #define NV3_PFIFO_INTR_EN 0x2140 // FIFO - Interrupt Enable +#define NV3_PFIFO_CONFIG_0 0x2200 +#define NV3_PFIFO_CONFIG_0_DMA_FETCH 8 +#define NV3_PFIFO_CONFIG_RAMHT 0x2210 + #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem #define NV3_PRM_INTR 0x4100 @@ -669,7 +673,7 @@ typedef struct nv3_pbus_s } nv3_pbus_t; // Command submission to PGRAPH -typedef struct nv_pfifo_s +typedef struct nv3_pfifo_s { uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable @@ -715,6 +719,16 @@ typedef struct nv3_pramdac_s uint32_t hserr_width; // horizontal sync error width } nv3_pramdac_t; +typedef struct nv3_pgraph_context_switch_s +{ + +} nv3_pgraph_context_switch_t; + +typedef struct nv3_pgraph_context_control_s +{ + +} nv3_pgraph_context_control_t; + // Graphics Subsystem typedef struct nv3_pgraph_s { @@ -723,6 +737,8 @@ typedef struct nv3_pgraph_s uint32_t interrupt_status_1; // Interrupt status 1 uint32_t interrupt_enable_1; // Interrupt enable 1 + nv3_pgraph_context_control_t context_control; + nv3_pgraph_context_switch_t context_user_submit; uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache } nv3_pgraph_t; From 108bba522fc25d47d9dbde3c77a726e9f8af7051 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 7 Jan 2025 23:54:56 +0000 Subject: [PATCH 050/274] some more defines --- src/include/86box/nv/vid_nv3.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 5c7ad1d81..0e80eb23b 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -199,7 +199,9 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CONFIG_0 0x2200 #define NV3_PFIFO_CONFIG_0_DMA_FETCH 8 -#define NV3_PFIFO_CONFIG_RAMHT 0x2210 +#define NV3_PFIFO_CONFIG_RAMHT 0x2210 // Hashtable for graphics objects config +#define NV3_PFIFO_CONFIG_RAMRO 0x2214 +#define NV3_PFIFO_CONFIG_RAMFC 0x2218 #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem From 19aa366806a37faeb6f0384aed59224df956d7c0 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 8 Jan 2025 01:59:50 +0000 Subject: [PATCH 051/274] Rough implementation of cyclical logging detection with minimal order 4 and refactor logging system. --- src/86box.c | 213 ++++++++++++++++++++++++++++++++++++-- src/include/86box/86box.h | 5 +- src/video/nv/nv_base.c | 2 +- 3 files changed, 206 insertions(+), 14 deletions(-) diff --git a/src/86box.c b/src/86box.c index 59143a012..fdfdcf1e8 100644 --- a/src/86box.c +++ b/src/86box.c @@ -253,12 +253,37 @@ static volatile atomic_int do_pause_ack = 0; static volatile atomic_int pause_ack = 0; #ifndef RELEASE_BUILD -static char buff[1024]; -static int seen = 0; + +#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ +#define LOG_SIZE_BUFFER_CYCLIC 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ +#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ + +static char buff[LOG_SIZE_BUFFER]; +static char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC][LOG_SIZE_BUFFER]; +static int32_t cyclic_last_line = 0; +static int32_t log_cycles = 0; + +static int seen = 0; static int suppr_seen = 1; #endif +/* + Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic +*/ +void +pclog_ensure_stdlog_open() +{ + if (stdlog == NULL) { + if (log_path[0] != '\0') { + stdlog = plat_fopen(log_path, "w"); + if (stdlog == NULL) + stdlog = stdout; + } else + stdlog = stdout; + } +} + /* * Log something to the logfile or stdout. * @@ -270,19 +295,12 @@ void pclog_ex(const char *fmt, va_list ap) { #ifndef RELEASE_BUILD - char temp[1024]; + char temp[LOG_SIZE_BUFFER]; if (strcmp(fmt, "") == 0) return; - if (stdlog == NULL) { - if (log_path[0] != '\0') { - stdlog = plat_fopen(log_path, "w"); - if (stdlog == NULL) - stdlog = stdout; - } else - stdlog = stdout; - } + pclog_ensure_stdlog_open(); vsprintf(temp, fmt, ap); if (suppr_seen && !strcmp(buff, temp)) @@ -299,6 +317,179 @@ pclog_ex(const char *fmt, va_list ap) #endif } + +/* +Starfrost, 7-8 January 2025: + +For RIVA 128 emulation I needed a way to suppress logging if a repeated pattern of the same set of lines were found. + +It doesn't need to be super fast so I don't bother to use an optimised approach. I use iterate from 1 to n/2 and see if a pattern repeats or not. + +Implements a version of the Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm +*/ +void +pclog_ex_cyclic(const char* fmt, va_list ap) +{ +#ifndef RELEASE_BUILD + char temp[LOG_SIZE_BUFFER]; + + cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC; + + vsprintf(temp, fmt, ap); + + pclog_ensure_stdlog_open(); + + strncpy(cyclic_buff[cyclic_last_line], temp, LOG_SIZE_BUFFER); + + uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC] = {0}; + + // Random numbers + uint32_t base = 257; + uint32_t mod = 1000000007; + + uint32_t repeat_order = 0; + bool is_cycle = false; + + // compute the set of hashes for the current log buffer + for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC; log_line++) + { + if (cyclic_buff[log_line][0] == '\0') + continue; // skip + + for (int32_t log_line_char = 0; log_line_char < LOG_SIZE_BUFFER; log_line_char++) + { + hashes[log_line] = hashes[log_line] * base + cyclic_buff[log_line][log_line_char] % mod; + } + } + + + // Now see if there are real cycles... + // We implement a minimum repeat size. + for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC / 2; check_size++) + { + //TODO: Log what we need for cycle 1. + //TODO: Command line option that lets us turn off this behaviour. + for (int32_t log_line_to_check = 0; log_line_to_check < check_size; log_line_to_check++) + { + if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC]) + { + repeat_order = check_size; + break; + } + } + + is_cycle = (repeat_order != 0); + + // if there still is a cycle.. + if (is_cycle) + break; + + } + + if (is_cycle) + { + if (cyclic_last_line % repeat_order == 0) + { + log_cycles++; + + if (log_cycles == 1) + fprintf(stdlog, "%s", temp); // allow normal logging + if (log_cycles > 1 && log_cycles < 100) + fprintf(stdlog, "***** Cyclical Log Repeat of Order %d #%d *****\n", repeat_order, log_cycles); + else if (log_cycles == 100) + fprintf(stdlog, "Logged the same cycle 100 times...shutting up until something interesting happens\n"); + } + } + else + { + log_cycles = 0; + fprintf(stdlog, "%s", temp); + } + + cyclic_last_line++; + + /* + This version sucks + bool is_cycle = false; + + uint32_t repeat_order = 0; + + for (int32_t log_line = 0; log_line < (LOG_SIZE_BUFFER_CYCLIC); log_line++) + { + for (int32_t check_size = 1; check_size < (LOG_SIZE_BUFFER_CYCLIC / 2); check_size++) + { + uint32_t log_to_check = log_line + check_size; + + // Loop around + if (log_to_check >= LOG_SIZE_BUFFER_CYCLIC) + log_to_check %= LOG_SIZE_BUFFER_CYCLIC; + + // find an initial repeat + if (cyclic_buff[log_line][0] != '\0' + || cyclic_buff[log_to_check][0] != '\0') + { + if (!strncmp(cyclic_buff[log_line], cyclic_buff[log_to_check], LOG_SIZE_BUFFER)) + { + // now see if there are actually repeats (loop over to the start of the log...) + + int32_t highest_real_loop = 1; + + for (int32_t loop_chk = 1; loop_chk < check_size; loop_chk++) + { + uint32_t log_to_check_1 = log_line + loop_chk; + uint32_t log_to_check_2 = (log_line + loop_chk) + check_size; + + if (log_to_check_1 >= LOG_SIZE_BUFFER_CYCLIC) + log_to_check_1 %= LOG_SIZE_BUFFER_CYCLIC; + + if (log_to_check_2 >= LOG_SIZE_BUFFER_CYCLIC) + log_to_check_2 %= LOG_SIZE_BUFFER_CYCLIC; + + if (!strncmp(cyclic_buff[log_to_check_1], cyclic_buff[log_to_check_2], LOG_SIZE_BUFFER)) + { + is_cycle = true; + repeat_order = check_size; + break; + } + } + + + } + + } + + if (is_cycle) break; + } + + if (is_cycle) break; + } + + if (is_cycle + && cyclic_last_line % repeat_order == 0) + { + log_cycles++; + } + else if (!is_cycle) + log_cycles = 0; + + + if (log_cycles <= 1) + { + strcpy(buff, temp); + fprintf(stdlog, "%s", temp); + } + else + { + if (cyclic_last_line % repeat_order == 0) + fprintf(stdlog, "***** Cyclical Log Repeat %d *****\n", log_cycles); + } + + fflush(stdlog); +*/ +#endif + +} + void pclog_toggle_suppr(void) { diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index ffa670b7e..e43310149 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -187,8 +187,9 @@ extern int config_changed; /* config has changed */ /* Function prototypes. */ #ifdef HAVE_STDARG_H -extern void pclog_ex(const char *fmt, va_list); -extern void fatal_ex(const char *fmt, va_list); +extern void pclog_ex(const char *fmt, va_list ap); +extern void pclog_ex_cyclic(const char* fmt, va_list ap); +extern void fatal_ex(const char *fmt, va_list ap); #endif extern void pclog_toggle_suppr(void); extern void pclog(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index da776a721..88cb5208b 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -34,7 +34,7 @@ void nv_log(const char *fmt, ...) if (nv_do_log) { va_start(ap, fmt); - pclog_ex(fmt, ap); + pclog_ex_cyclic(fmt, ap); va_end(ap); } } From 39419ae95638c449087dfcc9fc0bd038732b86a6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 9 Jan 2025 02:14:48 +0000 Subject: [PATCH 052/274] Initial cfg for ramfc (unused dma context storage), RAMHT --- src/86box.c | 78 ------------------------ src/include/86box/nv/vid_nv3.h | 25 ++++++-- src/video/nv/nv3/nv3_core_arbiter.c | 8 +-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 53 ++++++++++++++++ src/video/nv/nv3/subsystems/nv3_pramin.c | 13 ++++ 5 files changed, 90 insertions(+), 87 deletions(-) diff --git a/src/86box.c b/src/86box.c index fdfdcf1e8..2825535fd 100644 --- a/src/86box.c +++ b/src/86box.c @@ -408,84 +408,6 @@ pclog_ex_cyclic(const char* fmt, va_list ap) cyclic_last_line++; - /* - This version sucks - bool is_cycle = false; - - uint32_t repeat_order = 0; - - for (int32_t log_line = 0; log_line < (LOG_SIZE_BUFFER_CYCLIC); log_line++) - { - for (int32_t check_size = 1; check_size < (LOG_SIZE_BUFFER_CYCLIC / 2); check_size++) - { - uint32_t log_to_check = log_line + check_size; - - // Loop around - if (log_to_check >= LOG_SIZE_BUFFER_CYCLIC) - log_to_check %= LOG_SIZE_BUFFER_CYCLIC; - - // find an initial repeat - if (cyclic_buff[log_line][0] != '\0' - || cyclic_buff[log_to_check][0] != '\0') - { - if (!strncmp(cyclic_buff[log_line], cyclic_buff[log_to_check], LOG_SIZE_BUFFER)) - { - // now see if there are actually repeats (loop over to the start of the log...) - - int32_t highest_real_loop = 1; - - for (int32_t loop_chk = 1; loop_chk < check_size; loop_chk++) - { - uint32_t log_to_check_1 = log_line + loop_chk; - uint32_t log_to_check_2 = (log_line + loop_chk) + check_size; - - if (log_to_check_1 >= LOG_SIZE_BUFFER_CYCLIC) - log_to_check_1 %= LOG_SIZE_BUFFER_CYCLIC; - - if (log_to_check_2 >= LOG_SIZE_BUFFER_CYCLIC) - log_to_check_2 %= LOG_SIZE_BUFFER_CYCLIC; - - if (!strncmp(cyclic_buff[log_to_check_1], cyclic_buff[log_to_check_2], LOG_SIZE_BUFFER)) - { - is_cycle = true; - repeat_order = check_size; - break; - } - } - - - } - - } - - if (is_cycle) break; - } - - if (is_cycle) break; - } - - if (is_cycle - && cyclic_last_line % repeat_order == 0) - { - log_cycles++; - } - else if (!is_cycle) - log_cycles = 0; - - - if (log_cycles <= 1) - { - strcpy(buff, temp); - fprintf(stdlog, "%s", temp); - } - else - { - if (cyclic_last_line % repeat_order == 0) - fprintf(stdlog, "***** Cyclical Log Repeat %d *****\n", log_cycles); - } - - fflush(stdlog); -*/ #endif } diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 0e80eb23b..f2d28eaf9 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -199,9 +199,26 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CONFIG_0 0x2200 #define NV3_PFIFO_CONFIG_0_DMA_FETCH 8 + #define NV3_PFIFO_CONFIG_RAMHT 0x2210 // Hashtable for graphics objects config -#define NV3_PFIFO_CONFIG_RAMRO 0x2214 -#define NV3_PFIFO_CONFIG_RAMFC 0x2218 +#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS 12 +#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS_DEFAULT 0x0 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE 16 +#define NV3_PFIFO_CONFIG_RAMHT_4K 0x0 +#define NV3_PFIFO_CONFIG_RAMHT_8K 0x1 +#define NV3_PFIFO_CONFIG_RAMHT_16K 0x2 +#define NV3_PFIFO_CONFIG_RAMHT_32K 0x3 + +#define NV3_PFIFO_CONFIG_RAMFC 0x2214 +#define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS 9 +#define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS_DEFAULT 0x1C00 // Hardcoded in silicon? + +#define NV3_PFIFO_CONFIG_RAMRO 0x2218 +#define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS 9 +#define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS_DEFAULT 0x1E00 // Hardcoded in silicon? +#define NV3_PFIFO_CONFIG_RAMRO_SIZE 16 +#define NV3_PFIFO_CONFIG_RAMRO_SIZE_512B 0x0 +#define NV3_PFIFO_CONFIG_RAMRO_SIZE_8K 0x1 #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem @@ -1001,8 +1018,8 @@ 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); +uint32_t nv3_pramin_arbitrate_read(uint32_t address); +void nv3_pramin_arbitrate_write(uint32_t address, uint32_t value); // TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_pramin_* // GPU subsystems diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index ee8fe048d..22da49b20 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -104,7 +104,7 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t 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 + ret = nv3_pramin_arbitrate_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); @@ -167,7 +167,7 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t 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 + nv3_pramin_arbitrate_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); @@ -201,6 +201,4 @@ 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 +void nv3_user_write(uint32_t address, uint32_t 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 index 97d5e6f0e..931244b20 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -37,6 +37,9 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_INTR, "PFIFO - Interrupt Status", NULL, NULL}, { NV3_PFIFO_INTR_EN, "PFIFO - Interrupt Enable", NULL, NULL,}, + { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, + { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, + { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -90,6 +93,16 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_INTR_EN: ret = nv3->pfifo.interrupt_enable; break; + // These may need to become functions. + case NV3_PFIFO_CONFIG_RAMFC: + ret = nv3->pfifo.ramfc_config; + break; + case NV3_PFIFO_CONFIG_RAMHT: + ret = nv3->pfifo.ramht_config; + break; + case NV3_PFIFO_CONFIG_RAMRO: + ret = nv3->pfifo.ramro_config; + break; } } @@ -149,6 +162,46 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_INTR_EN: nv3->pbus.interrupt_enable = value & 0x00001111; break; + case NV3_PFIFO_CONFIG_RAMHT: + nv3->pfifo.ramht_config = value; +// This code sucks a bit fix it later +#ifdef ENABLE_NV_LOG + uint32_t new_size_ramht = ((value >> 16) & 0x03); + + if (new_size_ramht == 0) + new_size_ramht = 0x1000; + else if (new_size_ramht == 1) + new_size_ramht = 0x2000; + else if (new_size_ramht == 2) + new_size_ramht = 0x4000; + else if (new_size_ramht == 3) + new_size_ramht = 0x8000; + + nv_log("NV3: RAMHT Reconfiguration\n" + "Base Address in RAMIN: %d\n" + "Size: 0x%08x bytes\n", (value >> 12) & 0x1f, new_size_ramht); +#endif + break; + case NV3_PFIFO_CONFIG_RAMFC: + nv3->pfifo.ramfc_config = value; + + nv_log("NV3: RAMFC Reconfiguration\n" + "Base Address in RAMIN: %d\n", (value >> 12) & 0x1f); + break; + case NV3_PFIFO_CONFIG_RAMRO: + nv3->pfifo.ramro_config = value; + + uint32_t new_size_ramro = ((value >> 16) & 0x01); + + if (new_size_ramro == 0) + new_size_ramro = 0x200; + else if (new_size_ramro == 1) + new_size_ramro = 0x2000; + + nv_log("NV3: RAMRO Reconfiguration\n" + "Base Address in RAMIN: %d\n" + "Size: 0x%08x bytes\n", (value >> 12) & 0x1f, new_size_ramro); + break; } } } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index a6561be9d..c5093a175 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -147,3 +147,16 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) nv_log("NV3: Write dword to RAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } + +/* +Arbitrates reads and writes to RAMFC, RAMRO, RAMHT and generic RAMIN +*/ +uint32_t nv3_pramin_arbitrate_read(uint32_t address) +{ + return 0; +} + +void nv3_pramin_arbitrate_write(uint32_t address, uint32_t value) +{ + +} \ No newline at end of file From 4778e86273e29158a8b9ed3002f535b3cfec7fa6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 11 Jan 2025 02:24:12 +0000 Subject: [PATCH 053/274] Initial progress at RAMRO, RAMHT and RAMFC --- src/include/86box/nv/vid_nv3.h | 26 ++- src/video/nv/nv3/nv3_core_arbiter.c | 5 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 6 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 196 ++++++++++++++++-- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 9 + .../nv/nv3/subsystems/nv3_pramin_ramht.c | 15 +- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 9 + 7 files changed, 232 insertions(+), 34 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index f2d28eaf9..e831de61f 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -7,7 +7,7 @@ * This file is part of the 86Box distribution. * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated 2 January 2025 (STILL WORKING ON IT) + * Last updated 9 January 2025 (STILL WORKING ON IT) * * * @@ -204,10 +204,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS 12 #define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS_DEFAULT 0x0 #define NV3_PFIFO_CONFIG_RAMHT_SIZE 16 -#define NV3_PFIFO_CONFIG_RAMHT_4K 0x0 -#define NV3_PFIFO_CONFIG_RAMHT_8K 0x1 -#define NV3_PFIFO_CONFIG_RAMHT_16K 0x2 -#define NV3_PFIFO_CONFIG_RAMHT_32K 0x3 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE_4K 0x0 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE_8K 0x1 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE_16K 0x2 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE_32K 0x3 #define NV3_PFIFO_CONFIG_RAMFC 0x2214 #define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS 9 @@ -578,7 +578,7 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_STANDARDVGA_END 0x18 -// These are nvidia (25-63) +// These are nvidia, licensed from weitek (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 @@ -865,7 +865,7 @@ typedef struct nv3_pramin_ramht_s nv3_pramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; } nv3_pramin_ramht_t; -uint32_t nv3_pramin_ramht_hash(nv3_pramin_name_t name, uint32_t channel); +uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel); // Anti-fuckup device typedef struct nv3_pramin_ramro_s @@ -963,6 +963,16 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv); 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 +bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value); // Read arbitration so we can read/write to the structures in the first 64k of ramin +bool nv3_pramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin + +uint32_t nv3_ramfc_read(uint32_t address); +void nv3_ramfc_write(uint32_t address, uint32_t value); +uint32_t nv3_ramro_read(uint32_t address); +void nv3_ramro_write(uint32_t address, uint32_t value); +uint32_t nv3_ramht_read(uint32_t address); +void nv3_ramht_write(uint32_t address, uint32_t value); + // MMIO Arbitration // Determine where the hell in this mess our reads or writes are going uint32_t nv3_mmio_arbitrate_read(uint32_t address); @@ -1018,8 +1028,6 @@ 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_arbitrate_read(uint32_t address); -void nv3_pramin_arbitrate_write(uint32_t address, uint32_t value); // TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_pramin_* // GPU subsystems diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 22da49b20..bab378b9f 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -103,8 +103,9 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) 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_arbitrate_read(address); // RAMHT, RAMFC, RAMRO etc dettermined by nv3_ramin_* function + // RAMIN is handled by a separate memory mapping in PCI BAR1 + //else if (address >= NV3_PRAMIN_START && address <= NV3_PRAMIN_END) + //ret = nv3_pramin_arbitrate_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); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 931244b20..1838faef5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -179,14 +179,14 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv_log("NV3: RAMHT Reconfiguration\n" "Base Address in RAMIN: %d\n" - "Size: 0x%08x bytes\n", (value >> 12) & 0x1f, new_size_ramht); + "Size: 0x%08x bytes\n", ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12, new_size_ramht); #endif break; case NV3_PFIFO_CONFIG_RAMFC: nv3->pfifo.ramfc_config = value; nv_log("NV3: RAMFC Reconfiguration\n" - "Base Address in RAMIN: %d\n", (value >> 12) & 0x1f); + "Base Address in RAMIN: %d\n", ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9); break; case NV3_PFIFO_CONFIG_RAMRO: nv3->pfifo.ramro_config = value; @@ -200,7 +200,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv_log("NV3: RAMRO Reconfiguration\n" "Base Address in RAMIN: %d\n" - "Size: 0x%08x bytes\n", (value >> 12) & 0x1f, new_size_ramro); + "Size: 0x%08x bytes\n", ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9, new_size_ramro); break; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index c5093a175..35fa80603 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -48,11 +48,15 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) addr ^= (nv3->nvbase.svga.vram_max- 0x10); - uint8_t val = nv3->nvbase.svga.vram[addr]; + uint32_t val = 0x00; - nv_log("NV3: Read byte from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + if (!nv3_pramin_arbitrate_read(addr, &val)) // Oh well + { + val = (uint8_t)nv3->nvbase.svga.vram[addr]; + nv_log("NV3: Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + } - return val; + return (uint8_t)val; } // Read 16-bit ramin @@ -68,9 +72,13 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 1; // what - uint16_t val = vram_16bit[addr]; // what + uint32_t val = 0x00; - nv_log("NV3: Read word from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + if (!nv3_pramin_arbitrate_read(addr, &val)) + { + val = (uint16_t)vram_16bit[addr]; + nv_log("NV3: Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + } return val; } @@ -88,9 +96,14 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what - uint32_t val = vram_32bit[addr]; + uint32_t val = 0x00; - nv_log("NV3: Read dword from RAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + if (!nv3_pramin_arbitrate_read(addr, &val)) + { + val = vram_32bit[addr]; + + nv_log("NV3: Read dword from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + } return val; } @@ -107,9 +120,15 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) // 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - nv3->nvbase.svga.vram[addr] = val; + uint32_t val32 = 0x00; + + if (!nv3_pramin_arbitrate_write(addr, val32)) + { + nv3->nvbase.svga.vram[addr] = val; + nv_log("NV3: Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); + } + - nv_log("NV3: Write byte to RAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); } // Write 16-bit ramin @@ -125,9 +144,15 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 1; // what - vram_16bit[addr] = val; + uint32_t val32 = 0x00; + + if (!nv3_pramin_arbitrate_write(addr, val32)) + { + vram_16bit[addr] = val; + nv_log("NV3: Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); + } + - nv_log("NV3: Write word to RAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); } // Write 32-bit ramin @@ -143,20 +168,155 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what - vram_32bit[addr] = val; + uint32_t val32 = 0x00; + + if (!nv3_pramin_arbitrate_write(addr, val32)) + { + vram_32bit[addr] = val; + nv_log("NV3: Write dnv3_pramin_arbitrate_readword to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); + } - nv_log("NV3: Write dword to RAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } /* -Arbitrates reads and writes to RAMFC, RAMRO, RAMHT and generic RAMIN +RAMIN access arbitration functions +Arbitrates reads and writes to RAMFC (unused dma context storage), RAMRO (invalid object submission location), RAMHT (hashtable for graphics objectstorage) (RAMAU?) +and generic RAMIN + +Takes a pointer to a result integer. This is because we need to check its result in our normal write function. +Returns true if a valid "non-generic" address was found (e.g. RAMFC/RAMRO/RAMHT). False if the specified address is a generic RAMIN address */ -uint32_t nv3_pramin_arbitrate_read(uint32_t address) +bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value) { - return 0; + if (!nv3) return 0x00; + + uint32_t ramht_size = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03); + uint32_t ramro_size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + + // Get the addresses of RAMHT, RAMFC, RAMRO + // They must be within first 64KB of PRAMIN! + uint32_t ramht_start = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12; // Must be 0x1000 aligned + uint32_t ramfc_start = ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned + uint32_t ramro_start = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned + + // Calculate the RAMHT and RAMRO end points. + // (RAMFC is always 0x1000 bytes on NV3.) + uint32_t ramht_end = ramht_start; + uint32_t ramfc_end = ramfc_start + 0x1000; + uint32_t ramro_end = ramro_start; + + switch (ramht_size) + { + case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_0; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_1; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_2; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_3; + break; + } + + switch (ramro_size) + { + case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: + ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_0; + break; + case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: + ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_1; + break; + } + + if (address >= ramht_start + && address <= ramht_end) + { + *value = nv3_ramht_read(address); + return true; + } + else if (address >= ramfc_start + && address <= ramfc_end) + { + *value = nv3_ramfc_read(address); + return true; + } + else if (address >= ramro_start + && address <= ramro_end) + { + *value = nv3_ramro_read(address); + return true; + } + + /* temp */ + return false; } -void nv3_pramin_arbitrate_write(uint32_t address, uint32_t value) +bool nv3_pramin_arbitrate_write(uint32_t address, uint32_t value) { - + if (!nv3) return 0x00; + + uint32_t ramht_size = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03); + uint32_t ramro_size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + + // Get the addresses of RAMHT, RAMFC, RAMRO + // They must be within first 64KB of PRAMIN! + uint32_t ramht_start = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12; // Must be 0x1000 aligned + uint32_t ramfc_start = ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned + uint32_t ramro_start = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned + + // Calculate the RAMHT and RAMRO end points. + // (RAMFC is always 0x1000 bytes on NV3.) + uint32_t ramht_end = ramht_start; + uint32_t ramfc_end = ramfc_start + 0x1000; + uint32_t ramro_end = ramro_start; + + switch (ramht_size) + { + case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_0; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_1; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_2; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: + ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_3; + break; + } + + switch (ramro_size) + { + case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: + ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_0; + break; + case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: + ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_1; + break; + } + + if (address >= ramht_start + && address <= ramht_end) + { + nv3_ramht_write(address, value); + return true; + } + else if (address >= ramfc_start + && address <= ramfc_end) + { + nv3_ramfc_write(address, value); + return true; + } + else if (address >= ramro_start + && address <= ramro_end) + { + nv3_ramro_write(address, value); + return true; + } + + return false; } \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 5c6d3a54c..e06445755 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -28,3 +28,12 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> +uint32_t nv3_ramfc_read(uint32_t address) +{ + nv_log("NV3: RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); +} + +void nv3_ramfc_write(uint32_t address, uint32_t value) +{ + nv_log("NV3: RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED", value, address); +} \ 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 index b77f18729..ff72e811c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -32,9 +32,20 @@ It is used to get the offset within RAMHT of a graphics object. */ -uint32_t nv3_pramin_ramht_hash(nv3_pramin_name_t name, uint32_t channel) +uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel) { uint32_t hash = (name.byte_high ^ name.byte_mid2 ^ name.byte_mid1 ^ name.byte_low ^ (uint8_t)channel); nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)", name, channel); return hash; -} \ No newline at end of file +} + + +uint32_t nv3_ramht_read(uint32_t address) +{ + nv_log("NV3: RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); +} + +void nv3_ramht_write(uint32_t address, uint32_t value) +{ + nv_log("NV3: RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED", value, address); +} diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 865bc46ec..6176e191a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -28,3 +28,12 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> +uint32_t nv3_ramro_read(uint32_t address) +{ + nv_log("NV3: RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); +} + +void nv3_ramro_write(uint32_t address, uint32_t value) +{ + nv_log("NV3: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)", value, address); +} \ No newline at end of file From 543487be43aa0af9ce5a0f0df5046922ecca59a0 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 00:55:23 +0000 Subject: [PATCH 054/274] Ramout enum. More descriptive names for the crtc registers in mmio --- src/include/86box/nv/vid_nv3.h | 38 ++++++++++++++++++++++++++++------ src/video/nv/nv3/nv3_core.c | 15 +++++--------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index e831de61f..a7780b07e 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -451,10 +451,10 @@ extern const device_config_t nv3_config[]; #define NV3_PRMCIO_START 0x601000 // Following four are CRTC+I2C access registers // and get redirected to VGA -#define NV3_PRMCIO_CRX_MONO 0x6013B4 -#define NV3_PRMCIO_CR_MONO 0x6013B5 -#define NV3_PRMCIO_CRX_COLOR 0x6013D4 -#define NV3_PRMCIO_CR_COLOR 0x6013D5 +#define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO 0x6013B4 // Current CRTC Register Index - Monochrome +#define NV3_PRMCIO_CRTC_REGISTER_CUR_MONO 0x6013B5 // Currently Selected CRTC Register - Monochrome +#define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR 0x6013D4 // Current CRTC Register Index - Colour +#define NV3_PRMCIO_CRTC_REGISTER_CUR_COLOR 0x6013D5 #define NV3_PRMCIO_END 0x601FFF #define NV3_PDAC_START 0x680000 // OPTIONAL external DAC @@ -825,7 +825,7 @@ typedef struct nv3_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; -typedef struct nv3_pramin_name_s +typedef struct nv3_pramin_name_sd { union { @@ -843,7 +843,7 @@ typedef struct nv3_pramin_context_s { uint32_t context; uint8_t dma_channel; - uint8_t render_object; //0=sw, 1=render + uint8_t render_object; //0=sw, 1=hw accelerated render uint8_t class_id; uint8_t ramin_offset; //find }; @@ -867,6 +867,32 @@ typedef struct nv3_pramin_ramht_s uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel); +typedef enum nv3_pramin_ramro_reason_e +{ + nv3_runout_reason_illegal_access = 0, + + // PFIFO CACHE0/CACHE1 were turned off, so the graphics object could not be processed. + nv3_runout_reason_no_cache_available = 1, + + // Ran out of CACHE0 & CACHE1 space. + nv3_runout_reason_cache_ran_out = 2, + + nv3_runout_reason_free_count_overrun = 3, + + nv3_runout_reason_caught_lying = 4, + + // Access reserved by pagetable + nv3_runout_reason_reserved_access = 5, + +} nv3_pramin_ramro_reason; + +/* This is a gigantic error handling system */ +typedef struct nv3_pramin_ramro_entry_s +{ + + //todo +} nv3_pramin_ramro_entry_t; + // Anti-fuckup device typedef struct nv3_pramin_ramro_s { diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 6114749a6..b7b3319aa 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -45,10 +45,10 @@ bool nv3_is_svga_redirect_address(uint32_t addr) { return (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO); + || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_COLOR + || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR + || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_MONO + || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO); } // All MMIO regs are 32-bit i believe internally @@ -164,12 +164,7 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) addr &= 0xFFFFFF; // This is weitek vga stuff - if (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CR_COLOR - || addr == NV3_PRMCIO_CRX_COLOR - || addr == NV3_PRMCIO_CR_MONO - || addr == NV3_PRMCIO_CRX_MONO) + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; From 2cd01db878dda5b95cc593cafcc32e9fd26d225b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 00:58:43 +0000 Subject: [PATCH 055/274] initial attempt to "replay" log entries --- src/86box.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/86box.c b/src/86box.c index 2825535fd..f15ca4de3 100644 --- a/src/86box.c +++ b/src/86box.c @@ -254,12 +254,12 @@ static volatile atomic_int pause_ack = 0; #ifndef RELEASE_BUILD -#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ -#define LOG_SIZE_BUFFER_CYCLIC 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ -#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ +#define LOG_SIZE_BUFFER 1024 /* Log size buffer */ +#define LOG_SIZE_BUFFER_CYCLIC_LINES 32 /* Cyclic log size buffer (number of lines that should be cehcked) */ +#define LOG_MINIMUM_REPEAT_ORDER 4 /* Minimum repeat size */ static char buff[LOG_SIZE_BUFFER]; -static char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC][LOG_SIZE_BUFFER]; +static char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; static int32_t cyclic_last_line = 0; static int32_t log_cycles = 0; @@ -333,7 +333,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) #ifndef RELEASE_BUILD char temp[LOG_SIZE_BUFFER]; - cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC; + cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC_LINES; vsprintf(temp, fmt, ap); @@ -341,7 +341,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) strncpy(cyclic_buff[cyclic_last_line], temp, LOG_SIZE_BUFFER); - uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC] = {0}; + uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC_LINES] = {0}; // Random numbers uint32_t base = 257; @@ -351,7 +351,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) bool is_cycle = false; // compute the set of hashes for the current log buffer - for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC; log_line++) + for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC_LINES; log_line++) { if (cyclic_buff[log_line][0] == '\0') continue; // skip @@ -365,13 +365,13 @@ pclog_ex_cyclic(const char* fmt, va_list ap) // Now see if there are real cycles... // We implement a minimum repeat size. - for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC / 2; check_size++) + for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER; check_size < LOG_SIZE_BUFFER_CYCLIC_LINES / 2; check_size++) { //TODO: Log what we need for cycle 1. //TODO: Command line option that lets us turn off this behaviour. for (int32_t log_line_to_check = 0; log_line_to_check < check_size; log_line_to_check++) { - if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC]) + if (hashes[log_line_to_check] == hashes[(log_line_to_check + check_size) % LOG_SIZE_BUFFER_CYCLIC_LINES]) { repeat_order = check_size; break; @@ -393,7 +393,22 @@ pclog_ex_cyclic(const char* fmt, va_list ap) log_cycles++; if (log_cycles == 1) + { + // 'Replay' the last few log entries so they actually show up + // Todo: is this right? + + for (uint32_t index = cyclic_last_line - 1; index > (cyclic_last_line - repeat_order); cyclic_last_line--) + { + // *very important* to prevent out of bounds index + uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; + fprintf(stdlog, "%s", temp); + + } + fprintf(stdlog, "%s", temp); // allow normal logging + } + + if (log_cycles > 1 && log_cycles < 100) fprintf(stdlog, "***** Cyclical Log Repeat of Order %d #%d *****\n", repeat_order, log_cycles); else if (log_cycles == 100) From 1127400b1a97eaf61dfa2375c49c28430395e903 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 02:11:30 +0000 Subject: [PATCH 056/274] many many more pgraph structs --- src/86box.c | 2 - .../86box/nv/classes/vid_nv3_classes.h | 26 +++++-- src/include/86box/nv/vid_nv3.h | 69 ++++++++++++++++--- src/video/nv/nv3/classes/nv3_class_names.c | 5 +- 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/86box.c b/src/86box.c index f15ca4de3..560bd0a3d 100644 --- a/src/86box.c +++ b/src/86box.c @@ -323,8 +323,6 @@ Starfrost, 7-8 January 2025: For RIVA 128 emulation I needed a way to suppress logging if a repeated pattern of the same set of lines were found. -It doesn't need to be super fast so I don't bother to use an optimised approach. I use iterate from 1 to n/2 and see if a pattern repeats or not. - Implements a version of the Rabin-Karp algorithm https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm */ void diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index a22871beb..d63394da8 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -75,7 +75,17 @@ typedef struct nv3_color_argb_32_s uint8_t a; } nv3_color_argb_32_t; -/* A4R4G4B4 */ +/* 30-bit colour format for internal PGRAPH use */ +typedef struct nv3_color_x3a10g10b10_s +{ + uint8_t reserved : 2; + bool alpha_if_chroma_key_otherwise_reserved2 : 1; // 1-bit ALPHA if chroma key, OTHERWISE USELESS and IGNORE + uint16_t r : 10; + uint16_t g : 10; + uint16_t b : 10; +} nv3_color_x3a10g10b10_t; + +/* 16-bit A4R4G4B4 colour format */ typedef struct nv3_color_16_a4r4g4b4_s { uint8_t a : 4; @@ -108,16 +118,25 @@ typedef struct nv3_position_16_s { uint32_t pos; - struct vid_nv3_classes + struct { uint16_t y; uint16_t x; }; - } position; } nv3_position_16_t; +/* A big position format with 30:16 = y, 15:11 = nothing, 10:0 = x */ +typedef struct nv3_position_16_bigy_s +{ + // WHOSE IDEA WAS THIS? + uint16_t x : 11; + uint8_t reserved : 5; + uint16_t y : 15; + bool reserved2 : 1; +} nv3_position_16_bigy_t; + /* Generic 16-bit size */ typedef struct nv3_size_16_s { @@ -130,7 +149,6 @@ typedef struct nv3_size_16_s uint16_t h; uint16_t w; }; - } size; } nv3_size_16_t; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a7780b07e..b1d7556f6 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -5,18 +5,24 @@ * system designs based on the PCI bus. * * This file is part of the 86Box distribution. - * - * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated 9 January 2025 (STILL WORKING ON IT) - * - * + * + * Welcome to what happens when a single person demands that their overly complicated "vision" of a design be implemented + * with absolutely no compromise. This is true lunacy. + * + * Explanation of what is being done here, and how this all works, hopefully posted on the 86Box blog. + * Notes specific to a subsystem in the header or c file for that subsystem + * Also check the doc folder for some more notres + * + * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) + * Last updated: 12 January 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * * Copyright 2024-2025 Connor Hyde */ - +#pragma once +#include <86Box/nv/classes/vid_nv3_classes.h> // The GPU base structure extern const device_config_t nv3_config[]; @@ -738,19 +744,42 @@ typedef struct nv3_pramdac_s uint32_t hserr_width; // horizontal sync error width } nv3_pramdac_t; +/* Holds DMA context channel information */ typedef struct nv3_pgraph_context_switch_s { - + /* TODO */ } nv3_pgraph_context_switch_t; typedef struct nv3_pgraph_context_control_s { - + /* TODO */ } nv3_pgraph_context_control_t; +/* DMA object context info + Context uploaded from CACHE0/CACH1 by DMA Puller +*/ +typedef struct nv3_pgraph_context_user_s +{ + bool reserved3 : 1; + uint8_t channel : 7; + uint8_t reserved2 : 4; + uint8_t class : 5; + uint8_t subchannel : 3; + uint16_t reserved : 12; +} nv3_pgraph_context_user_t; + +typedef struct nv3_pgraph_dma_settings_s +{ + /* TODO */ +} nv3_pgraph_dma_settings_t; + // Graphics Subsystem typedef struct nv3_pgraph_s { + uint32_t debug_0; + uint32_t debug_1; + uint32_t debug_2; + uint32_t debug_3; uint32_t interrupt_status_0; // Interrupt status 0 uint32_t interrupt_enable_0; // Interrupt enable 0 uint32_t interrupt_status_1; // Interrupt status 1 @@ -758,7 +787,29 @@ typedef struct nv3_pgraph_s nv3_pgraph_context_control_t context_control; nv3_pgraph_context_switch_t context_user_submit; - uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache + nv3_pgraph_context_user_t context_user; + uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache (nv3_pgraph_context_user_t array?) + + // UCLIP stuff + uint32_t abs_uclip_xmin; + uint32_t abs_uclip_xmax; + uint32_t abs_uclip_ymin; + uint32_t abs_uclip_ymax; + nv3_position_16_bigy_t src_canvas_min; + nv3_position_16_bigy_t src_canvas_max; + nv3_position_16_bigy_t dst_canvas_min; + nv3_position_16_bigy_t dst_canvas_max; + nv3_color_x3a10g10b10_t pattern_color_0_0; + uint32_t pattern_color_0_1; // only 7:0 relevant + nv3_color_x3a10g10b10_t pattern_color_1_0; + uint32_t pattern_color_1_1; // only 7:0 relevant + uint32_t pattern_bitmap_high; // high part of pattern bitmap for blit + uint32_t pattern_bitmap_low; + uint32_t pattern_shape; // may need to be an enum - 0=8x8, 1=64x1, 2=1x64 + uint32_t plane_mask; // only 7:0 relevant + nv3_color_x3a10g10b10_t chroma_key; // color key + nv3_pgraph_dma_settings_t dma_settings; + } nv3_pgraph_t; // GPU Manufacturing Configuration (again) diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index 48b85cab4..3124f727c 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -15,10 +15,7 @@ * Copyright 2024-2025 starfrost */ -/* Taken from Win9x drivers 0.77, these had the best reversing potential */ - -#include <86Box/nv/classes/vid_nv3_classes.h> - +#include <86Box/nv/vid_nv3.h> /* These are the object classes AS RECOGNISED BY THE GRAPHICS HARDWARE. */ /* The drivers implement a COMPLETELY DIFFERENT SET OF CLASSES. */ From ced6c58616bbb189469d4e8b8b7edce9158c98b7 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 02:15:22 +0000 Subject: [PATCH 057/274] whoops... --- src/86box.c | 2 +- src/include/86box/nv/vid_nv3.h | 2 +- src/video/nv/nv3/classes/nv3_class_names.c | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/86box.c b/src/86box.c index 560bd0a3d..8450c8b6b 100644 --- a/src/86box.c +++ b/src/86box.c @@ -395,7 +395,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) // 'Replay' the last few log entries so they actually show up // Todo: is this right? - for (uint32_t index = cyclic_last_line - 1; index > (cyclic_last_line - repeat_order); cyclic_last_line--) + for (uint32_t index = cyclic_last_line - 1; index > (cyclic_last_line - repeat_order); index--) { // *very important* to prevent out of bounds index uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index b1d7556f6..15efb9562 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -809,7 +809,7 @@ typedef struct nv3_pgraph_s uint32_t plane_mask; // only 7:0 relevant nv3_color_x3a10g10b10_t chroma_key; // color key nv3_pgraph_dma_settings_t dma_settings; - + uint32_t beta_factor; } nv3_pgraph_t; // GPU Manufacturing Configuration (again) diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index 3124f727c..2b38afbb4 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -14,7 +14,15 @@ * * Copyright 2024-2025 starfrost */ - +#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> /* These are the object classes AS RECOGNISED BY THE GRAPHICS HARDWARE. */ From e2d875b75fb926ff35924dc3e25f81bc6b8bf2d4 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 12 Jan 2025 15:01:27 +0000 Subject: [PATCH 058/274] notifier shit --- src/include/86box/nv/vid_nv3.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 15efb9562..8c4daf0e1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -664,6 +664,12 @@ typedef struct nv3_pci_config_s uint8_t int_line; } nv3_pci_config_t; +/* Notifier Engine */ +typedef struct nv3_notifier_s +{ + /* TODO */ +} nv3_notifier_t; + // add enums for eac // Chip configuration typedef struct nv3_straps_s @@ -773,6 +779,11 @@ typedef struct nv3_pgraph_dma_settings_s /* TODO */ } nv3_pgraph_dma_settings_t; +typedef struct nv3_pgraph_clip_misc_settings_s +{ + /* TODO */ +} nv3_pgraph_clip_misc_settings_t; + // Graphics Subsystem typedef struct nv3_pgraph_s { @@ -808,8 +819,10 @@ typedef struct nv3_pgraph_s uint32_t pattern_shape; // may need to be an enum - 0=8x8, 1=64x1, 2=1x64 uint32_t plane_mask; // only 7:0 relevant nv3_color_x3a10g10b10_t chroma_key; // color key - nv3_pgraph_dma_settings_t dma_settings; uint32_t beta_factor; + nv3_pgraph_dma_settings_t dma_settings; + nv3_pgraph_clip_misc_settings_t clip_misc_settings; + nv3_notifier_t notifier; } nv3_pgraph_t; // GPU Manufacturing Configuration (again) From cefc313a3983311647f46a7a8a6062cff324d2dd Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 14 Jan 2025 00:01:04 +0000 Subject: [PATCH 059/274] patches from p0 --- src/86box.c | 10 +++++++--- src/include/86box/nv/vid_nv_rivatimer.h | 8 ++++---- src/timer.c | 2 +- src/video/nv/nv_rivatimer.c | 18 +++++++++--------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/86box.c b/src/86box.c index 8450c8b6b..85cbb9957 100644 --- a/src/86box.c +++ b/src/86box.c @@ -266,14 +266,17 @@ static int32_t log_cycles = 0; static int seen = 0; static int suppr_seen = 1; + +// Functions only used in this translation unit +void pclog_ensure_stdlog_open(void); #endif /* Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic */ -void -pclog_ensure_stdlog_open() +void pclog_ensure_stdlog_open(void) { +#ifndef RELEASE_BUILD if (stdlog == NULL) { if (log_path[0] != '\0') { stdlog = plat_fopen(log_path, "w"); @@ -282,6 +285,7 @@ pclog_ensure_stdlog_open() } else stdlog = stdout; } +#endif } /* @@ -399,7 +403,7 @@ pclog_ex_cyclic(const char* fmt, va_list ap) { // *very important* to prevent out of bounds index uint32_t real_index = index % LOG_SIZE_BUFFER_CYCLIC_LINES; - fprintf(stdlog, "%s", temp); + fprintf(stdlog, "%s", cyclic_buff[real_index]); } diff --git a/src/include/86box/nv/vid_nv_rivatimer.h b/src/include/86box/nv/vid_nv_rivatimer.h index d5bb86b90..59f6cfebf 100644 --- a/src/include/86box/nv/vid_nv_rivatimer.h +++ b/src/include/86box/nv/vid_nv_rivatimer.h @@ -47,7 +47,7 @@ So I decided to create this timer that is completely separate from the CPU Core. #include #include #include -#include <86Box\86box.h> +#include <86box/86box.h> #ifdef _WIN32 #include @@ -63,7 +63,7 @@ typedef struct rivatimer_s double value; // The current value of the rivatimer bool running; // Is this RivaTimer running? struct rivatimer_s* next; // Next RivaTimer - void (*callback)(); // Callback to call on fire + void (*callback)(double real_time); // Callback to call on fire #ifdef _WIN32 LARGE_INTEGER starting_time; // Starting time. #else @@ -72,11 +72,11 @@ typedef struct rivatimer_s double time; // Accumulated time in uS. } rivatimer_t; -void rivatimer_init(); // Initialise the Rivatimer. +void rivatimer_init(void); // Initialise the Rivatimer. rivatimer_t* rivatimer_create(double period, void (*callback)(double real_time)); void rivatimer_destroy(rivatimer_t* rivatimer_ptr); -void rivatimer_update_all(); +void rivatimer_update_all(void); void rivatimer_start(rivatimer_t* rivatimer_ptr); void rivatimer_stop(rivatimer_t* rivatimer_ptr); double rivatimer_get_time(rivatimer_t* rivatimer_ptr); diff --git a/src/timer.c b/src/timer.c index 2b92a1958..a3a7c9efb 100644 --- a/src/timer.c +++ b/src/timer.c @@ -4,7 +4,7 @@ #include #include <86box/86box.h> #include <86box/timer.h> -#include <86Box/nv/vid_nv_rivatimer.h> +#include <86box/nv/vid_nv_rivatimer.h> uint64_t TIMER_USEC; uint32_t timer_target; diff --git a/src/video/nv/nv_rivatimer.c b/src/video/nv/nv_rivatimer.c index 343ec02ff..12d40c026 100644 --- a/src/video/nv/nv_rivatimer.c +++ b/src/video/nv/nv_rivatimer.c @@ -22,7 +22,7 @@ Since Windows XP, QueryPerformanceCounter and QueryPerformanceFrequency cannot f */ -#include <86Box/nv/vid_nv_rivatimer.h> +#include <86box/nv/vid_nv_rivatimer.h> #ifdef _WIN32 LARGE_INTEGER performance_frequency; @@ -34,7 +34,7 @@ rivatimer_t* rivatimer_tail; // The tail of the rivatimer list. /* Functions only used in this translation unit */ bool rivatimer_really_exists(rivatimer_t* rivatimer); // Determine if a rivatimer really exists in the linked list. -void rivatimer_init() +void rivatimer_init(void) { // Destroy all the rivatimers. rivatimer_t* rivatimer_ptr = rivatimer_head; @@ -120,7 +120,7 @@ bool rivatimer_really_exists(rivatimer_t* rivatimer) void rivatimer_destroy(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_destroy: The timer was already destroyed, or never existed in the first place."); // Case: We are destroying the head if (rivatimer_ptr == rivatimer_head) @@ -162,7 +162,7 @@ void rivatimer_destroy(rivatimer_t* rivatimer_ptr) rivatimer_ptr = NULL; //explicitly set to null } -void rivatimer_update_all() +void rivatimer_update_all(void) { rivatimer_t* rivatimer_ptr = rivatimer_head; @@ -221,7 +221,7 @@ void rivatimer_update_all() void rivatimer_start(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_start: The timer has been destroyed, or never existed in the first place."); if (rivatimer_ptr->period <= 0) fatal("rivatimer_start: Zero period!"); @@ -239,7 +239,7 @@ void rivatimer_start(rivatimer_t* rivatimer_ptr) void rivatimer_stop(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_stop: The timer has been destroyed, or never existed in the first place."); rivatimer_ptr->running = false; rivatimer_ptr->time = 0; @@ -249,7 +249,7 @@ void rivatimer_stop(rivatimer_t* rivatimer_ptr) double rivatimer_get_time(rivatimer_t* rivatimer_ptr) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_get_time: The timer has been destroyed, or never existed in the first place."); return rivatimer_ptr->time; } @@ -257,7 +257,7 @@ double rivatimer_get_time(rivatimer_t* rivatimer_ptr) void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double real_time)) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_set_callback: The timer has been destroyed, or never existed in the first place."); if (!callback) fatal("rivatimer_set_callback: No callback!"); @@ -268,7 +268,7 @@ void rivatimer_set_callback(rivatimer_t* rivatimer_ptr, void (*callback)(double void rivatimer_set_period(rivatimer_t* rivatimer_ptr, double period) { if (!rivatimer_really_exists(rivatimer_ptr)) - fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place. Punch starfrost in the face"); + fatal("rivatimer_set_period: The timer has been destroyed, or never existed in the first place."); rivatimer_ptr->period = period; } \ No newline at end of file From 1c25e61896472e96683b6b8de4dcfa23dfe07467 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 15 Jan 2025 01:52:33 +0000 Subject: [PATCH 060/274] Move to new logging system. --- src/include/86box/nv/vid_nv.h | 6 ++++-- src/video/nv/nv3/nv3_core.c | 16 ++++++++++++++-- src/video/nv/nv_base.c | 18 ++++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index d067c0148..2927b91c6 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -27,7 +27,7 @@ #ifdef EMU_DEVICE_H // what //TODO: split this all into nv1, nv3, nv4... - +#include <86box/log.h> #include <86box/i2c.h> #include <86box/vid_ddc.h> #include <86box/timer.h> @@ -35,6 +35,7 @@ #include <86box/vid_svga_render.h> #include <86box/nv/vid_nv_rivatimer.h> +void nv_log_set_device(void* device); void nv_log(const char *fmt, ...); // Defines common to all NV chip architectural generations @@ -92,6 +93,7 @@ typedef struct nv_base_s rom_t vbios; // NVIDIA/OEm VBIOS // move to nv3_cio_t? svga_t svga; // SVGA core (separate to nv3) - Weitek licensed + void* log; // new logging engine // 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 @@ -114,7 +116,7 @@ typedef struct nv_base_s rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID - void* ddc; + void* ddc; // Display Data Channel for EDID } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index b7b3319aa..9cf8a602a 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -913,6 +913,10 @@ void nv3_update_mappings() // void* nv3_init(const device_t *info) { + nv3->nvbase.log = log_open("NV3"); + + // Allows nv_log to be used for multiple nvidia devices + nv_log_set_device(nv3->nvbase.log); nv_log("NV3: initialising core\n"); // Figure out which vbios the user selected @@ -928,9 +932,13 @@ void* nv3_init(const device_t *info) int32_t err = rom_init(&nv3->nvbase.vbios, vbios_file, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); if (err) - nv_log("NV3: failed to load VBIOS err=%d\n", err); + { + nv_log("NV3 FATAL: failed to load VBIOS err=%d\n", err); + fatal("Nvidia NV3 init failed: Somehow selected a nonexistent VBIOS? err=%d\n", err); + return NULL; + } else - nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); + nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); // set the vram amount and gpu revision uint32_t vram_amount = device_get_config_int("VRAM"); @@ -1004,6 +1012,10 @@ void* nv3_init_agp(const device_t* info) void nv3_close(void* priv) { + // Shut down logging + log_close(nv3->nvbase.log); + nv_log_set_device(NULL); + // Shut down I2C and the DDC ddc_close(nv3->nvbase.ddc); i2c_gpio_close(nv3->nvbase.i2c); diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index 88cb5208b..96e011430 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -17,9 +17,11 @@ // Common NV1/3/4... init #define HAVE_STDARG_H // wtf is this crap +#include #include #include -#include + +#include <86box/log.h> #include <86box/86box.h> #include <86box/nv/vid_nv.h> @@ -28,13 +30,25 @@ #ifdef ENABLE_NV_LOG int nv_do_log = ENABLE_NV_LOG; +// A bit of kludge so that in the future we can abstract this function acorss multiple generations of Nvidia GPUs +void* nv_log_device; + +void nv_log_set_device(void* device) +{ + nv_log_device = device; +} + void nv_log(const char *fmt, ...) { + if (!nv_log_device) + return; + va_list ap; if (nv_do_log) { va_start(ap, fmt); - pclog_ex_cyclic(fmt, ap); + + log_out_cyclic(nv_log_device, fmt, ap); va_end(ap); } } From f20fd42e79ba59b06060b1675d7d6d81fc5cef1a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 17 Jan 2025 15:30:51 +0000 Subject: [PATCH 061/274] Add pgraph status and even more registers, man that was boring... --- .../86box/nv/classes/vid_nv3_classes.h | 2 +- src/include/86box/nv/vid_nv3.h | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index d63394da8..5ccd067a9 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -78,7 +78,7 @@ typedef struct nv3_color_argb_32_s /* 30-bit colour format for internal PGRAPH use */ typedef struct nv3_color_x3a10g10b10_s { - uint8_t reserved : 2; + uint8_t reserved : 1; bool alpha_if_chroma_key_otherwise_reserved2 : 1; // 1-bit ALPHA if chroma key, OTHERWISE USELESS and IGNORE uint16_t r : 10; uint16_t g : 10; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 8c4daf0e1..7845f132b 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -784,6 +784,29 @@ typedef struct nv3_pgraph_clip_misc_settings_s /* TODO */ } nv3_pgraph_clip_misc_settings_t; +typedef struct nv3_pgraph_status_s +{ + bool overall_busy : 1; // Is anything busy? + uint8_t reserved : 3; + bool xy_logic_busy : 1; // Determines if the line drawing/xy/vector stuff is busy. + uint8_t reserved2 : 3; + bool port_notify_busy : 1; // Mediaport?/PIO? notifier engine busy + uint8_t reserved3 : 3; + bool port_register_busy : 1; + uint8_t reserved4 : 3; + bool port_dma_busy : 1; // Mediaport?/PIO? DMA engine busy + bool dma_engine_busy : 1; // DMA engine busy + uint8_t reserved5 : 2; + bool dma_notify_busy : 1; // Are the notifiers busy? + uint8_t reserved6 : 3; + bool engine_3d_busy : 1; // 3d engine busy? + bool engine_cache_busy : 1; // PFIFO CACHE0/CACHE1 busy? + bool engine_zfifo_busy : 1; // ZFIFO (zeta buffer? z buffer?) busy? + bool port_user_busy : 1; // User context switch? + uint8_t reserved7 : 3; + +} nv3_pgraph_status_t; + // Graphics Subsystem typedef struct nv3_pgraph_s { @@ -823,6 +846,17 @@ typedef struct nv3_pgraph_s nv3_pgraph_dma_settings_t dma_settings; nv3_pgraph_clip_misc_settings_t clip_misc_settings; nv3_notifier_t notifier; + nv3_position_16_bigy_t clip0_min; + nv3_position_16_bigy_t clip0_max; + nv3_position_16_bigy_t clip1_min; + nv3_position_16_bigy_t clip1_max; + uint32_t fifo_access; + nv3_pgraph_status_t status; + uint32_t trapped_address; + uint32_t trapped_data; + uint32_t trapped_instance; + uint32_t interrupt_status_dma; + uint32_t interrupt_enable_dma; } nv3_pgraph_t; // GPU Manufacturing Configuration (again) From 7ba088a5eb0643b5e358997109a96b6cb9b9ecb5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 20 Jan 2025 01:09:32 +0000 Subject: [PATCH 062/274] Actually add basic read and write to those pgraph registers so we can debug them, add PFIFO definitions and add the PFIFO CACHE1 gray code conversion functions as preparation for future work --- .../86box/nv/classes/vid_nv3_classes.h | 5 +- src/include/86box/nv/vid_nv3.h | 104 +++++++-- src/video/nv/nv3/classes/nv3_class_names.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 30 ++- src/video/nv/nv3/subsystems/nv3_pgraph.c | 213 ++++++++++++++++++ .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 6 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 6 +- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 4 +- 8 files changed, 336 insertions(+), 34 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 5ccd067a9..099c0bdc6 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1043,9 +1043,12 @@ typedef struct nv3_object_class_017 /* No placeholder needed, it really is that long. */ } nv3_d3d5_accelerated_triangle_with_zeta_buffer_t; -/* 0x18, 0x19, 0x1A, 0x1B don't exist */ +/* 0x19, 0x1A, 0x1B don't exist */ +typedef struct nv3_object_class_018 +{ +} nv3_point_with_zeta_buffer_t; /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? They are making it look like a bitfield but it's hex? diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 7845f132b..a2c54748e 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 12 January 2025 (STILL WORKING ON IT!!!) + * Last updated: 20 January 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -218,7 +218,6 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CONFIG_RAMFC 0x2214 #define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS 9 #define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS_DEFAULT 0x1C00 // Hardcoded in silicon? - #define NV3_PFIFO_CONFIG_RAMRO 0x2218 #define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS 9 #define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS_DEFAULT 0x1E00 // Hardcoded in silicon? @@ -226,6 +225,44 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CONFIG_RAMRO_SIZE_512B 0x0 #define NV3_PFIFO_CONFIG_RAMRO_SIZE_8K 0x1 +#define NV3_PFIFO_RUNOUT_STATUS 0x2400 +#define NV3_PFIFO_RUNOUT_STATUS_RANOUT 0 // 1 if we fucked up +#define NV3_PFIFO_RUNOUT_STATUS_LOW_MARK 4 // 1 if ramro is empty +#define NV3_PFIFO_RUNOUT_STATUS_HIGH_MARK 8 +#define NV3_PFIFO_RUNOUT_PUT 0x2410 +#define NV3_PFIFO_RUNOUT_PUT_ADDRESS 3 // 9:3 if small ramfc(?) otherwise 12:3 +#define NV3_PFIFO_RUNOUT_GET 0x2420 +#define NV3_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 + +#define NV3_PFIFO_CACHE0_SIZE 1 // This is for software-injected notified only! +#define NV3_PFIFO_CACHE1_SIZE_REV_AB 32 +#define NV3_PFIFO_CACHE1_SIZE_REV_C 64 +#define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C +#define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 +#define NV3_PFIFO_CACHE0_ACCESS 0x3000 +#define NV3_PFIFO_CACHE0_DMA_CHANNEL_ID 0x3004 +#define NV3_PFIFO_CACHE0_PUT 0x3010 +#define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit +#define NV3_PFIFO_CACHE0_PULLER 0x3040 +#define NV3_PFIFO_CACHE0_GET 0x3070 +#define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit +#define NV3_PFIFO_CACHE1_ACCESS 0x3200 +#define NV3_PFIFO_CACHE1_DMA_CHANNEL_ID 0x3204 +#define NV3_PFIFO_CACHE1_PUT 0x3210 +#define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 +#define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 +#define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 +#define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 +#define NV3_PFIFO_CACHE1_DMA_CONFIG_2 0x3228 +#define NV3_PFIFO_CACHE1_DMA_CONFIG_3 0x322C +// Why does a gpu need its own translation lookaside buffer and pagetable format. Are they crazy +#define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 +#define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA +#define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA +#define NV3_PFIFO_CACHE1_PULLER 0x3240 +#define NV3_PFIFO_CACHE1_PULLER_CONTEXT_IS_CLEAN 0x3250 +#define NV3_PFIFO_CACHE1_GET 0x3270 +#define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem #define NV3_PRM_INTR 0x4100 @@ -750,7 +787,7 @@ typedef struct nv3_pramdac_s uint32_t hserr_width; // horizontal sync error width } nv3_pramdac_t; -/* Holds DMA context channel information */ +/* Holds DMA channel context information */ typedef struct nv3_pgraph_context_switch_s { /* TODO */ @@ -766,12 +803,20 @@ typedef struct nv3_pgraph_context_control_s */ typedef struct nv3_pgraph_context_user_s { - bool reserved3 : 1; - uint8_t channel : 7; - uint8_t reserved2 : 4; - uint8_t class : 5; - uint8_t subchannel : 3; - uint16_t reserved : 12; + union + { + uint32_t value; + + struct + { + bool reserved3 : 1; + uint8_t channel : 7; + uint8_t reserved2 : 3; + uint8_t class : 5; + uint8_t subchannel : 3; + uint16_t reserved : 13; + }; + }; } nv3_pgraph_context_user_t; typedef struct nv3_pgraph_dma_settings_s @@ -819,6 +864,7 @@ typedef struct nv3_pgraph_s uint32_t interrupt_status_1; // Interrupt status 1 uint32_t interrupt_enable_1; // Interrupt enable 1 + uint32_t context_switch; // TODO: Make this a struct, it's just going to be enormous lol. nv3_pgraph_context_control_t context_control; nv3_pgraph_context_switch_t context_user_submit; nv3_pgraph_context_user_t context_user; @@ -923,16 +969,21 @@ typedef struct nv3_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; -typedef struct nv3_pramin_name_sd +typedef struct nv3_pramin_name_s { union { uint32_t name; - uint8_t byte_high; - uint8_t byte_mid2; - uint8_t byte_mid1; - uint8_t byte_low; + + struct + { + uint8_t byte_high; + uint8_t byte_mid2; + uint8_t byte_mid1; + uint8_t byte_low; + }; }; + } nv3_pramin_name_t; typedef struct nv3_pramin_context_s @@ -940,10 +991,16 @@ typedef struct nv3_pramin_context_s union { uint32_t context; - uint8_t dma_channel; - uint8_t render_object; //0=sw, 1=hw accelerated render - uint8_t class_id; - uint8_t ramin_offset; //find + + struct + { + + uint8_t dma_channel; + uint8_t render_object; //0=sw, 1=hw accelerated render + uint8_t class_id; + uint8_t ramin_offset; //find + }; + }; } nv3_pramin_context_t; @@ -1110,8 +1167,6 @@ 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); @@ -1133,8 +1188,6 @@ 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 @@ -1163,11 +1216,18 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now); // NV3 PGRAPH void nv3_pgraph_init(); +uint32_t nv3_pgraph_read(uint32_t address); +void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); // NV3 PFIFO void nv3_pfifo_init(); +uint32_t nv3_pfifo_read(uint32_t address); +void nv3_pfifo_write(uint32_t address, uint32_t value); +// NV3 PFIFO - Caches +uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); +uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); // NV3 PFB void nv3_pfb_init(); diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index 2b38afbb4..0ee56443b 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -56,7 +56,7 @@ const char* nv3_class_names[] = "NV3 class 0x15: Stretched image from CPU", "NV3 INVALID class 0x16", "NV3 class 0x17: Direct3D 5.0 accelerated textured triangle w/zeta buffer", - "NV3 INVALID class 0x18", + "NV3 class 0x18: Point with zeta buffer", "NV3 INVALID class 0x19", "NV3 INVALID class 0x1A", "NV3 INVALID class 0x1B", diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1838faef5..b770116f3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -28,8 +28,6 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> - - // // ****** pfifo register list START ****** // @@ -206,4 +204,32 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) } } +} + +/* +https://en.wikipedia.org/wiki/Gray_code +WHY?????? IT'S NOT A TELEGRAPH IT'S A GPU????? + +Convert from a normal number to a total insanity number which is only used in PFIFO CACHE1 for ungodly and totally unknowable reasons +*/ +uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val) +{ + return (val) ^ (val >> 1); +} + +/* +Back to sanity +*/ +uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) +{ + uint32_t mask = val >> 1; + + // shift right until we have our normla number again + while (mask) + { + val ^= mask; + mask >>= 1; + } + + return val; } \ 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 index 32f87ee30..fbdf058ec 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -120,6 +120,17 @@ uint32_t nv3_pgraph_read(uint32_t address) { switch (reg->address) { + case NV3_PGRAPH_DEBUG_0: + ret = nv3->pgraph.debug_0; + break; + case NV3_PGRAPH_DEBUG_1: + ret = nv3->pgraph.debug_1; + break; + case NV3_PGRAPH_DEBUG_2: + ret = nv3->pgraph.debug_2; + break; + case NV3_PGRAPH_DEBUG_3: + ret = nv3->pgraph.debug_3; //interrupt status and enable regs case NV3_PGRAPH_INTR_0: ret = nv3->pgraph.interrupt_status_0; @@ -133,6 +144,101 @@ uint32_t nv3_pgraph_read(uint32_t address) case NV3_PGRAPH_INTR_EN_1: ret = nv3->pgraph.interrupt_enable_1; break; + // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like + // during the driver initialisation process + + // In the future, these will most likely have their own functions... + + // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) + case NV3_PGRAPH_CONTEXT_SWITCH: + ret = nv3->pgraph.context_switch; + break; + case NV3_PGRAPH_CONTEXT_CONTROL: + ret = *(uint32_t*)&nv3->pgraph.context_control; + break; + case NV3_PGRAPH_CONTEXT_USER: + ret = *(uint32_t*)&nv3->pgraph.context_user; + break; + // Clip + case NV3_PGRAPH_ABS_UCLIP_XMIN: + ret = nv3->pgraph.abs_uclip_xmin; + break; + case NV3_PGRAPH_ABS_UCLIP_XMAX: + ret = nv3->pgraph.abs_uclip_xmax; + break; + case NV3_PGRAPH_ABS_UCLIP_YMIN: + ret = nv3->pgraph.abs_uclip_ymin; + break; + case NV3_PGRAPH_ABS_UCLIP_YMAX: + ret = nv3->pgraph.abs_uclip_ymax; + break; + // Canvas + case NV3_PGRAPH_SRC_CANVAS_MIN: + ret = *(uint32_t*)&nv3->pgraph.src_canvas_min; + break; + case NV3_PGRAPH_SRC_CANVAS_MAX: + ret = *(uint32_t*)&nv3->pgraph.src_canvas_max; + break; + // Pattern + case NV3_PGRAPH_PATTERN_COLOR_0_0: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_0; + break; + case NV3_PGRAPH_PATTERN_COLOR_0_1: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_1; + break; + case NV3_PGRAPH_PATTERN_COLOR_1_0: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_0; + break; + case NV3_PGRAPH_PATTERN_COLOR_1_1: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_1; + break; + case NV3_PGRAPH_PATTERN_BITMAP_HIGH: + ret = nv3->pgraph.pattern_bitmap_high; + break; + case NV3_PGRAPH_PATTERN_BITMAP_LOW: + ret = nv3->pgraph.pattern_bitmap_low; + break; + // Beta factor + case NV3_PGRAPH_BETA: + ret = nv3->pgraph.beta_factor; + break; + // DMA + case NV3_PGRAPH_DMA: + ret = *(uint32_t*)&nv3->pgraph.dma_settings; + break; + case NV3_PGRAPH_NOTIFY: + ret = *(uint32_t*)&nv3->pgraph.notifier; + break; + // More clip + case NV3_PGRAPH_CLIP0_MIN: + ret = *(uint32_t*)&nv3->pgraph.clip0_min; + break; + case NV3_PGRAPH_CLIP0_MAX: + ret = *(uint32_t*)&nv3->pgraph.clip0_max; + break; + case NV3_PGRAPH_CLIP1_MIN: + ret = *(uint32_t*)&nv3->pgraph.clip1_min; + break; + case NV3_PGRAPH_CLIP1_MAX: + ret = *(uint32_t*)&nv3->pgraph.clip1_max; + break; + case NV3_PGRAPH_CLIP_MISC: + ret = *(uint32_t*)&nv3->pgraph.clip_misc_settings; + break; + // Overall Status + case NV3_PGRAPH_STATUS: + ret = *(uint32_t*)&nv3->pgraph.status; + break; + // Trapped Address + case NV3_PGRAPH_TRAPPED_ADDRESS: + ret = nv3->pgraph.trapped_address; + break; + case NV3_PGRAPH_TRAPPED_DATA: + ret = nv3->pgraph.trapped_data; + break; + case NV3_PGRAPH_TRAPPED_INSTANCE: + ret = nv3->pgraph.trapped_instance; + break; } } @@ -189,6 +295,18 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) { switch (reg->address) { + case NV3_PGRAPH_DEBUG_0: + nv3->pgraph.debug_0 = value; + break; + case NV3_PGRAPH_DEBUG_1: + nv3->pgraph.debug_1 = value; + break; + case NV3_PGRAPH_DEBUG_2: + nv3->pgraph.debug_2 = value; + break; + case NV3_PGRAPH_DEBUG_3: + nv3->pgraph.debug_3 = value; + break; //interrupt status and enable regs case NV3_PGRAPH_INTR_0: nv3->pgraph.interrupt_status_0 &= ~value; @@ -210,6 +328,101 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) nv3->pgraph.interrupt_enable_1 = value & 0x00011111; nv3_pmc_handle_interrupts(true); + break; + // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like + // during the driver initialisation process + + // In the future, these will most likely have their own functions... + + // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) + case NV3_PGRAPH_CONTEXT_SWITCH: + nv3->pgraph.context_switch = value; + break; + case NV3_PGRAPH_CONTEXT_CONTROL: + *(uint32_t*)&nv3->pgraph.context_control = value; + break; + case NV3_PGRAPH_CONTEXT_USER: + *(uint32_t*)&nv3->pgraph.context_user = value; + break; + // Clip + case NV3_PGRAPH_ABS_UCLIP_XMIN: + nv3->pgraph.abs_uclip_xmin = value; + break; + case NV3_PGRAPH_ABS_UCLIP_XMAX: + nv3->pgraph.abs_uclip_xmax = value; + break; + case NV3_PGRAPH_ABS_UCLIP_YMIN: + nv3->pgraph.abs_uclip_ymin = value; + break; + case NV3_PGRAPH_ABS_UCLIP_YMAX: + nv3->pgraph.abs_uclip_ymax = value; + break; + // Canvas + case NV3_PGRAPH_SRC_CANVAS_MIN: + *(uint32_t*)&nv3->pgraph.src_canvas_min = value; + break; + case NV3_PGRAPH_SRC_CANVAS_MAX: + *(uint32_t*)&nv3->pgraph.src_canvas_max = value; + break; + // Pattern + case NV3_PGRAPH_PATTERN_COLOR_0_0: + *(uint32_t*)&nv3->pgraph.pattern_color_0_0 = value; + break; + case NV3_PGRAPH_PATTERN_COLOR_0_1: + *(uint32_t*)&nv3->pgraph.pattern_color_0_1 = value; + break; + case NV3_PGRAPH_PATTERN_COLOR_1_0: + *(uint32_t*)&nv3->pgraph.pattern_color_1_0 = value; + break; + case NV3_PGRAPH_PATTERN_COLOR_1_1: + *(uint32_t*)&nv3->pgraph.pattern_color_1_1 = value; + break; + case NV3_PGRAPH_PATTERN_BITMAP_HIGH: + nv3->pgraph.pattern_bitmap_high = value; + break; + case NV3_PGRAPH_PATTERN_BITMAP_LOW: + nv3->pgraph.pattern_bitmap_low = value; + break; + // Beta factor + case NV3_PGRAPH_BETA: + nv3->pgraph.beta_factor = value; + break; + // DMA + case NV3_PGRAPH_DMA: + *(uint32_t*)&nv3->pgraph.dma_settings = value; + break; + case NV3_PGRAPH_NOTIFY: + *(uint32_t*)&nv3->pgraph.notifier = value; + break; + // More clip + case NV3_PGRAPH_CLIP0_MIN: + *(uint32_t*)&nv3->pgraph.clip0_min = value; + break; + case NV3_PGRAPH_CLIP0_MAX: + *(uint32_t*)&nv3->pgraph.clip0_max = value; + break; + case NV3_PGRAPH_CLIP1_MIN: + *(uint32_t*)&nv3->pgraph.clip1_min = value; + break; + case NV3_PGRAPH_CLIP1_MAX: + *(uint32_t*)&nv3->pgraph.clip1_max = value; + break; + case NV3_PGRAPH_CLIP_MISC: + *(uint32_t*)&nv3->pgraph.clip_misc_settings = value; + break; + // Overall Status + case NV3_PGRAPH_STATUS: + *(uint32_t*)&nv3->pgraph.status = value; + break; + // Trapped Address + case NV3_PGRAPH_TRAPPED_ADDRESS: + nv3->pgraph.trapped_address = value; + break; + case NV3_PGRAPH_TRAPPED_DATA: + nv3->pgraph.trapped_data = value; + break; + case NV3_PGRAPH_TRAPPED_INSTANCE: + nv3->pgraph.trapped_instance = value; break; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index e06445755..a3872c40a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * NV3 PFIFO RAMFC area: Stores context unused DMA channels + * NV3 PFIFO RAMFC area: Stores context for unused DMA channels * * * @@ -30,10 +30,10 @@ uint32_t nv3_ramfc_read(uint32_t address) { - nv_log("NV3: RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); + nv_log("NV3: RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramfc_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED", value, address); + nv_log("NV3: RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); } \ 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 index ff72e811c..7df99d697 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -35,17 +35,17 @@ It is used to get the offset within RAMHT of a graphics object. uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel) { uint32_t hash = (name.byte_high ^ name.byte_mid2 ^ name.byte_mid1 ^ name.byte_low ^ (uint8_t)channel); - nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)", name, channel); + nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", name, channel); return hash; } uint32_t nv3_ramht_read(uint32_t address) { - nv_log("NV3: RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); + nv_log("NV3: RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramht_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED", value, address); + nv_log("NV3: RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 6176e191a..b7d8f4f57 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -30,10 +30,10 @@ uint32_t nv3_ramro_read(uint32_t address) { - nv_log("NV3: RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00", address); + nv_log("NV3: RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramro_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)", value, address); + nv_log("NV3: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)\n", value, address); } \ No newline at end of file From e4f446c6d019481a7d8510ef63704eda133c92d6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 22 Jan 2025 14:52:46 +0000 Subject: [PATCH 063/274] fix logging for ramin writes. --- doc/nvidia_notes/nv3_object_classes.txt | 2 +- src/include/86box/nv/vid_nv3.h | 27 +++++++++++++++++++++++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 13 ++++++++++-- src/video/nv/nv3/subsystems/nv3_pramin.c | 16 ++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/doc/nvidia_notes/nv3_object_classes.txt b/doc/nvidia_notes/nv3_object_classes.txt index 3cb99882b..05d1d9bf3 100644 --- a/doc/nvidia_notes/nv3_object_classes.txt +++ b/doc/nvidia_notes/nv3_object_classes.txt @@ -26,7 +26,7 @@ Object classes as understood by the GPU. 0x15 = Stretched image from CPU 0x16 = INVALID 0x17 = Direct3D 5.0 accelerated textured triangle w/zeta buffer -0x18 = INVALID +0x18 = Point w/zeta buffer 0x19 = INVALID 0x1A = INVALID 0x1B = INVALID diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a2c54748e..4033873bb 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -242,6 +242,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_ACCESS 0x3000 #define NV3_PFIFO_CACHE0_DMA_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 +#define NV3_PFIFO_CACHE0_STATUS 0x3014 +#define NV3_PFIFO_CACHE0_STATUS_RANOUT 0 // 1 if we fucked up +#define NV3_PFIFO_CACHE0_STATUS_LOW_MARK 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE0_STATUS_HIGH_MARK 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit #define NV3_PFIFO_CACHE0_PULLER 0x3040 #define NV3_PFIFO_CACHE0_GET 0x3070 @@ -250,6 +254,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 +#define NV3_PFIFO_CACHE1_STATUS 0x3214 +#define NV3_PFIFO_CACHE1_STATUS_RANOUT 0 // 1 if we fucked up +#define NV3_PFIFO_CACHE1_STATUS_LOW_MARK 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE1_STATUS_HIGH_MARK 8 #define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 #define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 #define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 @@ -740,6 +748,20 @@ typedef struct nv3_pbus_s nv3_pbus_rma_t rma; } nv3_pbus_t; +typedef struct nv3_pfifo_cache_s +{ + uint8_t put_address; // Trigger a DMA into the value you put here. + uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. + /* TODO */ +} nv3_pfifo_cache_t; + +typedef struct nv3_pfifo_cache_entry_s +{ + uint8_t subchannel_id : 3; + uint16_t method : 11; // method id depending on class (offset from entry channel start in ramin) + uint32_t data; // is this the context +} nv3_pfifo_cache_entry_t; + // Command submission to PGRAPH typedef struct nv3_pfifo_s { @@ -749,7 +771,10 @@ typedef struct nv3_pfifo_s uint32_t ramfc_config; // RAMFC config uint32_t ramro_config; // RAMRO config uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? - + nv3_pfifo_cache_t cache0_settings; + nv3_pfifo_cache_t cache1_settings; + uint32_t cache0_status; // status of cache0 + uint32_t cache1_status; // status of cache1 } nv3_pfifo_t; // create_object(uint32_t type) here diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index b770116f3..990d085f2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> // -// ****** pfifo register list START ****** +// ****** PFIFO register list START ****** // nv_register_t pfifo_registers[] = { @@ -38,6 +38,12 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, + { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, + { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, + { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, + { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, + { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, + { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -101,6 +107,9 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CONFIG_RAMRO: ret = nv3->pfifo.ramro_config; break; + case NV3_PFIFO_CACHE0_GET: + //wa + break; } } @@ -220,7 +229,7 @@ uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val) /* Back to sanity */ -uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) +uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) { uint32_t mask = val >> 1; diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 35fa80603..ee6d0ddf5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -43,6 +43,8 @@ // Read 8-bit ramin uint8_t nv3_ramin_read8(uint32_t addr, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); uint32_t raw_addr = addr; // saved after and @@ -62,6 +64,8 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) // Read 16-bit ramin uint16_t nv3_ramin_read16(uint32_t addr, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); // why does this not work in one line @@ -86,6 +90,8 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) // Read 32-bit ramin uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); // why does this not work in one line @@ -111,6 +117,8 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) // Write 8-bit ramin void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); uint32_t raw_addr = addr; // saved after and @@ -134,6 +142,8 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) // Write 16-bit ramin void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); // why does this not work in one line @@ -149,7 +159,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) if (!nv3_pramin_arbitrate_write(addr, val32)) { vram_16bit[addr] = val; - nv_log("NV3: Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("NV3: Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); } @@ -158,6 +168,8 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) // Write 32-bit ramin void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) { + if (!nv3) return; + addr &= (nv3->nvbase.svga.vram_max - 1); // why does this not work in one line @@ -173,7 +185,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) if (!nv3_pramin_arbitrate_write(addr, val32)) { vram_32bit[addr] = val; - nv_log("NV3: Write dnv3_pramin_arbitrate_readword to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("NV3: Write dword to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); } } From 81404a35fc480477926721ab3d7f385ba3d1e9e3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 22 Jan 2025 14:54:19 +0000 Subject: [PATCH 064/274] fix compilation --- src/video/nv/nv3/subsystems/nv3_pramin.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index ee6d0ddf5..a5c75ea21 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -43,7 +43,7 @@ // Read 8-bit ramin uint8_t nv3_ramin_read8(uint32_t addr, void* priv) { - if (!nv3) return; + if (!nv3) return 0x00; addr &= (nv3->nvbase.svga.vram_max - 1); uint32_t raw_addr = addr; // saved after and @@ -64,7 +64,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) // Read 16-bit ramin uint16_t nv3_ramin_read16(uint32_t addr, void* priv) { - if (!nv3) return; + if (!nv3) return 0x00; addr &= (nv3->nvbase.svga.vram_max - 1); @@ -90,7 +90,7 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) // Read 32-bit ramin uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { - if (!nv3) return; + if (!nv3) return 0x00; addr &= (nv3->nvbase.svga.vram_max - 1); From 07dc73bcf8c52904473406a57628b58984130a19 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 22 Jan 2025 15:28:14 +0000 Subject: [PATCH 065/274] fixes to the logging system and register names --- src/86box.c | 2 +- src/include/86box/nv/vid_nv3.h | 103 ++++++++++++----------- src/log.c | 10 ++- src/video/nv/nv3/nv3_core.c | 8 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 5 files changed, 70 insertions(+), 55 deletions(-) diff --git a/src/86box.c b/src/86box.c index 97c211bee..a696dfc1e 100644 --- a/src/86box.c +++ b/src/86box.c @@ -267,7 +267,7 @@ void pclog_ensure_stdlog_open(void); #endif /* - Ensures STDLOG is open for pclog_ex and pclog_ex_cyclic + Ensures STDLOG is open for pclog_ex */ void pclog_ensure_stdlog_open(void) { diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 4033873bb..1976bd732 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 20 January 2025 (STILL WORKING ON IT!!!) + * Last updated: 2 January 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -267,7 +267,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULLER 0x3240 +#define NV3_PFIFO_CACHE1_PULLER_STATE0 0x3240 +#define NV3_PFIFO_CACHE1_PULLER_STATE0_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULLER_STATE0_HASH_SUCCESS 4 +#define NV3_PFIFO_CACHE1_PULLER_STATE0_DEVICE 8 #define NV3_PFIFO_CACHE1_PULLER_CONTEXT_IS_CLEAN 0x3250 #define NV3_PFIFO_CACHE1_GET 0x3270 #define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 @@ -445,56 +448,56 @@ extern const device_config_t nv3_config[]; // 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_CLASS01_BETA_START 0x410000 // Beta blending factor +#define NV3_PGRAPH_CLASS01_BETA_END 0x411FFF +#define NV3_PGRAPH_CLASS02_ROP_START 0x420000 // Blending render operation used at final pixel/fragment generation stage +#define NV3_PGRAPH_CLASS02_ROP_END 0x421FFF +#define NV3_PGRAPH_CLASS03_COLORKEY_START 0x430000 // Color key for image +#define NV3_PGRAPH_CLASS03_COLORKEY_END 0x431FFF +#define NV3_PGRAPH_CLASS04_PLANEMASK_START 0x440000 // Plane mask (for clipping?) +#define NV3_PGRAPH_CLASS04_PLANEMASK_END 0x441FFF +#define NV3_PGRAPH_CLASS05_CLIP_START 0x450000 // clipping, probably class 23 +#define NV3_PGRAPH_CLASS05_CLIP_END 0x451FFF +#define NV3_PGRAPH_CLASS06_PATTERN_START 0x460000 // presumably a blend pattern +#define NV3_PGRAPH_CLASS06_PATTERN_END 0x461FFF +#define NV3_PGRAPH_CLASS07_RECTANGLE_START 0x470000 // also class 25 - that's black [NV1] +#define NV3_PGRAPH_CLASS07_RECTANGLE_END 0x471FFF // also class 25 - that's black [NV1] +#define NV3_PGRAPH_CLASS08_POINT_START 0x480000 // A single point +#define NV3_PGRAPH_CLASS08_POINT_END 0x481FFF +#define NV3_PGRAPH_CLASS09_LINE_START 0x490000 // A line +#define NV3_PGRAPH_CLASS09_LINE_END 0x491FFF +#define NV3_PGRAPH_CLASS0A_LIN_START 0x4A0000 // A lin - a line without its starting or ending pixels +#define NV3_PGRAPH_CLASS0A_LIN_END 0x4A1FFF +#define NV3_PGRAPH_CLASS0B_TRIANGLE_START 0x4B0000 // A triangle [NV1 variant] - in NV1 this was converted to a quad patch +#define NV3_PGRAPH_CLASS0B_TRIANGLE_END 0x4B1FFF +#define NV3_PGRAPH_CLASS0C_GDITEXT_START 0x4C0000 // Windows 95/NT GDI text acceleration +#define NV3_PGRAPH_CLASS0C_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_CLASS0D_MEM2MEM_XFER_START 0x4D0000 // memory to memory transfer (not sure about which class this is) +#define NV3_PGRAPH_CLASS0D_MEM2MEM_XFER_END 0x4D1FFF +#define NV3_PGRAPH_CLASS0E_IMAGE2MEM_XFER_SCALED_START 0x4E0000 // class 55, 56 +#define NV3_PGRAPH_CLASS0F_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_CLASS10_BLIT_START 0x500000 // Blit 2d image from memory +#define NV3_PGRAPH_CLASS10_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_CLASS11_CPU2MEM_IMAGE_START 0x510000 // Used for class 33, 34, 54 +#define NV3_PGRAPH_CLASS11_CPU2MEM_IMAGE_END 0x511FFF +#define NV3_PGRAPH_CLASS12_CPU2MEM_BITMAP_START 0x520000 // not sure, might depend on format +#define NV3_PGRAPH_CLASS12_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_CLASS14_IMAGE2MEM_XFER_START 0x540000 // send image to vram, not sure what class +#define NV3_PGRAPH_CLASS14_IMAGE2MEM_XFER_END 0x541FFF +#define NV3_PGRAPH_CLASS15_CPU2MEM_STRETCHED_START 0x550000 // stretched cpu->vram transfer, 54 +#define NV3_PGRAPH_CLASS15_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_CLASS17_D3D5TRI_ZETA_START 0x570000 // [NV3] Copy a direct3d 5.0 accelerated triangle to the zeta buffer +#define NV3_PGRAPH_CLASS17_D3D5TRI_ZETA_END 0x571FFF +#define NV3_PGRAPH_CLASS18_POINTZETA_START 0x580000 // possibly class 69 +#define NV3_PGRAPH_CLASS18_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_CLASS1C_MEM2IMAGE_START 0x5C0000 // class 55, 56, 62, 63? +#define NV3_PGRAPH_CLASS1C_MEM2IMAGE_END 0x5C1FFF #define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers #define NV3_PGRAPH_REAL_END 0x5C1FFF @@ -600,6 +603,12 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_DATA_OUT 0x3C0 #define NV3_CRTC_MISCOUT 0x3C2 +#define NV3_RMA_REGISTER_START 0x3D0 +#define NV3_RMA_REGISTER_END 0x3D3 + +#define NV3_CRTC_REGISTER_INDEX 0x3D4 +#define NV3_CRTC_REGISTER_CURRENT 0x3D5 + // These are standard (0-18h) #define NV3_CRTC_REGISTER_HTOTAL 0x00 #define NV3_CRTC_REGISTER_HDISPEND 0x01 @@ -662,8 +671,6 @@ extern const device_config_t nv3_config[]; #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 diff --git a/src/log.c b/src/log.c index a4d84e616..7d1a9b8fa 100644 --- a/src/log.c +++ b/src/log.c @@ -46,6 +46,7 @@ typedef struct log_t { char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; // Cyclical log buffer. This is 32kb, might calloc? int32_t cyclic_last_line; int32_t log_cycles; + int32_t last_repeat_order; // used to detect changes between different repeating patterns } log_t; extern FILE *stdlog; /* file to log output to */ @@ -216,6 +217,13 @@ log_out_cyclic(void* priv, const char* fmt, va_list ap) { log->log_cycles++; + // If the order of the log repeat changes + if (log->last_repeat_order != repeat_order + && log->last_repeat_order > 0) + { + log->log_cycles = 1; + } + if (log->log_cycles == 1) { // 'Replay' the last few log entries so they actually show up @@ -251,7 +259,7 @@ log_out_cyclic(void* priv, const char* fmt, va_list ap) } log->cyclic_last_line++; - + log->last_repeat_order = repeat_order; #endif } diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 9cf8a602a..5e7017c65 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -575,10 +575,10 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) switch (addr) { // Alias for "get current SVGA CRTC register ID" - case 0x3D4: + case NV3_CRTC_REGISTER_INDEX: ret = nv3->nvbase.svga.crtcreg; break; - case 0x3D5: + case NV3_CRTC_REGISTER_CURRENT: // Support the extended NVIDIA CRTC register range switch (nv3->nvbase.svga.crtcreg) { @@ -644,12 +644,12 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) switch (addr) { - case 0x3D4: + case NV3_CRTC_REGISTER_INDEX: // real mode access to GPU MMIO space... nv3->nvbase.svga.crtcreg = val; break; // support the extended crtc regs and debug this out - case 0x3D5: + case NV3_CRTC_REGISTER_CURRENT: // Implements the VGA Protect register if ((nv3->nvbase.svga.crtcreg < NV3_CRTC_REGISTER_OVERFLOW) && (nv3->nvbase.svga.crtc[0x11] & 0x80)) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index a5c75ea21..8203519c5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -185,7 +185,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) if (!nv3_pramin_arbitrate_write(addr, val32)) { vram_32bit[addr] = val; - nv_log("NV3: Write dword to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log("NV3: Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } } From 94af12da439fed03bd6663e93bd2eb393bc43720 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 23 Jan 2025 23:55:41 +0000 Subject: [PATCH 066/274] prepare for implementation of multithreaded renderer and add some of the simpler pfifo registers. e.g. debug0, which the driver writes to. --- .../nv3_object_classes_driver.txt | 2 +- .../86box/nv/classes/vid_nv3_classes.h | 2 +- src/include/86box/nv/vid_nv3.h | 50 ++++++-- src/video/nv/nv3/nv3_core.c | 6 +- src/video/nv/nv3/nv3_core_config.c | 108 ++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 39 ++++++- 6 files changed, 183 insertions(+), 24 deletions(-) diff --git a/doc/nvidia_notes/nv3_object_classes_driver.txt b/doc/nvidia_notes/nv3_object_classes_driver.txt index 83b421335..3e4955a80 100644 --- a/doc/nvidia_notes/nv3_object_classes_driver.txt +++ b/doc/nvidia_notes/nv3_object_classes_driver.txt @@ -47,7 +47,7 @@ object class 64/40h = video scaler object class 65/41h = video color key (as opposed to image) object class 66/42h = capture video to memory object class 67/43h = Solid ROP5 -object class 68/44h = zeta buffer (something to do with how the gpu internally renders 3d stuff) from CPU(?) +object class 68/44h = zeta buffer (combined z and stencil buffer) from CPU(?) object class 69/45h = zeta buffer in VRAM object class 70/46h = zeta buffer patchcord object class 71/47h = render solid point into zeta buffer (Also rectangle0 diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 099c0bdc6..afe3fe18d 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -660,7 +660,7 @@ typedef struct nv3_object_class_015 0x?? (drivers) Also 0x57 in context IDs. - Direct3D 5.0 accelerated triangle with zeta buffer (Not the same as a Zbuffer...) + Direct3D 5.0 accelerated triangle with zeta buffer (combined z buffer and stencil buffer) This is the final boss of this GPU. True horror stands below. */ diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 1976bd732..37c4683d5 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -200,6 +200,10 @@ extern const device_config_t nv3_config[]; #define NV3_PBUS_END 0x1FFF #define NV3_PFIFO_START 0x2000 // FIFO for DMA Object Submission (uses hashtable to store the objects) +#define NV3_PFIFO_DEBUG_0 0x2080 // PFIFO Debug Register +#define NV3_PFIFO_CACHE0_ERROR_PENDING 0 +#define NV3_PFIFO_CACHE1_ERROR_PENDING 1 + #define NV3_PFIFO_INTR 0x2100 // FIFO - Interrupt Status #define NV3_PFIFO_INTR_EN 0x2140 // FIFO - Interrupt Enable @@ -247,7 +251,12 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_STATUS_LOW_MARK 4 // 1 if ramro is empty #define NV3_PFIFO_CACHE0_STATUS_HIGH_MARK 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE0_PULLER 0x3040 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_SUCCESS 4 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE0_PULLER_STATE1 0x3050 +#define NV3_PFIFO_CACHE0_PULLER_STATE1_CTX_IS_CLEAN 4 #define NV3_PFIFO_CACHE0_GET 0x3070 #define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit #define NV3_PFIFO_CACHE1_ACCESS 0x3200 @@ -267,11 +276,12 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULLER_STATE0 0x3240 -#define NV3_PFIFO_CACHE1_PULLER_STATE0_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULLER_STATE0_HASH_SUCCESS 4 -#define NV3_PFIFO_CACHE1_PULLER_STATE0_DEVICE 8 -#define NV3_PFIFO_CACHE1_PULLER_CONTEXT_IS_CLEAN 0x3250 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL 0x3240 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_SUCCESS 4 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE1_PULLER_STATE1 0x3250 +#define NV3_PFIFO_CACHE1_PULLER_STATE1_CTX_IS_CLEAN 4 #define NV3_PFIFO_CACHE1_GET 0x3270 #define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 #define NV3_PFIFO_END 0x3FFF @@ -759,6 +769,26 @@ typedef struct nv3_pfifo_cache_s { uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. + uint8_t channel_id; + uint32_t status; + uint32_t status_puller; + uint32_t control; + uint32_t context[8]; + + /* cache1 only + do we even need to emulate this? + */ + bool dma_enabled; // 0x3220 bit0 + bool dma_is_busy; // 0x3220 bit4 + uint32_t dma_length; + uint32_t dma_address; + uint8_t dma_target_node; // depends on card bus + uint8_t dma_tlb_tag; + uint8_t tlb_pte; // DMA Engine - Translation Lookaside Buffer + uint8_t tlb_pt_base; // DMA Engine - TLB Pagetable Base Addres + + bool context_is_dirty; + /* TODO */ } nv3_pfifo_cache_t; @@ -767,6 +797,7 @@ typedef struct nv3_pfifo_cache_entry_s uint8_t subchannel_id : 3; uint16_t method : 11; // method id depending on class (offset from entry channel start in ramin) uint32_t data; // is this the context + } nv3_pfifo_cache_entry_t; // Command submission to PGRAPH @@ -774,14 +805,17 @@ typedef struct nv3_pfifo_s { uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable + uint32_t debug_0; // Cache Debug register uint32_t ramht_config; // RAMHT config uint32_t ramfc_config; // RAMFC config uint32_t ramro_config; // RAMRO config uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? nv3_pfifo_cache_t cache0_settings; nv3_pfifo_cache_t cache1_settings; - uint32_t cache0_status; // status of cache0 - uint32_t cache1_status; // status of cache1 + + nv3_pfifo_cache_entry_t cache0_entries[1]; + nv3_pfifo_cache_entry_t cache1_entries[NV3_PFIFO_CACHE1_SIZE_MAX]; // ONLY 32 USED ON REVISION A/B CARDS + } nv3_pfifo_t; // create_object(uint32_t type) here diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 5e7017c65..499c06a92 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -920,7 +920,7 @@ void* nv3_init(const device_t *info) nv_log("NV3: initialising core\n"); // Figure out which vbios the user selected - const char* vbios_id = device_get_config_bios("VBIOS"); + const char* vbios_id = device_get_config_bios("vbios"); const char* vbios_file = ""; // depends on the bus we are using @@ -941,8 +941,8 @@ void* nv3_init(const device_t *info) nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); // set the vram amount and gpu revision - uint32_t vram_amount = device_get_config_int("VRAM"); - nv3->nvbase.gpu_revision = device_get_config_int("Chip Revision"); + uint32_t vram_amount = device_get_config_int("vram_size"); + nv3->nvbase.gpu_revision = device_get_config_int("chip_revision"); // set up the bus and start setting up SVGA core if (nv3->nvbase.bus_generation == nv_bus_pci) diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index 3bc8692b9..ca4c3bf38 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -31,61 +31,105 @@ const device_config_t nv3_config[] = { // VBIOS type configuration { - .name = "VBIOS", + .name = "vbios", + #ifndef RELEASE_BUILD .description = "VBIOS", + #else + .description = "Model", + #endif .type = CONFIG_BIOS, .default_string = "NV3_VBIOS_ERAZOR_V15403", .default_int = 0, .bios = { { + #ifndef RELEASE_BUILD .name = "[NV3 - 1997-09-30] ELSA VICTORY Erazor VBE 3.0 DDC2B DPMS Video BIOS Ver. 1.47.01 (ZZ/ A/00)", .files_no = 1, + #else + .name = "[RIVA 128] ELSA Victory Erazor v1.47.01", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_ERAZOR_V14700", .files = {NV3_VBIOS_ERAZOR_V14700, ""} }, { + #ifndef RELEASE_BUILD .name = "[NV3 - 1998-02-06] ELSA VICTORY Erazor Ver. 1.54.03 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, + #else + .name = "[RIVA 128] ELSA Victory Erazor v1.54.03", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_ERAZOR_V15403", .files = {NV3_VBIOS_ERAZOR_V15403, ""} }, { - .name = "[NV3 - 1998-05-04] ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, + #ifndef RELEASE_BUILD + .name = "[NV3 - 1998-05-04] ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, + #else + .name = "[RIVA 128] ELSA Victory Erazor v1.55.00", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_ERAZOR_V15500", .files = {NV3_VBIOS_ERAZOR_V15500, ""} }, { + #ifndef RELEASE_BUILD .name = "[NV3 - 1998-01-14] Diamond Multimedia Systems, Inc. Viper V330 Version 1.62-CO", .files_no = 1, + #else + .name = "[RIVA 128] Diamond Viper V330", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_DIAMOND_V330_V162", .files = {NV3_VBIOS_DIAMOND_V330_V162, ""}, }, { + #ifndef RELEASE_BUILD .name = "[NV3 - 1997-09-06] ASUS AGP/3DP-V3000 BIOS 1.51B", .files_no = 1, + #else + .name = "[RIVA 128] ASUS AGP/3DP-V3000", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_ASUS_V3000_V151", .files = {NV3_VBIOS_ASUS_V3000_V151, ""}, }, { + #ifndef RELEASE_BUILD .name = "[NV3 - 1997-12-17] STB Velocity 128 (RIVA 128) Ver.1.82", .files_no = 1, + #else + .name = "[RIVA 128] STB Velocity 128", .files_no = 1, + #endif .internal_name = "NV3_VBIOS_STB_V128_V182", .files = {NV3_VBIOS_STB_V128_V182, ""}, }, { + #ifndef RELEASE_BUILD .name = "[NV3T - 1998-09-15] Diamond Multimedia Viper V330 8M BIOS - Version 1.82B", .files_no = 1, + #else + .name = "[RIVA 128 ZX] Diamond Multimedia Viper V330 8MB", .files_no = 1, + #endif .internal_name = "NV3T_VBIOS_DIAMOND_V330_V182B", .files = {NV3T_VBIOS_DIAMOND_V330_V182B, ""}, }, { + #ifndef RELEASE_BUILD .name = "[NV3T - 1998-08-04] ASUS AGP-V3000 ZXTV BIOS - V1.70D.03", .files_no = 1, + #else + .name = "[RIVA 128 ZX] ASUS AGP-V3000 ZXTV", .files_no = 1, + #endif .internal_name = "NV3T_VBIOS_ASUS_V170", .files = {NV3T_VBIOS_ASUS_V170, ""}, }, { + #ifndef RELEASE_BUILD .name = "[NV3T - 1998-07-30] RIVA 128 ZX BIOS - V1.71B-N", .files_no = 1, + #else + .name = "[RIVA 128 ZX] Nvidia Reference BIOS v1.71", .files_no = 1, + #endif .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V171", .files = {NV3T_VBIOS_REFERENCE_CEK_V171, ""}, }, - + { + #ifndef RELEASE_BUILD .name = "[NV3T+SGRAM - 1998-08-15] RIVA 128 ZX BIOS - V1.72B", .files_no = 1, + #else + .name = "[RIVA 128 ZX] Nvidia Reference BIOS v1.72", .files_no = 1, + #endif .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V172", .files = {NV3T_VBIOS_REFERENCE_CEK_V172, ""}, }, @@ -93,18 +137,19 @@ const device_config_t nv3_config[] = }, // Memory configuration { - .name = "VRAM", + .name = "vram_size", .description = "VRAM Size", .type = CONFIG_SELECTION, .default_int = VRAM_SIZE_4MB, .selection = { - // This never existed officially but was planned. Same for 64-bit bus. Debug only +#ifndef RELEASE_BUILD + // This never existed officially but was planned. Debug only { .description = "2 MB (Never officially sold)", .value = VRAM_SIZE_2MB, }, - +#endif { .description = "4 MB", .value = VRAM_SIZE_4MB, @@ -117,26 +162,69 @@ const device_config_t nv3_config[] = }, { - .name = "Chip Revision", + .name = "chip_revision", .description = "Chip Revision", .type = CONFIG_SELECTION, .default_int = NV3_PCI_CFG_REVISION_B00, .selection = { +#ifndef RELEASE_BUILD { - .description = "NV3/STG3000 Engineering Sample / Stepping A0 (January 1997)", + .description = "NV3/STG3000 Engineering Sample / Stepping A0 (January 1997) with integrated PAUDIO sound card", +#else + .description = "RIVA 128 Prototype (Revision A)", +#endif .value = NV3_PCI_CFG_REVISION_A00, }, +#ifndef RELEASE_BUILD { - .description = "RIVA 128 (NV3) / Stepping B0 (August 1997)", + .description = "RIVA 128 / Stepping B0 (October 1997)", +#else + .description = "RIVA 128 (Revision B)", +#endif .value = NV3_PCI_CFG_REVISION_B00, }, +#ifndef RELEASE_BUILD { - .description = "RIVA 128 ZX (NV3T) / Stepping C0 (March 1998)", + .description = "NV3T - RIVA 128 ZX / Stepping C0 (March 1998)", +#else + .description = "RIVA 128 ZX (Revision C)", +#endif .value = NV3_PCI_CFG_REVISION_C00, }, } }, + // Multithreading configuration + { + + .name = "pgraph_threads", +#ifndef RELEASE_BUILD + .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", +#else + .description = "Render threads", +#endif + .type = CONFIG_SELECTION, + .default_int = 1, // todo: change later + .selection = + { + { + .description = "1 thread (Only use if issues appear with more threads)", + .value = 1, + }, + { + .description = "2 threads", + .value = 2, + }, + { + .description = "4 threads", + .value = 4, + }, + { + .description = "8 threads", + .value = 8, + }, + }, + }, { .type = CONFIG_END } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 990d085f2..3de7661e3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -38,6 +38,11 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, + { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller State0", NULL, NULL}, + { NV3_PFIFO_CACHE0_PULLER_STATE1, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller State0", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULLER_STATE1, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, + { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, @@ -97,6 +102,10 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_INTR_EN: ret = nv3->pfifo.interrupt_enable; break; + // Debug + case NV3_PFIFO_DEBUG_0: + ret = nv3->pfifo.debug_0; + break; // These may need to become functions. case NV3_PFIFO_CONFIG_RAMFC: ret = nv3->pfifo.ramfc_config; @@ -110,6 +119,17 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE0_GET: //wa break; + // Reassignment + case NV3_PFIFO_CACHE_REASSIGNMENT: + ret = nv3->pfifo.cache_reassignment & 0x01; //1bit meaningful + break; + // Control + case NV3_PFIFO_CACHE0_PULLER_CONTROL: + ret = nv3->pfifo.cache0_settings.control & 0xFF; // 8bits meaningful + break; + case NV3_PFIFO_CACHE1_PULLER_CONTROL: + ret = nv3->pfifo.cache1_settings.control & 0xFF; // only 8bits are meaningful + break; } } @@ -208,6 +228,21 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv_log("NV3: RAMRO Reconfiguration\n" "Base Address in RAMIN: %d\n" "Size: 0x%08x bytes\n", ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9, new_size_ramro); + break; + case NV3_PFIFO_DEBUG_0: + nv3->pfifo.debug_0 = value; + break; + // Reassignment + case NV3_PFIFO_CACHE_REASSIGNMENT: + nv3->pfifo.cache_reassignment = value & 0x01; //1bit meaningful + break; + // Control + case NV3_PFIFO_CACHE0_PULLER_CONTROL: + nv3->pfifo.cache0_settings.control = value; // 8bits meaningful + break; + case NV3_PFIFO_CACHE1_PULLER_CONTROL: + nv3->pfifo.cache1_settings.control = value; // 8bits meaningful + break; } } @@ -236,8 +271,10 @@ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) // shift right until we have our normla number again while (mask) { - val ^= mask; + // NT4 drivers v1.29 mask >>= 1; + val ^= mask; + } return val; From e5a566014e6133a876a53f5e0864ba0c133b9fd2 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 24 Jan 2025 00:33:27 +0000 Subject: [PATCH 067/274] implement prom subsystem, which i forgot existed --- src/include/86box/nv/vid_nv3.h | 3 ++- src/video/nv/nv3/nv3_core.c | 33 +++++++++++++++++++++--- src/video/nv/nv3/nv3_core_arbiter.c | 3 +-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 4 +-- src/video/nv/nv3/subsystems/nv3_ptimer.c | 1 - 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 37c4683d5..f0286e2db 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1248,7 +1248,8 @@ void nv3_pextdev_write(uint32_t address, uint32_t value); #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); +// Reads from vbios are 8bit +uint8_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); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 499c06a92..761ca9cf9 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -22,7 +22,7 @@ #include <86Box/86box.h> #include <86Box/device.h> #include <86Box/mem.h> -#include <86box/io.h> +#include <86Box/io.h> #include <86box/pci.h> #include <86Box/rom.h> // DEPENDENT!!! #include <86Box/video.h> @@ -727,6 +727,35 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) nv_log("nv3_draw_cursor drawline=0x%04x", drawline); } +// MMIO 0x6000->0x7FFF is mapped to a mirror of the VBIOS. + +uint8_t nv3_prom_read(uint32_t address) +{ + // prom area is 64k, so... + // first see if we even have a rom of 64kb in size + uint32_t max_rom_size = NV3_PROM_END - NV3_PROM_START; + uint32_t real_rom_size = max_rom_size; + + // set it + if (nv3->nvbase.vbios.sz < max_rom_size) + real_rom_size = nv3->nvbase.vbios.sz; + + //get our real address + uint8_t rom_address = address & max_rom_size; + + // Does this mirror on real hardware? + if (rom_address >= real_rom_size) + return 0xFF; + else + return nv3->nvbase.vbios.rom[rom_address]; +} + +void nv3_prom_write(uint32_t address, uint32_t value) +{ + uint32_t real_addr = address & 0x1FFFF; + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=)"); +} + // Initialise the MMIO mappings void nv3_init_mappings_mmio() { @@ -906,8 +935,6 @@ void nv3_update_mappings() } } - - // // Init code // diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index bab378b9f..88ab74fe5 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -189,8 +189,7 @@ 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_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) {}; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 3de7661e3..db657b63e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -125,10 +125,10 @@ uint32_t nv3_pfifo_read(uint32_t address) break; // Control case NV3_PFIFO_CACHE0_PULLER_CONTROL: - ret = nv3->pfifo.cache0_settings.control & 0xFF; // 8bits meaningful + ret = nv3->pfifo.cache0_settings.control; // 8bits meaningful break; case NV3_PFIFO_CACHE1_PULLER_CONTROL: - ret = nv3->pfifo.cache1_settings.control & 0xFF; // only 8bits are meaningful + ret = nv3->pfifo.cache1_settings.control; // only 8bits are meaningful break; } } diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index ab19c1e61..938389054 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -105,7 +105,6 @@ uint32_t nv3_ptimer_read(uint32_t address) { nv_log("NV3: PTIMER Read from 0x%08x", address); } - uint32_t ret = 0x00; From e760c5311364992639505c7df5323d0faf7b13f3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 24 Jan 2025 00:35:40 +0000 Subject: [PATCH 068/274] whoops, missed comment and actually print address on read/write --- src/video/nv/nv3/nv3_core.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 761ca9cf9..ce67ee46c 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -727,7 +727,8 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) nv_log("nv3_draw_cursor drawline=0x%04x", drawline); } -// MMIO 0x6000->0x7FFF is mapped to a mirror of the VBIOS. +// MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. +// Note this area is 64kb and the vbios is only 32kb. See below.. uint8_t nv3_prom_read(uint32_t address) { @@ -745,15 +746,22 @@ uint8_t nv3_prom_read(uint32_t address) // Does this mirror on real hardware? if (rom_address >= real_rom_size) + { + nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); return 0xFF; + } else - return nv3->nvbase.vbios.rom[rom_address]; + { + uint8_t val = nv3->nvbase.vbios.rom[rom_address]; + nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address);# + return val; + } } void nv3_prom_write(uint32_t address, uint32_t value) { uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=)"); + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x)", address); } // Initialise the MMIO mappings From 334dbeb88314871f6842989352588fbdc304e49c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 24 Jan 2025 00:36:16 +0000 Subject: [PATCH 069/274] and the value too... --- src/video/nv/nv3/nv3_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ce67ee46c..4f317a325 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -753,7 +753,7 @@ uint8_t nv3_prom_read(uint32_t address) else { uint8_t val = nv3->nvbase.vbios.rom[rom_address]; - nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address);# + nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); return val; } } @@ -761,7 +761,7 @@ uint8_t nv3_prom_read(uint32_t address) void nv3_prom_write(uint32_t address, uint32_t value) { uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x)", address); + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); } // Initialise the MMIO mappings From 3edc11a68274f0371b09d2795aa7d4c3521c3c84 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 24 Jan 2025 23:05:40 +0000 Subject: [PATCH 070/274] add ability to disable cyclical logging --- doc/nvidia_notes/2025-01-24.txt | 1 + .../How to optimise riva 128 applications.txt | 4 +++ doc/nvidia_notes/multithreading.pdn | Bin 0 -> 106011 bytes doc/nvidia_notes/multithreading.png | Bin 0 -> 117148 bytes src/video/nv/nv3/nv3_core.c | 8 ++--- src/video/nv/nv3/nv3_core_config.c | 8 +++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 1 + src/video/nv/nv_base.c | 29 +++++++++++++++--- 8 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 doc/nvidia_notes/2025-01-24.txt create mode 100644 doc/nvidia_notes/How to optimise riva 128 applications.txt create mode 100644 doc/nvidia_notes/multithreading.pdn create mode 100644 doc/nvidia_notes/multithreading.png diff --git a/doc/nvidia_notes/2025-01-24.txt b/doc/nvidia_notes/2025-01-24.txt new file mode 100644 index 000000000..e35818dc0 --- /dev/null +++ b/doc/nvidia_notes/2025-01-24.txt @@ -0,0 +1 @@ +THIS IS FIFOSERVICE!!!!!!!!!!!! \ No newline at end of file diff --git a/doc/nvidia_notes/How to optimise riva 128 applications.txt b/doc/nvidia_notes/How to optimise riva 128 applications.txt new file mode 100644 index 000000000..1c20aef2a --- /dev/null +++ b/doc/nvidia_notes/How to optimise riva 128 applications.txt @@ -0,0 +1,4 @@ +How to optimise riva 128 applications: +* Ensure any set of polygons with one texture is close to a multiple of 128 polygons. +* Try to sort areas of a model with one texture to as close to 128 polygons as possible for efficient submission due to the lack of texturing. +* Try to have around (32*128) for nv3 or (64*128) for nv3t polygons \ No newline at end of file diff --git a/doc/nvidia_notes/multithreading.pdn b/doc/nvidia_notes/multithreading.pdn new file mode 100644 index 0000000000000000000000000000000000000000..427d4bab81b03e52de811f80ca4dd777b63c366d GIT binary patch literal 106011 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≠JI|LU&66?u>f>oZQ43 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
  • {w;*^ab z_sT!gE}4RW!dG;?sbD3rU@vVz(8LkK@8)>QWj+42TcZZF^J;Dj!|7h}20a+KYw{7= z``R#c#XF>OdKWUT&0_hqcESj`J~+=mO*7^(m$g9)KHvdsa5%~lDT4aeq|gIBmGfGY zXOfNroQGq&Q#EUF=Hp>2lthpFXz^`-r*>96#dYF!Il?blz&eDjZKkdD4n$Puc;{-! z?pO^B=Z{l;hmf+|Ww)>rG?|}=-`o6^cujT|tLi zD%}{Sx>W%&1*4{-2#@=kRxGAJTgdc*=^K!hfX7zId4mInnWuV^K*ZMqmH@s+LA&c3}M9BLq%D&gD{*ypUnp+aj<;FUgeMH3_l1$ z9Y)o*BRjC|0Fq<);QVR@f9fAGpPNwfD-6qRcQnp;4729c+yjCknBrV=?vGyf;74QH zlGaiEO=membM^tSst>$w^=Buvh8;-TAn0XQ4K*dSwI~skNRPAsKkpzpk%mwXJXV!y z%7PIjd%Lklk;*@xRI_vM^wW<<;Gyag2cpg$D4Y6rW`N3{X9k$dC`^k!wlJg=WG^lh zJ#zW$P`L}7{-^K38apvDt9n#uMNyc@h!LTftKvIepnT`Sw$&2}o`M?s7=Lo9!+FC0 zLaJxNhW0G?xB8%WdYq&Q!B|I`la52!ipHwGl^Ai*fS5iCX(`pmDvlkV7EYN&ne})x zb|Y3^5uMi+0Um&oh(n4llEP3?JB&4It^U%TT95N_{I)9lL>=9oj87_T>cD6Nm~_>$ ztE`3iG?_L&Kb^)ZmpeVpz7a=X2?^A07q^T$+)R~sT_`%6TJY^ah36N*3qH8v^CHHV zHINg1`r`#`B8R+Si#oG}AXrUn^@BjGptE?@0ur%~LsYjmte%2AQTogDu!3U6#3_ku`uR^@AuVuhsv9}ka`M~e``MYKaRK=jdO|^q8xy{Rt$8F z(}+mcb|zIC@Rk<+t&vk4+^lPyAVLbVUvbfKvD@+1kN52Xh9ZO(;jvJ>|@zq`e;e7t}Rojuxw1qn&lLS zQHYJW=6yc=sv_fpO?~}aAg{M18e5RQr0^Tlqhnklpocurtno>BXc5uk9j~K1#mb8u zof1S?T^p4>i1?jT>yD~PKU7XjV8zOvWQXrHsXByb^mym8FgQG z0>6n-;CbS*$mk4d+zZSd(hO(BCTnX0LR0r>&h786BPi<|Q~v5Y!e6-c{?IXpQM#ua zSYlz{TVP`h)qGP}vBgUHfnVo6@>!JwD)IE4Pvhr;tbGeh>W{m`4jefp9`~&O;4{mI zS5oTb&Pyk0TmF3C(mQM*7t16V@Mn_YOebYdjHwnb^V$H#dp288#pLEexXgf zw#y~!YS{DKoOrA%7w}Fc)U;;WMd5UAX8Xy(?cTPcE!$_w6U!Ygt}P%YOCLn_2mX&l zMSs$ZrLjGnH+}ZG%f|0_dY<;)2SF8iRcsI#IcZ@x=KMJGV}Rst3Bgku?@Pf%U2(E0 z@dvP^uFXUmxA*J0>74X6;%nePTKqsxamPLF48!{z!Tt8q;AQ1Y-It};45xH~tclI?}L^c|fvM1A1jzD5uuVGPxvw zDQVehh-HoOa4H1cT)#xSERKU%GUti|e-LOmJrr$UKF5R{d^qZMSED^+A9%%iK(RAw z`qxV}O5>G5HNdR(R1!a6V?eHe6O6j9F4m$G$?BO?gS4Ni_y7kqw9` z4Zes&%G1TI(IprkT#WzUJHD{)T&)NV?-o{mS;7$;^vUH{XP)TLjcTs6X{qo=PmvNM zpi(^QSGvos>~X%^M!_LwM08{hlE*#wj=!Cw(a;OXk)Mh<4<;5FCG_AE^%%&qCD_Wu zrICOBc;I7yBQsjEamz&`P}+J!B4pcldOom3DU{;wqUuN#ouo)C6o6lU$ZpMyAFHLo zky-{c+wDSDq7Ff4ue51#2ao#k`NSYj?}l(zmq&fQGIxBfyIkzrL;n|BZypcj{{R0E zPIcNi+K}x;QrQVHmKG&@C1f3C3o*$un8xT-QnqZ_sfesY_H~pKvQ5^pkFpHL%)}VO zY}fa4PMve!pWpAgx%tP;<+VJYkLUCCyk6J+6`3Xp^bsO~h=or`G>fu3Z2xB9C7{tT z$~y`C@8i{$p85&p8w4FjUe$%psX$?u=u_KX&h<4OF_s+?#yWYz$BVcOLxnzds0b?= z9NF62xM>leps_@`hB>E2bevJ_{wz-`bc=7?3cxz|l!`ZF5t_B{fv2e)KQ;Q>rqBD4 zeWb}#^|Egd8&k^KKp7DP|3XLH+MYMZ;jOR-gw0P1OhQgozb<|ZS@)oaAqUzCyT&@! zqW!50vJ}Y5789k!l{N?1BznLNd2@8y;$6UgTJ$F9XkPfmI&(*1eCpnB6327z zTbRma{?{(UT}#*ZD8K)-NlIM31&o26DNyJO3{%~xIqENNwXDM^T13MO{aS24Z&Ej$ zfsCLW3yok-%pBdvK9e-9NXHh)U?S+zS)KxFwa zCuW`QzQ^O8*Rj3tPwJU8j*%ubwHS2+b@yULXVsbuAOw>zet1v?z;S-?GFZEI;&!dB zhF&pq-%Qp3p{;#?pBs2EVnSX}%yU*kk3}}aKWvg^vz+v=MR-vbk9ZeCO&EKyUmkKb zH>K{Od+C!?6^uO4FVIU8gv%lC-n6Z_0~7ME8QJ6C*eSzjaVw*xLB)Vf5E(Jt)K|ai zG;?_cJcK7Y@I%wO}3CtXDcKuY}Fa@*-<-j?=-V2Oreqq_ci_<(y z>~7uR*OLlnh^W|;fpzoKnv*FNUY}Msu;7#zxm4EqQthF7N71$MJ5By=!v3B=U3~%x zzc+W-wJNsmZl$z(>8NqSsP+l=kvOP$ zx({Pid(MM5zi6>J_4WBCs_U&ECM7pquwFL*n9_fga6)yXrM9jZ)unZprz4Cg@s=$d^0k=hMo%+7-4xZaz zYrbVOQ|Q_p=D}{hyqy2tgqlV+XjZ&9Z{w+_|e;o1v#BCA+=E}9ii&-T1m`nCSPG#C%SmL83`3?cA3#d_0Z zTCG~O>t#>S&;1jZ(kJ%U?GMI$(DqWicdA_G;-KCBvXCR?zLaO%ooxHU-@4-JBav-b zb%|nR^z7em7J0~*D|`=`vD_!{nl!aD_1BVYq$>E00p%*adoF7s6#v$iR%7sgCC^c` z!Hv_0wg+JQhyGH4$Ab^vf%6lBc<>g3{Ted(w+q{!xeou8L6?9u34^#b$vQ>7xGT$U z{|?n(x7kAR*XeJqY`m`96;mbkZ#ektk6pys)_u-K?4BTg<-?ojz@GU0pHD2eSUt3S zs5$FVO@#>YO7`ihcxwHhW(MM2XU69I$L$~D=CYH{K>kkyrc@;AWVaq{_&I|qoRRo{ z(XX-|NQRF+)fpdfL<@0P$!}5!Nhi>~5c^?FsQ!WqdS81bP0P|gX>B!sHpU(eh zqzXOQ0)9r*U}gFjF!zUm>%Sv<|IN+!Hkl2#?D~$D2zl)A=1|~`cXA1Gaf+`+JEV;Y z6AiQ%edNRonvAbM(|eov=%elvi8uUOl+`2EPmbfO1qFSE=wYOXgstdd2k$s_My7vn z^@sySc6lP*<9;^|Vn#S2kad|(aH0oc=*z@l20z|3P|kEt9&wMkUA@ShQ=W2nABg_n@a2b zf@yE39!*I})uuZgy7oM+Py43)k*H)FgC{b8*^m&#cOwnZ)>3*%FJh1NA-H>AJ#1TT z`{wvGj;;CY(n}yh1(5l1uRpBK4K6?L#EmrL+oaY!1W(-xy=AbYHDKh!?TVWvO;W}T zw&vYn5EdtS@FsQjIE}E%-$9K-h^s6(b4?8XzHTT#kBn^SSkDc_(W@d&55%8iAf->w znR#K0%`>#)`^oS$ep@$d-Yx1MXYu2GZkfSru&KClJUY@t!EX+0 zuTxg&VRW=&jo@Z2Q&p=e`1f@!GyQp|7debXlvRq*`y@pQNk)A zL`62}2gEKt5fr(tO>(ZdDR1p0_|}K(Esl&*c*;?J$gVXMqh%R`4|(#$2luyLs_`p} zC9-uFZvE5!LrnuxYbB9lwG>SDZ}G23hz1^NgSNZ64b^=og$?5*_LqR12tg567GnC@ z18$cbMpz#oP_nYJ(y9Icq%T-%;6TRD#Sb55?vJMQU)i}FksWVh|SD)qt7%MPhk`;0iY z;tHvgXmy16?!0&Yz^sE;DJuw~{)eN?j}aBg%O*A38wA~9SnK~5Z(^nt{qBWwR$%#q z1?>6vMC`3x!#ItwvZZt&;_YcbT;uLdSAKAhqoYCE3&6UTGrB~S2AaQY5bx_Pq`jpp z!J%AV$qxNuIxH78t)50xT4W2gDlzR!5vsUHmDLd6?<($EB<5X&@T@mPw=_(y3a3;V z^ICPb+=t)4=gT;p_v(Sxvyi9a3pYr0`mvM9qv6v0vtqlx3x=Qeb9DXNR;5mx4zV8S z4^j%lWTbjcm7c$Qp#rve&owU8y+?SKm>!$F%w~Y>!dNEDg`3fTDQRO1qu&0&Co-*K z4MtKUAkP(h*t&>OWq~OSUGg2u=}@amDY@UE?BO#uK6GR_PXAM9m_9=#qU)xn7rHFn zfLlB*-rU=fdCfr#^*sVZyaPSQolB$I?rG{OsJ@ynNvuE)T{NPd-{Ekey+KUr3P_p9pA1(9=b zNTm;IeAlMU`5#_f_4l8i%Y|vo+B|@;wJ6L*BmF`60VfOYAhvxn#CFI$WC`McS%<2dfWP&$boHfIDB!$^fo3Pv55eh-L<_}-jH4{8^)qIvg$LuML4A;WwMPob z(hl^xVgJ@R`Ab8fcfiW1pyqV2%SZ2G&_?c9EQ5^APb>CK!G1%2Bq0Mz<0+n%Srm zxO_-EU@^SZewe&MPzHJzrMi;XcrN>{WzH7$BzbmB(M3O>UVo7_3Foq2<dwFr-mja_pQ(|aHcLUTSNO1j)N;nVwM>+91fecbCE!yP zTT6qq1fU2}wye032-x{F?BRD&TJ_7c;j&^z;Q5Bwt{RnH9jIfOG=j?rD|oh)2b5xp z-(28hY)@fQ8Q220k|cFPJHEU<`61Nu@`__|Y+5IQYk9Oku-=I?(DdjCSp z(DFY~WL>%(su;LD^sLLmWvtFmy6dF^Nqc(c!y}j2is04T?C)&?59hIlv0}U37M5*Y zI5Y+OA(9ljjjfp*b<%Q;U_Sm^hxmB&8X?Bc(!zei&2B=>-o6Kv=i!<#2G2XvHKx)D z4q6KTafyR{7CS2q_(?eXteUfnXwL6jk{wNg-zf!7a9H;e`+XvskbH(Lq zVSULz=KT5s*1$C@C@vsAa!9557togppY zG{4h})7?k~W3|5i1CSwmb)@JWu-svqs(j3A@|0!%D==u@`SR-gqE2?`@~KRn5Y>T} zAhI3a{~B&CqA(=;!I}|B#gb3jTML3Qt9GpySrxxou%wO$9xVn5Xm@x)JnC+7G`Sid zk-gg*cOGh_0hBs6*31%_mHj;(MGxcxU?ol~})SbYgYg3nCWZ4tA-! z+H81y=T;{264)i|@iiCq}TTraq3s?}}w~>hi zg(|)Urw=rF5H828Nl?dHP{@{z!*XlB=LJAKr+dIj-fOnnCujIErci4bSs;+&1IyrIKIO*iqzbJg8fh8miR%+(UEwx@{@p3| zw-Fu#$Oq{H()Y}w*$B)Xd1$81Oxt$9t>y$R={05BsrotXsLjRZFFERc0p3TQJl&+?zJtY zaCBw0{sCf0=}@e$IIiN!S-{c9+xt%AWt&gJCTOR#V{AqmxT2`G^M_$7O1NevE-xX$ z0_Q4jcKr4u?Y~g;yz>Uh7Dflwp_k^I1hqQWKSru%{*3Q>U`$kDvo!_1 z;@k`TK)|tNHM&wVlsi#=YkJlK$)L~!_tO!Og42LwCCCL${L8uqPJj!8?5;7$qCBQEaSW%bh zM?DsL(&AfW%`<-;1`}Vul~ENj-Oxi|oci#!?Wy15mt$f&!OAz5L6tH64(dg@H@@tF z3rLRK?R)?JePz&%zCi!5nkKyjG`xmRQrNX<$D9T$O0eT2?+aIvz#60-L5Z3TUj-FmQ4#km&kQP2jeZOky*j(!5Ppu&9nk<&moLaLt8Dvx)3kRjpPnD~??n$o zV*)`sVysr}c$D_=78Q(LKQ-XRqPvslYR87H2nZz}#z4a*A2@ZTIr$&>+k4{S$(QEL1hsso zW+$bk1R}3o1I0}{YP(p#V+G3V_pfby-DogIEohm8gnDU|RZUt?J&4TH^K$gkcgJvr z9PT~z<|3y8&0O$@=6H8=wx zR21h23cyRm;ART!LfYMudRdF?=wvyY+my+AVg0ZNbN+!XxAZyd2cI80^-aqh)NDr@ z%nHmDo{_RTL0Yg`2)DS*L-?;gUEn!aC~T$LsdatK!e{=svfuGy-nuoj#+!BdEyjCx zLzqgIWWObsfT$6+O1~I!hz0ESfd*REU;>fbP688Y7worBQL~jey{Pr>-rg85?AAg? zZhQUL2vG;(4dlfM5UQ>O!-BsJ#|uo;zMZuOExsJ|MBdM6I?ZEy>1yB_P437g(gq;; zUMQGioz+j{Duvsf?I5BJqk%>T_3GJu@Wf2b zgzrUg`QkSfi2n*Eh;3j&B$xSa@}^|&g(rJPtG#XVI3UaZJaxMH8fb@C!7~$vL4c@$ zyhGh@=GuYFNpMFnXNp>`3|P7zKpNSf;uLzyuIBDDko*bw{FN8fnj@QU%zOfLM;;rLybTrq*bT2f@RP`q)z&}D<{?R0KmFWUe9{Rh3U zG6$__QBD4^DKx-|ovrd3?y3MlXg>|L@#Uhxt???)c>QghW~|FH>s(_92u!U2VZBL2 ztjSCHXFVUb6Y|JTVThxT{=x#^BL9qa@ep3k2X#5k>1Ubit-)SF+v2H42Pr8$SlAQX z+Ac}$xB*sSmbb`V$#Sb;@}a#@0%A!&9ky}!s&yGW$6yt-6D$6u z=o;DAMZ6&YMe7sX(?kICY1@i8BO~eEiQs-os)= zDYYu(O9Hq-0W532soOSP9xE_1N+oyA9>|i-E&?&Aq4eb^ijEDBr+e++yMC^VwPBqH z6LDT!TiZJmH40#!leC^**05ak1sO3MHlz;9_jm8>HFw9Zq;Z{G-d06MM)Q67rPUQ8 zE3Qhggm8DhdbWb{#D|LUM!6wAiVCSD4FA42~5N@~xM66X%z^54{%6SqIzEWf4H?~EVa zA9U4)MW)*fIm?W0`c!B>|KrU`sV3VTZ+@cxC=$0~Nzr3uGi*tYQH=#FV=M>!TA#oB z<;Q1sdq3;+m*Pz!UUDVB*>&mH!t??3;J!9x)74`jqXz_W{q^fbXk}fbgOZfmRMT96 z)cbaR*{^ABW`v5?`K!#1ht$D^6R!bsh8&D1=DI59|~UZGVU*XbW1qm z)YE@&``d-(53y2f*=bQt7Y$x2gkF98qT&DdmUi!pB6V&p{0iu6=QPg9QY`*Wh2_pMv_Cc7sefd1CmXQ29vFmOyv<6C7+6MxWP4`H@u^xPM2c+5i zONYtA)U-oJyx`A~QFk*J6ge&htJmsSlDhvoQvT+8>#xTYF8}Q7iyR*1LtFK~onxI? z!^-eu9s&yeFWK;C@VB3Nzy~*ukg%e7K;b-K@?)g)`bRGqw5M;iKQ+j+?|&rqxa#bs zM*fIf!uetYbC6Ai@TZn+3bgN*Bd_zJ@TJ{>*}r_TP45OI9Nl2+SRY}CJzwhb^rxYt z>xTc{QqcCA0Y!}#4S&`-^4H)A|8vVf-_`E;UQhHNRwri!jVOhyLnfE1Lmam6Xu}6U z2j*+WW8R-#M0Z0*2~AO-)feNqDLiEmuSnQj<+ODko+N2Vm`Gzhd=TP)L)9Hmm@JDU^OW2u1$LDG63(ThjbH*C9)(gYy$+Q;#hJC8kZ@T`dzLy)|%l4HyNJ-t( z_4+@(DnGwCa?)$z09fzNx|H`VMC8JijJN-^_BTkg4!kksFiMvXoNN~!`Gl)_Edd7^ zv|Et;PzG9YK6Tk&s11Fo}ZZX>wVXhrB8VU*s%!T+zJ4xj34Y z<+Ds7mQDTYeBlBN;m(QW8~r%MIs45Pra|{;M(cKqj4cv616Q&64>DP{Sn2O~KMJoq z>i^=LX59XBYV$l(4Gqi>#uxhXuR;a2j1067>ndNk()O>FMABZ$=oQhL-M3&6zUxh+ zKpL~be9iYyHNMvNQ;eFsqW$>JD*Bsjpc&Fi6682q7lJEmiB_|C>dea|y}t806PM8M zs#me1kWIzg4ju-Zgj^n-qy6|WK`w)1O+kC`o>l(lXv*`w!?^w8PcJjeC57&K-r3;` zveGOuG=7tv<75RXSXAdPf?K8rdWYf>mt0FCo51L7Fy~eaU+1Gt&KxvwO%`)ur9_(g zjlq`Nau+0?+Kl0C^D$qq%S#)$Hbj~RdDppwoHuAu*3y4J^*~D1$)F_=d-0h+LZRn4 zEu_S9-Ff`vKd$!}65JO2D$4E)U{?Yjqcnv5-~bG0^~CU$@H>k`8XxWkq1gb-?93sXp%>HJOxgkPAm(o z-uLS!0R%DKB%$ddv3P=^C07zbCRT+5)emN-2Yn$s`GR}o(4`5hKPB9=a{WNOM{m>X zzWDWLcN-ChX^FW8u8CIwdoN21z`nsyfekOjyx~w4r=dCeZ_J)P#w`QV)LIw5zVm4B z_DZMzN1nMq_4vv`UgreAbI9s)096sNP-ahpei<58b5^i-UoS4}Rhf+UJjuSL&yKKXCQ{xb zWd_aiSa)k)DL%YO=+9@5w?gZthQ!^QLZ3Z*xbeuLS<+=XB!)vhD#FQfUXF?Wj_q(? zAO2vU(rgj|pk&s1NH+n|_0NIdv1vHS%x+5?oUhc6&^VA8a3~%$Y*9^%%}0@$vm2D& zC@NaW{PTM$FwBa{OEHf3Se*#lHN9$4i(c>4W)d=bb$3=D1LS;Rl!4&Bn0M&pLZD`g zao7Le?k2Zo$x|^zH0!W#s>VMd{)|XNN*VF_A0FWioR>M#My-I}fjXSFFS)qkPX(!V z^{(}1!wXusbyNHwH$+tZd0K?acz6!xozZY`Z4*|jRPDCZ+Pssuj*zv~6j#rEw&a2+ zR0GFBj+f&ja5KmX6RJ0beY*FV3b)@^Z2~mYuGWH-Ba? zRmL?4BJJhEuhb*)^Tmre2DW15gw(<28gG+{SN&`%p*Q#snxxcPd?8$)0P86}{^c`E zr_p$Ji(nQdVJiBsBJYDZ>ey5{8e|X!J3y~!F29WX(w9ZnKJgTARCxnKTCc=|Jy|@b zABALO%RL2#E1#J(iP0}4blfY|j1YwIL&^=_qDDr6{4%^c^1esuYrP2=Lu}Ck_lu&6 zUKL!;H5oA5E4S{gX}1sKWsCx%`a8axX4U)j&04uu1{zZ1_wc^6Ab zHuQZrA+>e>W*)~Up>t^HrAg{(OZ1%xpm);c*H$T82LC4EL%t~%X)hRhL^(X}x}NM5 zcJnNx*lHZTpH20B$hB4vgb8dCET!9tnBr`;Pk>$)4as{9U_IdBBEFtO2VrPYMdFe_ zL!R}A3sUzBX@1(Wn{Oi*Gp{a$*(uDSlV_VYyp2icbUh^n$7~H=(z+B&T7eoN!Kp-F z8^N)g1`R{OO&j6`+5HO}M8#p~pkpOL^rluW`BVP1f*=^Vq`aR+_K|&N!pb5feB-=; zNfjo6Y57ko@@qIF2GD}O;aZDqJzerPZUuaWwOXl;V_$P>tL@*rA8>vNmJHka&ig6je;lPyU?FSpCu5Lz_H6bImZM#uZppHIG zyU&TeYr-OE-QOe>g?0S2yfZq>2!X&Bp+3OrI$wviH_#Z3=SDu(22|%a8n<$L=_F|? zqS90CqeE>h?R)fww{`&uR~>dz3SXz4GXEW$5!IP-idLJ`;GgdOK`x%SHtoGk55&jD z=oD-b^0O&w*%8_lL{ad*FEJQ2yv0-P-WwB_ai)Mar=NqggA$)m2Zl3D?AE8K^%Bz z$k7Psf!6B^i|E>}uV}*VO@gj;yC#Ow6qWuyY(v*zJrL0fNZjqp0j0AX=+$|e_A^f` zzATO>xl4Q(6x>2(QLIXV!Ywi5;>gn3^USKi z{%r6Gd@L6sY59^4d_BzLd{}ovxb;zLmJJx7eKXpC7Rm3}i&E{i35*i*^}d~9uXhrG zWvy|eM0+uSg|`g2X-d3Gpe%4GpqM-ER6Z)I11$9kl0%=&M_Rw?44Bh98$n5OqH zfT^KIe1uei%31@Bn+7n>v*i(RWat4iw7AEB9b3H_;I%N1k zdl>Fj-x`)xBLzT9{xdAX0j1xxq*Qx1n?ESbRd{_xlhZ1%+zYei;weP2bj#3zp<&$^ zaFx(uw$*lub*I<`rT!5pqYRHpw-NQdmpPY;+NLY*TiZy}Utfxv*$#*eXyrTLkFxe{ zP^b*p1xMyYezx{kdBFbplv+bAG(&3+-H1drWvHGwkpE9keF#zz+_yNhhov>Nz8dnD zE7W>nI(TINBm?inc>&M?#Vhw;tC?}iKpCIWX2NtU@$U_0oEkKkLM#V9;h82%n+ge9 zowan4q3m@AVA-|;Ek{&Z_L^~`RN2%&&*^71<77Vrl;P)W>i*%_xSu}oby30wke146 z+|qMI7ZL4xYf|hUu?XVLde)ubKr~ppo~3?j4yDa~$D*VrJ)en7fpeLQYH_6@EfIf2 zvTv82l8sAi2`H^_Z6Y348!-peUz64Hn^J5va%x|0&9$E8RF7orWMdkL@(FAVH7sZE z`uX>##sk51kMjJB6FXZu*_uZaDmgh2QwGQ72^V1omv`i4=p8EnP|_V^*f^P_+DIU9 zJ}!L?yMq##UC5#Vn#FDE`1QtwG)S-#PNa9oCc%#fDBicP4%N+c3c-)6e}Qlhd_@mq zHQ$444XnxJ^?yU|q37D4Q+gZ-YLv{8m-$Dy2uDmD{r)CQg$wmoApd4*h+i}22fXp0 zoJ3c3Fz%ZKLI|$mo&g$>&V-uGi)F8h(RPe{;Ep6=UwqenTtaxr&0le`(SJN8$6N~s ztZH*Q04Bu-mvdtH+CL({FI=V>_3G(%2> zy@I#v1ug5lEa9|2IMnw12kF`P%=q{u5Hk9Onv1{QXC6Bd|P zEHjiL&?=rk?I?)Ujx#H)EQKa&&!KC3W(j-2v@5m2lPK_y^WS|LVD6WnF*{h97<4ec z2fJHz67W6pJ>(eRJw-tYnp>k>B>?Dn5?NwZ$0Lo|rslBd`fOHs@bZ+$+W>XWCeeAn zvYSm_6}oMxf~3hzBQa#Fpo2*y=DINd=kN3p!2J1QHxo92S(xrzeR206t^4ek-P?dU zkjSI6e?~(WcDVIdNZjvPA5L+IS^(ob2A9ZF;=FN*f@$gU&n+amTDFJ8(K>W_?g}`+ zv&pWL^fh6^Y=}XaICSx@onQFQNg}2(^CvN!uTJ$FwTHu#=~)f5drI_WhfKP|4kqEV zAXms&p`mOqO!Ax(+n{zD>e>Q%e;$%hRbI0?u{c zAaLOCH2MnZwr?$*g7Ec)7e!_+mkAjuurX6=wmBB}nFRA`;UlYENW*BAZeX1LN%XP~ z?tDJ?d$fAp;HA0lqzChFhZeL8V=-p1s5DYa2FcT3^P*W+Ru4UqHwR~y&ra&}+n|l? zE7*eRN@~RcX3xV4{5<(jzJD<8c!5>G;%fy^lU{mAso681!N$6zRCiU!> zdrike!0dLw&b#-%Ag(f|kP2{6p2_|VDlKYJUr+HfeEgd)8c!&giZ{NT`P%B#vr*Yk zD{{H{5lIQLWK-2ahN)yPHs`t6L8F47O4lhrIsU~mAnSJP!{$9RypSm=X7mQOER6VA zyP|qBul$w$LETgHjzClG!SIV0 zxtjA2eB9oCIcwJnWcw};{4@QtgO&!_8nh6ls7~g<&7qG#tTLHe_649MeWmHCyUES3 zWBr41LbGViGjt9X3s-itbPVXN>?<@CjE7Gio%-wxpEdI3V9Iu9pOiY7r@e9I-dDL) zZ(Ev`_;nqWrx{b|6j|a7c_T)QxRxKoL>R4NZER-HPm!dkuN$K$lK)8zkCX1sO#8;U z>QWJq@cldiKCfAb0OTfRmeOpx19j7#yv6722|+gcqivq86kDFEeg`{`7N2UtJ<1$( z)X2~B#D*ht_@BU+&s=Z9Ae&ka;-|kI5|Sis7^(g&LtGe__vEHjh3NoDoBr8<%D7&g4pR54 zv~mN}q5gqko(@HH?9)(kqYW6Vrn@}qyxV%J`A)B6F&Xc5rL&hd_W{t>q-L2<-^&;w zx~^dq!8_2=q2cEKSXF(7g{Rud0*n=Z_b6?gNf7mC5}cgIZdh&@&y9Aj{7g!I^{o5? zXv6@`rHlX67LKXk7NzbRLG~WC_RQrXcGMCoQ0|3bN^o}>wNiHMo5RElKv#j7J_Q9s zuh(?y?Trab4CfE12gA59JF1rxm__!W5E}3SkrLU+X0>mXRQU2@^SI$8QE+epF2Y3w zn}a2RWCgxv+$=_r6F>GdT>duT{u=N(3Si++f5&O(&7YI&2r4UXrPi=QzSE(_KK1%F6?9 z)hlx{YI0SVixD=Gu%cpa&=FvVc@anBNBJMbTpfP=pYs0{l09$P+_4(hovM1Fe(uQq z7oUA%FZ(5!7$|1#Ozb)1`}E$V=M|Yl{IL$F#p9kwnEraK{><^=8-rh!Q?(C2HHyfL zCe3uAhc(t z*j+<2%!U!2Chq3mcK43ft8a){>7Erwca8R*6K)S|LhgR3#|Dx46-97g%g81a95g8t z(ddmdZC^E+Tvg7lRvic5;77<(NnO!FP+1>yd13q^JY|QP{l|vfVWJ!bms1o>sTRR0 zxxT-|m8oB7(IRV@P`)aujc>9UOvO1Y++w;rXS=!Sf4}|yke7L63l40bGY4M#{7llv zZnyl1(wq zGnM_Tx=opW{mGVCul z#+^U%l4X44J-v;J)paFq9L!G6Lnl=|$$On%`Wkx|?k-SAB~p|tV|?z&b)%zB&pl+_ zr~{jgzrC=G@edVRBRHF=W!SD|yQhwwI=n{s>c>S4BM+~A_GR8~uPe5b(PF^W`&dq& z7_7)Na?+rM(i&l=#&zGZr-tfg9^xREVCdXm#DIvCMg_(ogHbJkdwNMJ!Pl!2lXYBI z5R8z`zc1x9>Mx(iH<=A&-t2f;H6>4^mpV#X(4C{9#tl~n4zujrrig`(!h{=m*fsu#+ye`mPQ;cJ zC#bea9z8UNRlo)L-909d^CncWbhNfM=;>_c9CMry5&3=W zIo9!PT{kDpctBS@+qUzm3GtT*;#^_A zzVyVNsr(MjH!IOxj;1RZy-o)opFe+d((BcB+aa73Al5qe__%l7VfvlNWtkVmC z`^L&qc(6c?(k7EIg7XAk*Y({?Kj={tM+^Y>cwHOFs?hVg7@lW1$;$ciR`2489Bo}V z%Ew4DWqmdeb+a2;0q*W}TFRa{DaxCGyu`IvToFX3u=st`)0(kM=XuTvsQ=A?;QPy^ z7qM&`D~5UuXHxl>HA12KaWOh z%wZqJ0}Ffo`(Ub-Ha+qy3c7EWcz9ZQ2mZzb>_4iOz~(3my+p!Bm$hw z@|(NrXDg>jZK299<7B9)S`F4#Jjun}JfjEvtLiPK(el{Jym@GdzKn~5`M}H7v8P(N zHt3>vUY_`Ivh0#{!Uza84-LcBF|aUM+>L?VpE;9dy3&!Dzo0TD$m0^|QRJSr2DXBX8?q-|5TE%*IjKjWd;(D7E5{BO(8z6HHkEJQ^K% zdwN~vQ?;biOW;I@jnWe9Dt;qHeT@Jf!D;!K3%ZbXO9(A)-WrcXiLY0BsE$mjjRzmI znf#r?2i0`pEV&q$X@0IIVDrd+3GVZggZhv6HMO&JOb4nmTez}w4Mwa_R=$KHJkAyb zjW!S)!`fwBY_ak{j4dkovQuybB83z7`})_uv6?(`y`RO>5NTE9h8;p85myvO)3d!FmRfuE4tgpb+y#Y70`$nttp|;qKU?w} zTUs*wCZQ*7v1hy0Z~5M91f7eDOfJ&G%rz?<^SuA5rEJlSE9Y72+iMetMFkJj>0@u$ zntHWoFcw*9wE~D_aI%mxW>)G4O{QVO0Pge#9Zu0%BZQj2J^9@3aWS}m@Xg8RM^A2> z!8!Ci)&{JfD`){%@Efh0;~!rDl!L@mPQ+M!5g4+vAq{BH>}6ena_YF|K`7mHKOK|Q z#E|H`YknLeMt1All=vK$aRR?5?VFQdFX=l0nlMXH42Ijt`d~#Ex)KVWHbz3S9oYFk zA6=}_CpBpBkh zf1dn>)mpdVHX0`k&QZwAM7{?6Q5<^84+~IBGQ9(&f&FRewHKhAxw!<4yO_mPdNl#w z^N?5<TNt`86=89ItXFmXaV8#mx&ZSiG00xFBd1i`usJd>u}e=T92@t)rCgY_4O*Mn&-qL@S3nl1`D=uJn z9htB8Sr^)PLbo=LMdIAuuv)m_&@6E3CuDSOs)YOoxcBfI=?Ni!rX;P;6F-=Pki?*l z4f*AQxuZgJ;CDBOl|$I1Ijq2ZDOqd+mc!t7ufdG$PvFPPT_=SaY6@gKB4kK*lCS}r z&6soDUq7qBv)+UqId)56U8r~EZ7NDx=xFPklK&c0>u<@fk1+E+;!WDb$yvvYtSOS) z7PWZ)JXV0A3~)+FvlT(iE6fSy`nX3U>gKUocDLf(^=*KF z{Xn`3{!2^A$2!97I8f;9%X)N}sckbL@XcXy^|MMGn*lsQ^I`52DxVMvSA5)QdQeFD zubzM7V|M;UHu@sDMU|t_1uv26+BLAA;-*eY-Dp^0JP_6p1-=7_Oa-|K2z#eH1#x*; z#{^cL-F=6U5Z@>Dc`n}IZhwKVFN;sdaleEG)Q3Y2x}+7xfK z{9JK<3;v1wpuO>Y3>z`su##Y25?tD&PMFneR!Fg4D*mS4iQYBQ)kEzq`gO+g<+!+1}Y$P=_6^J#PW;9ML$$9{I=PwSe zb2lmDI4xSe1-Shq0`-J5T^>R=y-q6?S6upq!Q3xConr_#Gfaz0Behfsg{(g}+&>E}zCE$BsWtX{)7D6-v3mnXRRw-q~T)zxUqiw{; zba=;ThV&(R&0)io@Ijd?%sr&DwGN|SIU#u_ThwEpgYnDv9Q6HAQ`RqlX{QQ(?5?#x zPlJ5dNlwtpS7ydHC;hxO)+`wL>_>X~R4TC@)aj4#gCASEX$x+A=W@LMJJHGpGW9bD zMLL=VDfFdMA(Ot(UFf!^wxTU+9Y2jQp#Bsb5XF=xs6lULZ&Q@TfKxryamfj=_OfDqp)Q(jwxb@zG%{8f1`Jq9bzE4UUi=ja4%7L{9 zKtAs|$^Ws@znY#agRD1?#Q1!3Vi~tHmvn&H1bo5CCJ;3mKp5q< z3S0=b@0;vMpF#A_EYBW8&HPhkv3+NzZcAr|Rq*R-!^_}WQ1J?t>27+!s-J%8~*qXY1XpY1DOu1qY2W2&2uR0z~ zvRw1by?_X%SM|)oet!6NhRgcQxu>0vMy0e?(m%KxX z>mmyaH6@PV3z>49smqPW#VO2&k_#+#8Ati0q-a!|E*SVC(#Ed2Z#dMiQU4#WB53)rwcnduVTSaNx2e+z=lzeWjZY8V;x^*{=t8S4nCPGKEKDb&RqN40 z^Hntd(@Xw`jWBf^qQjkNi#sm8-&n^vrx!+d36+>Mg&OHk)YZg!ION)IQ5zGt-s^kk z#w8{)fbOtYOzA|Sm{J>ai=v2 z_5<&?RNd*g0T0QEY|?o4_3t9fCxKoO213Rv-A061AFAKwnX-T-;!`U*bTEHOowI`u zW%4!GL2FX;IwmXljG4?^)9GLR2w#t^5&G`lbw|`~i5w%sy(vE!;ouQR6Kq826Rkr3 zt_@1*3m{C-lhz-tajR^x)DZ+5iw)Qw>t1%_in5@_G?wXk*4KoVWWPScJ$*)wiOD=B z;|ggkXaks1#^t2x0FM6cXHMH{Hy} z%fk!j^`$-2<~C<3o2#~{S2a_;E4NbkU~qR=(zeQNU-EB?=CBoxrxC!&o-OLKQ3n27 zdL`>m`Q?2n(hIl3AV_!&C6Q&9;Uic16! zKmXaBw#ji$TUhI@77tabW@^%)jV%|dV_ON`yyhTu*7cRWvG%FYX&3u6Rf(a;iZ?#$ z3X9Ta1=@*Jvz|q16kI=sQFAZszKD^BhfbWwZ1aAt&3-?hd41Qay(}Q?-79Lff0&_P zNbvOPX6o_U{jr@(7%034C*G^?9Zcqs3pt^pRlxQLO^?V_j8=5kdgN%aI?_d zW@@w^n@uHtBQ9p2AsG66P0PCD?tgnHTT=}>*fTu>deo1r^+E>V^wJgDaimHCd>Lb8 zqgPG(bB`AhTVZyy8(j7_ccERdNLf~wwTjii&cjUKG?7Zn99j4JcfaH1Kvdkz)V_|s zgKGl}p=eZE=2oArV(-%I#Z?4fA@%0d1l_pehSOQ3^nMH=Pe@xy%y9FaX3v0e;BKW$ zAAH~Y$^XaRna4x9zYkwI<&@%_R4Sp9?UYc~?3E(fDxtDf%48X08e+_hR>@YBeW{47 zW8WDiA9LJ>QGRynfdd{f>?CKwSKQ2sj#K>@k#N0R1Pl=16 zD;ESJFYPdh=^je_>Ys3BDsgyChu6TP+5#Kl;uH_;gAA&i#qYC#>wf0~n-mALE}dx2 zsM&3pq1JffpDcALNmUpa|@N zLeiCY=wB69L4A&@H}N1Ili1r%(STRy^1Z^Z`>wlYB@8GY6GC;ie_S2X=j8t^i11zc zL^0M|=s^C=;_Jl9a(^=-{a#Va?`bb`hZDb|RZFZs1$&Msnjf9nwg& zMbs9f&zi29!;=k$Y9$veMAyic0s_>SyEz#$vKFwt->fH|3S%ls^QK_Y)Woq=VozrC zlfNKa)O7pX`TNvH`#R>5Ts3by-~G=;j&Nn68J=VsY}1#0x$}S? zZZE!RJjvgmg!E_CBWOI7>&V_{UJ49VgSc;)iom`2DR=PwHfB8^t>&5L1b2RqXC@@j zsN)Ncw!`A9k8R|J_oLds(Q7ltY_!(!odUr(JVlG@5~yWR(VS{&byoz6nA=2y-tGlU z@PTAcn5jrmSVnJ-*FQ!TIRB+KTIAL8_4Z)Hn7f%7O*vlwnZE?UH~VB$6lTKW1+j`V z0;cp!!+ZW?Qi0bmX=kf-wZ&Gqm?qf_|2Vw(`R41=KS3g;_5wWGHm@2)Ca=9t9HN`) zSavBK6HNVUW~ZsJ?Z11Qlyy{|X#+3ymz%#SSl{tqm%1J;WsZAP@7%R{ow{EmJkg$r zXHpV~X@YGoS7z*f%q86Sat`o*d_Lz#XANr0?2;en-tN zCzbIssdiB8ZB9Z4Da4ZmMLeLEK%D$j(i24BuMR^?}0oXZ@$X^=m^|z+WnQZ$ja_3z4=DwMiv)XprcUw^(kc+)sNeH)Uudc>_^R zgao?IKc|=9NTfd8I)Od3sYztoiH zL7)10VA>Dh0eg#C_#&onoLt?STfBAswWCOK_LCRs^eFH1hTB>8ID2|nkJC3ftJ z{2aYZ|44mSGpzVaheVLFiENqwfPt!kiLBKM{X%(ECr6 z?-W~DXZ*f{?Vy!?>E5yqA+&mTS}m1FacPYc{2dQQOhu{e@Oq@OvqP zb}`i{J(D@$ys%Q#x?-x~-h%FQ3p3L(>mnZ$NaT_;5^diQ3VDP=0# z`S1}Zrs!AIvp~Dl-GZA|$U{yUD!L`*B>gx)`F^Ps2m(P=(IL&2o0J9A!N*gw)zmzY zt{f?1Qd2di6vo@9{|bkW394xJP+}I_tMEL`|k@>k&(__yh@rh4>MTkF|?}k@V>%Q@aq#v7qWriF3cWM@m6qjeo z)9WCdqeT0`ks;<^aw)nhe2&{t?+2BVMWT z@OfgCJe*TeICZO4?=J+`)w1JbIi_t{vt%yOVwrG}FF}q^ZTE2y^Z%Us$NV;&wm;%> ziVw8~4J5ZOp2f|IR%=K3f6&L(fxMca5hhJ7t+A$D9CHD5NUy@k3`C6D8|j!*w_c0? zTzbiqlyLLBg4X-t1KG&3i9iomUkpQHPRtT{)wM&O;{hHq_^LJsY-*lJc`R8>y0tGH z{Fkm~!ZQRUTQTcSLDhmW}DT30oj4B@?`zVc{SJ8X@pUq$ilKbMX9i|@-j z2`Vf$Pcv}p$^w(W@{H`ilK-2U*CTL7)XE*yIi`n zeaZQ5?k-u!L*FNr5I`$DobzZ+!xQ3R0X9*h6g_KIt0*5iLXh%atmFMvZ&?N{4pq{` z@nEs>+tucE==+mqAb`_x5WQE-`&_m8ak4>zcZKcL>}L*oe7I+9WG+{ggx_HCL&J)q zj1X{070z9)pk%Iv&vB))Xdx6cJ_U0n~@I~*^D*jdQWMiPU+|X*OKeGwd z<>_}ZWQjDJREMej5F{!~Px(p-8V1WJxkur+?a4p1S>7~uWTj57ZkO69SVB3v^zJ&J z)qYQ=N53^FL=nYKF^7VCE~d*X?xJuKjf{s$TnRq`njBjmWBJQTbi^jRQXOY4a9hC7X!kF5zJCmK6tj%n9@9I60{EKj~)_>q(25uo7i- zC5kI}NZGtbkuy-fplFNp#6p$o<|=Is{}5#R8P?_ywPH1WrLHM1Yw0G}LmQI@lhJDII2c3G)hapZt)bgHHAsIKhx{C`#`o2&2eeWMMLu87u+ zF3KgmYb`xrhIV;yp5N?%2=|aud$3(|fpy71YRq0N>Y?Xi{6?5me2-x)J#dEG$9!sz zRnWX=1sgIos};c%$i8V?@DN75)v3rhfADd>NHY-K`Bp8X?y<=y%d_3+@sffZKTR* zDy1i_y8vW(v_-JAS@Aa!1e9=^q{?1Mmri?ouj_))gX>Wk;B$S^j> zMJnh{d&qy{K&5R!P$$QDxVD^TMsdPi@Q03o44ol>_aUkV}D$&3bT~~?xE0NEE#Uw@p zmAY4K{l;lc{C=C!{Gg5;u? zB?{b-gzs}Mz@taEbZJYNay7blL>9Kj(nXHHgnYb{qhj|^9BA`JF3sc=Ho2(w~e`vrAi zd%%R?B8Qz%-DS31^PZ{(#lJ85EK}}(LHxZckndBln%6!%f!*_aZ0y|^83W*c&g|j} z?@d+ikDb6IPe?sxkIS#^5D2ONEo%LC$Z?v*+TXlLE*q#W=zw0n?nw?+DP zEQw!J512)nAaNr?7#eL9TuC5DNT69pFR81=;o2kA=z|GWH(f;vz@pOZ3j0+VztHSLLg^Vujw0wn%?+Z&9-JCA_ol&yC8$*N%2G z$W(_K{Xs$iS9wa*dl@UOqkU*Yzl%`LY>u;%kc}y}hFu-jL>0FN2s|sw4H`+eQ6}VkOv)_7c~r)jB;7L$N}L zGG@Mi`aag$vD)v)y+MJ|4+#mFj`{t=GY|W4ZA{mx%mI5=9?!fXRVTl?Xtkqj1CYLY z362~5@I#{D;iLpRTziG(*TrFdHRiJ(E^qjl~udMc;38rC2ob`Qw*;_!sbF#G;k}DJ|wK7k6LMbhKxF+5! z@}-LJGCMJb{*n2WU@xQB(M+formzC*wI+yc{@HzlEv~>TClWLUvbQG3Xr0_-JLkdv zEl3C>=bv86#BB^mK+SWNulyskc3q$g9N)D;I@jQWzRx=Oih50QuuA$1(m55jki1_m zB@mqNa2W}6QCP<3DE5i`+Qc^y3UW~Opyug;p9kOk(!BpmLN77LP+4*T=QS;=QuFqg zhQ}Gn*2rR=^5q8BCGYo^&bn)RX8Wp)YAD!qRE~Ioor0u`DcE)9 ze;?=%CxKGukJzQu`5g_mZtsvFrzpf%EmT&8Ssi6Xj!*rRLn<7Wi?O`qFwRZG9Yob|oHGZXN2O#+`=kL}bRa?(eDLzpGNQ~B}#Lg^IS z;q$z}8A(5#2>d0htGas{3^IRJEoGLN*hs&hcU}!ahP30MIyvhFGEjWsv zc(oxNhTgmbN2br*7bThr6mta1}sR=4Ul z7|gO2wX-+=r`PMfGTnR*O{BJ@+HG*{-M<=8P@qQX6Qd)8? zkPas0=;n|zx3AzSBb3o~M!dwh^xcRgyS_^v>vTxy)dhv6*+Tvi_Ju}oZ?Av%=2~oE zwNBvOwde}le`(m7n(8-7%GJ#kTqm$kFYLR%c6R2ZEx(LV2vWX~M~U}Id6w%$o6VWc zmCoFL?_FT6PSCo3FY2GCCismy=i28+tQ+4)ly~6x;&aXZ8Q=b@x;J4{Vw9c6vahGvAFK6voYS5HsN z?s)O^<=KM=U%Wgmcqlr0bM~b~GVV~wD^0mFNl@JbtLfa0-Qc@@t z*(NzMCZcP@cBIC(&yHlSH7cEBpQCRgx~9dEkV`B(Vy5mwNUvOPX6IUxKiYPvS6GUA8>*j>R9%{qx%p5$8i#Nt!CvBBv0`390XuyurwzxK;;vG^CgGT-~i z8dMEji$Pc2l0f*F2=^}hp4rUxfcwK6rausRq$}=$DuLiH?GI=jN!QHH-wgH;$vAi)Q`6cFQnMl zRTXxlYIR(f&wE^3rk;vcH~jHde~VvsL3VvIbC8JLjzpBu+>%4J z%4y4$G!ZR-U`$e$HKqUXzu;TGHyG_7Q#r3F;hjeL@$1DI@XS$uKjPT$Z|ER-EU<9b zu~omnOcTEs00QrvWJ7BRsvb`T(;wT2_tY+o3eYugmo9lxiKc9WslZNV^eGdcVl#x3X!f3+ezvL7Rsrv zyJ+6qx*Ypl=`7?*UoRX?dk>ZFub20gq>X-cp*Xrm2fch=Vrab$L~uDy_WewGz;A&=bV}x9%!Bq;q{JZqo;n>=bUxLQgAc;8l)XR0N#67+Lil{ zQQu;lckFJ-rhMrTg+m9S&V^UGSPC)^87#zyzEj0wPtMdnI7;5X< zEMz^*LFDFlOYz1j#qJ@5=5-1*IHz3FuPC%aXY1rM)?a9WrD-E>L$lIlKzkqdUe7OI zXOLiYIRb9Y;Fa0~1=T)*_0QG|!cS?;4cYBCN3#*Pq;0`OuWtuTEmI~RJ90|8fd_U? z{}BTSBW<{c@9_N<|2qT(gvQp#KYH4RA=$iWm)-3iY0-I12&NNqT;@7ltvATKFW*g5j!6XwGR`WE6OqdOSTjX+qqTDR(HEtJRt#OEM=b(oCXz z5`Iow`JAiKBn7rVlEgb79boz-6K10i`e2#V4u9{Gj9U&wgF+?`$AetJzxlY~)`Trj9+3 z4U68NPaBgqEk4>Bki|w`T4%nmPRi$s3*1!D{?TbYX<`PF>ovV?WgYDa9qcB(y!p+o z(rdjwRFCt~+NL0gau)sK=B?W$a;oC3KTEos5wq0N?3Ht6{O>n*pl(Ii>z^CY?oRwD2UujZ}QznelY zMeq7PZannMn_kf;l~OV5hgGfdlWbC=s{k?D96F}AUfkp>u5tsz>cW@PCf)+$E1-QT z>8&ve;vu5aQiZX~j)TWpl-^tkGyuYG?NFGMevl6)0X%_`ars={bl%U6<8gX&+|85G zGK_$RPT8;+hdUdaZS`x;8`4Sl@6K^6+GL{iF0$+VDBKG0aOUvOrTTGLghjD@YaFAh zLC6#pJCSSw|2U9gQ#fF7+mFVT($8J zP1FjKQ%#gufT%uv=3U%w5a#Q63`FM*`rp>VGN+QfQ@hx-TXBxI9K#e14_lvBxwuRG zE7tZumkG(ksOJ+5&G;+tx0A6f5*q!TShlNK=g#PfqPsI)PIx%TgjOdQtWKvl#UFEd z){&&;uiFfw+n&|X>N%AzZ3A-!jy2CxYXLr|NgpJBo?^lHY_fQmxQg-!$hv;31tKt& zVvk(87`|V-m_+Z0KbGEK>elT;2065dqB~OG zFxOu+Ug;sNcdw91vu8jop+AczHjK(*&6n_+@jpy$rO&t&Rf2QLx34H5v1zM}*)WhV zm)$&$sa^Ww)vUk#)u<`--c$nQna}uVMxctAy!qdL^FvpX-}{#91g$JM;VVE4916e4 zrQ^k=Q7dD?ICL6fzUtdzvowu02(kzB^%Wf%5GyFS5CmT*;=D^g%$-sgIMI$yN>g#H z4+`+1cWCw1KMde=*-i7qH8uHFvjsJ)$$+MzA$KM#O*7R+2@N%v>eq&_=Vs>yO2Tpr zs>AKTCvxuw~5gz~$TH3@EW26mC+j%JIwW5Qr9R(&%%rP7=0C(J5m}ezh<6objs@ zk`5|thJgcsAy#jf(CmrCetV|qj{hwVf0A|cz!a9lqq8uzN`hS3Ak=u) zuMWh;Ug8f}+y7Y33Vh82W5co-e6W1^Cg`c{rCDVv^(!}AIzY}Kkqky+@vXLfm^fJf z5@gYj#|FuC^X28qTCBUnAga8UI0VBbYd7Z~g48)O=LW1f^D`s!Rmo6)_C$=;vkwo2 zq*;<((rB)ai$zs^7X77R6szThjGzHP^(CkHGb@>(LA>$~LB<=OY|rJ=v%dVy)3ak|ZQe6U!9 zx)XZzzR;>(ABeH8slyAa)D{wFC-M8{FM{;hzN#-Qecs|<<2>tsKx$)tG_0CaIO_{T0hZWd2fLbXkJWef3!{X%ku>BH{o(SZyh<95E3q` zbMBDn&KKL{F1DP1^0eiTFE85@TJ>Z_@2N(GUao+D@lwK#aFU^<8WE%c16c{-;RiX% zh%b$pI+h_IAb^esQ6LvU4ON&d=wRs~1`=dW;_sD%^x>lB?DKC<&8-$*Z@kf`3$hcL zPE=w=REm;weGq{h2!e5Y*apbePX&zWPMLVU0Ne6A$t!p?hp7aw2NTm?770x4S#@}@Lu+!XUckjXay1yUJ*=HOcTOxLKt8JNQQwJ{ zQhN^&pz6~19rDRYZH|;|o8@yr_+lP8U80O5V z5(FZcyXUc1X$Bql(zIby8Myi&wkj0kRQHV02vQZ*MzF3Oi6_BI1`oio-vrVcxA@UU zB!@uQIumi&9f0;1UZ1-PGWv`8mYQBe@qIiNNL0Lnrwx=Sz)=gMA3_1%p~W9c3cQUT zQNFa5NxD+v`fA$eYMJ{hKR_RhzanWZ@v&Lwuxf8CigyDOp%gjICxSVyr!UPA?UYBl zzLMI_9zx@jwXNUs{HMRZ`L8~glO|n&6n%u>2$Qr>5~wx|0a=(uEL{=K%AyNsx{y(D zus_uJ(;ifKlQw&g)NXOYq8u-;fc4lkKBzd={+_CjV7BRNdG(7_jvZXY8p{1#<(;_HMSOhj10SR-57kn!%~8_$ zdudH!;#oK_H8U&Fd@uuiN+p735jG@i4Z*;8*ryBTlz~!?_e#R&3m}LUx7t63xIl&h zbs(gZK=0%Qfs^NdY5Co9AnSMn3h2Wrmp*g86XD@Z5K)R{l=OiNYG&<6mJ9AK2!kJV zW0648;Ec2kufwH4Nf&wf_d)zNmtb z#v&Dhc;nbdgMJ*$}jY;@^LtMe;xl^%ZHYAn$2&4(FcY}>n|hO|7>9j0dot~*A--?Cv=sd}M~pld?r z3>>TPnsVaD5Jo^A$OOeEf5+rfR%4aypVjsSZxDO{>OO@8kShz<33L$DTnHTg`QXft z(dHNPSYTFc3gm*G)jVGXV!y9}Fzu6IFg*kpDVsqE>2@i-dw=MP&42Dh^yFPpyT_cn zLRker{qTDjfrwVc+7VE(YhWN1^|c#s-={tuNA3mvvp)eu!*2#F%!uq$zx@a31~T!w z{$6wXS@bHv0L48%%PxW72hqF0ujhiV1BYj*r_cj@1UUQL&_{v%*9RbwR;Wn>#a%f_ z>&~^q9Q>Y6sBsvJ6deuFS|$`McoC-w{dAgO3XYI({}8;?6dd5O869VKp+uHMna*-N z(Vwfj^Ay=S$7wB&HH$QjlJH7&#-fybA&zD4HeWMLW5QXtLBwhyICmbz8U|TSE|5Jy zp8ePWICjkiSv3$4r2EjZ#*gQV&9X|{+!m{(s3$hx46yi!$uGM&m|v&I-@R{IEB8|N z#T;OXt_Ljed#v*~JAgZ$@Vf{o`lPWhd7K|Nq68;wtbrt-O!`_5HE_01u2eB&?fd z2a2``92ICRsh7}bm^!oqkLjTV<9uYW55&8DZP`m%Xq5j7K z{gvgV?KI*w5u^F6Y4rhm?vR{}I}uvcTh5CQwjl!o&d;JA3-w~46R%(6m`R75jfa@2gEK9TyV40#6i8K1l;gUu``F$$M zGk3G63XF9*TIp8%EcL#5{i|^=0VC~#t8}cZDl~ua-h8D*kzMd2oo$Y}ub> z-nNbx7`<8JKOb>!*y*u^?G>B@V6P*L(Lz&_nge% zF6m1ml*`9ZtGX;RbpdEc1y(Jg+y?p*z?IX#RGIrz4}kxCY;W=$D0go~BgokN`e@+C zy1#w6#*`@qe((4^V++osAFo(Y#VyhL@? zTq2oDw$UcAVIQq~kCO-{d_?nei~>MS{er|eD|e>n}OLn zD53+TWLOHS{k6d5_PF{A>Jgh}VUw?=n!=_zgBDgx12kE3u>h`K>3egf_00#WJTaw{ zSY12&wu;F`45DX9zx9i(xI&eB>jl%;n|viRU)N}f=>2IoXZ~Of>lY1S{cWO=tM@E( zF%*wZC*m7Xp`xN3#+U%!V}$j>6ldj&+#52*+vX1VrqFU@l4B;GOK~bjW*Ig$XsT-5 z6Zoap{kXlI@S-ToV&&Fbrm>lnB^#*E&y?7}i|FN?bFQXv~v&ND7R60b66;CXeeXPu}Q$62!8uYz6=i|G#z z-5hVct(q0prtyyGp+%P0>l$az=JK>`SS#GOMs?CLdIU&K>`N3cw~aI6uoNoE&K|_} z4LaFVIW|#9np=&x`*0?Kcft8RA8ibN`S8kQ&?8G)S%Ovw|(ZaB9XtFG^YU!kQbB44QflWqFJ<}=vHUEHjVI_90ORFhDg^o78 z&A`Dw|2TN;kyFOA`zH>Z5XgS8UCG zZJq+oCz937Id}?$^xHx)yY_14#{PyN{{Vr$&4aDJJC;_C2b*lDm6#qsBM>7)qRPfT zv>*Z+Y0|lADT1Sq8(66u?p(TAN(B3C)z>1+b#a zi)ew!K27l?y#HwgVdH{8Fz{Qsu6RRLC=G~mGgR#I+7XQHNp2>CNX~2brs7rds!+G2 z=QkC)e&CC(k)%LcYzyMC#a1FPTq`}?6C@Rgc8@c&AkwBGO9sOwn!$EzBfb7Ta-|=b zWhPUaq7lnldm1bfctc|M40-p%X%THh6(17D3(0pfRv%kVE$s<@>|TyOd(tqZhK4fi zbSj9-yeuwekJ@zYl7)1dCbdBDj*T(;+iBadaF68GisYiE{c&}oCJK>FewkEf87eCJ zSf=iVp&NWA{uRT4gK=GE>jg)>(I?;ZY$H{;97IuC+v&0erAk%!cQ1_`&kVa*8fpn# z)@cN!-lZxL`ep?*3Aeo7QeW*gP=j!>zZH6oax8eX1d6#e$q*~dXITq4we}OPgu1{T zjitY7VveHQ=&!d6ZM>)8Qu8fC%VP}f-BK$u5h(HJUW{Ih>rE#7a+}K<3;YLLSHI(< z;}TY3TD2`N&C7tKkWSk)%%Tx!kpxzy{pO&DpEhX7EvK?>ML;AlG^#gjmVA1ZlkK5M z8&z=MHsmCL6aJLD-3NK8c@|Et%Ei0TN{mEoK}w~wRXko4P6m9)ehM>)z|fi1--3}F z^d^{NpBns{t2If2g|Ak7E>QTH)tcXUip(x#-AEp~X}{o=#D!ZG9lAa2v*u%(_v2No z2;!zIq^uM{{gY9l$J0qw)D%axf(XII@Er21^r$OeEw$Qkam?OvyyahQ1^CV6SRY8-p9s!&`xNRG#A7F~^kp=Jo2FOlIr& zE)`DS6q8g38FB?)9+J&EV9DP*xgPY^6NWT@1MAqf22^0wAj6lM$)(Y!09qFZ zLTf0O(kRM9Z@iQ9xiEFlovxZ*PnKfAFjc^SJU=`zA25a{=0KXmGKV{+V7ks9X-2!q zh|EZ2_$m`yrKwxCjAd2TE0=gJ^aGzxd7&P)YyZu_!rJNY zm9>KP5F_S#3K;cSbRM49qh^Ow`?pcgalIW4+I4W6q+MBmHU@r}+*&ctc|LhXAk28s ziTbVh2wgYpJh|+!CX6W3>sab@Ye%{khDQrOQP-vu>oXpQRnoM}`E-bI%$QhfuU4?d zafxdnuMCGOjZy5B(-yErvq4WiW>eiT%0{|PmmI`XMYl#l60;HvmYXXV@;a$>o!q55@qS8%KM1I4)qZ@ z*M9TqCIN0^QyD?KeAq(Q=R>M=JYz@9UYA4T&q(=f=)KR&3M}VHAiqsWBtOBSEC!L8 z4-D%SD-q*96(BO@9PSQ#Uo{!On{*9bAtz)U&UWN7S~dbTsbPH)7beU2kkNs%h#9g) zit``izTHk!rfZzx?M|GnwOtBc>xL~26i6bf!BmRNp^ zF^Dgs9@>(1OkpgfhKjD}x;)(aBC#G0f5zj`5$!syWa7&$%JNl*j8y5FgaAV57L(mI zmUW^mb^w&u;SC9IBP$K7f3qO2kajt?Pv3Y9+jo)gfA>BoFD&2kI=$nWOZc(FV;p5o z@T;%=Oy9i$3Dyo5MlrDYHzulBHT{bogR9Q17ycNZIh_Y-+6VezdeJs`^DzaWuEXkp z7lD51VBA9%HXG7EG&{4@%GuZQ4!=FL(Rd;?2BAHPZX$gygvrTH zRYm~3z#H}+#$1}ycD+e!B_3v&WjR%wN0njMTeH)sLysb-;Lmwd4o9a#Bzl(f2eB6l zCD%>bzt33lS*s6{i~dr+OY?{Fom)GrbEkGwHWr5ns258Bhev7%n_( zUIZWY)~fV3Vmy~tS;oQ{(TcJDWY_xLAQV)_P8ABNfyZoJI%+r6*#)4!RMEQV^z&~3 z2JGORZ;jKqDof@`hgqubi2v4aIa}7{nhe8Ll1F=BE4}}bj129bZlbsEGnku2AyV0X z^bha5vtDKs65Ge>F;`o5{TqRt|cNz!QfP%7j~*S7bBG zaZv$U$xGNBFnc?bD?!d8&GEFSahNb`3pOcVhR^!=Ads+2{MM@TUYAgyW4Qj7ofi2( zh;+}2Ga7TwU~#|vZYKqJkgg9B*PBRq!xoH+_tU-Q<(-jBv?rsR44dN`HkMpIUd2alrfVdl`o!&yPI)0bzdM z)DWZ1g#r`x6*&l>GrqmQvZHG6Qtpx^$%SricI3J>&X=uAI*(trir5qr-)P4e`qT_7e~ZKSp34&!8yV1!-JVS$+W~wlkjqRAZi)cygmf0^jbNiW%42 zO)B8Mi6R3_CajPi4+lg;tVNtvNmU!Dm(L(=9UYJ+m|o~GOGdU0(pnkSWbG#|&~h7Up0HHoYy zF@h#i7x2wdRSZ-usvT7e6Vzs{_m7(gRzSpJD{yu9hCsXZ?TCehh#6~V^>u` z5&N@wV}$*zDZr8R*F$Z)mK><3&5zx*H7W%zo@OV`bRX5PjmqVk%Zfs`l*dw4I;CdO zS+1pN`+V-GB*Ci~FECF(edcTWpaant$!O6?2O3-t)Z&(xQ7B;1l<=g#v~Z-5)n@?E zOI#hjc*9}B4sKg^G*%i0=PnP7rG-f6w>Cf5)oS`Y9F~+v$7b*8R#2^*IvPiOGq3ZK z`)zo1>=aPrExPI)Fg3MG(tg2KW&IT;WWP#slnL082;RFy*0V4(^kI^yujr+AGC^tJ z@v(oc`%f%j<8Xq&?B3i(onAj#<4Oe{re~rw2$xp_&b6yjkjSq^|=Q8ruy1Dm?M zLZZuqr1A<{-6R?rPE+!7@Qrv(F(gl&R`{`M|M}zj{dqK&1kUA7#N1vj({Q(N(=BKn z6&?o9u6Y%v@iM6B5GwI$I-*nvEJO1cb+@^t$ll21^(_G97%#z|IO};u0+>aXRkkF1 z_&iCSH3O%E`{DIcPgh8}Tj@tIiT!b;ou8DPb}nK^+)Qf&!;Ks{@c;vE_M$~brDz5^ zuE#PfmCQLd4d?gaJYE1+V^Jje$1c)u1G>Gtst(8<8}oHiWGdy=%wgWR$ng6};;C58 z1#HUcI>Lp$saaz{&gn~Nv2~pVJ~lZzqVhau*bU*uR)aNj)#Mvfvpw0 z2y@7$QbXNu;Cy=YOy1_%`T2XHhEa>qZrg8)`h7_Z!)MF%zbc|efKxCt@>DRY7-Zo; zRqxY$Z@51ft5|j$=5acNi%FvwdYwdHOe=kPW{4?UuJ(k{t?K{ghEJU0*z*U~t;Kn} z7O^|hCCAyyE6!sgCG(I_D2|LuKXnXmNK@e3Ibg`AOZX$Foulr409LE^eH*s!-zTE# z{Z}6_h_-(kV$qRS$kFW2lb{ix@zb;1PYl!bMiBe}q~0JnJN2{n;-I1FTNe#jI0rp^ zEfYI#PUMKqk|HOzIhu{%$Sv>o7#w!A#@=ySAsyR9YsAhEKH1;tttL6KyCChwi=a#u zfjy6=JV(prie72Y7o{&Y!uhPRkH;_3&~LQ;av9y~Xt$?ELzR)Dr30k0E$oa<8o2tH z1?Cv8k)SI15N_8A|Nvjj@BuS*kL_chkgOEaSMe+Z6_yn3mM^>eT}4 z9h`nUE)BbJmJBP)xmAFBdw+4TRv5~o9WIbwdmU_PZ6749Ak2J2GgOO2i&i#N$B5$-sj zQx-9 z)UG+JKUE#nWwU?qCZk5t-2&fw6l;5H1~oNRoVbp_~Ehx{trZKbDt! znGA0~$hTX%n{`ip4BZDO2AR1Wujte%A9vIry1b3f*-%Rv9LW(eD@Se&hHNAsoj@a@ zvyq?gj=b|hS(iC^WPJs`tXwpoc>wGZxiemU1n7a!Rz1^pxWu zA(5485A+!}s`Mmx=Th{ZN^Ye4i-;_jgnGyT1?<`pYsKlJg@UW8h<}aCc^>U;qESEA zUL>IT4~f3oBoQU=95c^zp

    5@L+uR!d-ZqT4*iKo=~Y_Pb{$qeXH=)__m0X$)gcd z-r3h@wKGTczASQ2bX20w(iBI@W-X1?U_+q0ActNf!w^i$|Ux-wjT}S z66RoNW^zM4-#SVu>NI6n+zP3$dKx|JE83&v9JyTbCO5`jsc-ohKs4>yQwYoEE+rC{ zmGkrWkFPwS%V>P-Yyeh=_GHW1hF#Bk04ZqCUAfja{;$ItF98@g?|;`ArrT+L2@zfQn*guuL_gw`xr zxW5gs_X+$IN)m4cMwSEXWPPPO4!7J#pZ^rVJ4qHfyG^@#6isSb#+vQqTIR)7`eO*1 zhbFiNyY79~Si~waUjvT$qnEe$a`#?zVfhHaG_qC>P4SaGR-^Sqp;sJT=BQY)In^_0 z@vk3&Q}hcf_h^xA3NLiE=6V{+&MT?G?brKI7wJmN5pCK7!veN+i26|Kr+jD@?JLW& zRu_dS3jTZsbuy(8zfb1JDw>Ybau9SJ_~R>`Ul2Y30hR|eZCD60##&P z(89C!AY=2s-U!-d=7Ads9rXSi;#;vNW1M6|zB~7Zby{q*2ajqxn6^ypdp|$CMQMIm zV|m~(`=Pj(k5a~AQ?#uL%4MOROe-%v5BD0?2y{bTeL zX&-S~PksH?>aR!7`JZmWle8^iF!6EA-)90Wy8~oY?f!1M1H+%&e{|gztK*Qn|jV_`!vs@u2^g8h4Qud0x zoU=8OL;8yBw)N{%u;p36Rf7Cf>Z{I2UBUi88w>;r?m|&L#E0@X1xm7Oq#?JVLykS{ zFZzfC4;a#0G;#g<|N9Bno)B%jo%YJVE!2-l9sNr%S#az8fZ2v4{X{|e|B_4p%T5lr>YKGn)`|w{MzIjb;Tf^TSYBh(0&jcb)JbM0j*oMpE z4O0TPf=4SWgif!YR{yK%&p%5?H+7^PZnM450FJf=c@0C_s}rVcjzrYAJ{^$~nAs}U zfmGM%^gDkurOwW9=*|&Vyg}sc{^k>qa_uL&^0@BjPJ7m>g#A)^RZAryFafNbGVAt) z>s{uxdsRwtG2>_n%Fa`Szj_b&iSX)3bzDnyUt|*9MVZ0t2fxmbrb$K8O~a9NP7r?K}7v5+S?eca8m(vDgjB~u6H+QYy%MI8mZgvI$5mz zr%LKi1)kTBqSMZhz8zF!&UR|?Xf<{Ed%Ci?wDwZ~!u@-bk=OsF>siAEtcqWNgHb7g ziV`3QF5FjU=$?&hO9muw#_r7B*l#+FGx@64p5-C|`OLE&tE_1~2-YxNFcmO!Ep^_~ zAR^$mxllDQGGaL%Ds+u;R@WyFtTHQb%|*b7B*VAE&354c5hXpc4YAom+N5v!PdXMX z7c13pSs;8t9{>F3V@6799Qp)s_fH=?7jTW@>;?> zDa>{&!m^{1*6Czg9mRp?&)+=C`BR#u1eRO-v~GP)8prX=7bT8iG@u#1vD==)@bb$~bLDWD=$u84w{vgdsT3bM5` z70#4vca-X?AFID5rd>13r4@w2Mtqv_D6f>8IZ-lVe3pbjs&Ei3K%m)#$@#mR*+x;3 zh9O(%kf;X#W)F+SulFVs4Fm#?6tGBhEqudwWszk3G_6}8Q)u=1(4h0S{j}#`jZY+x zl+sac#wbxa?(dzZOLmnCIVMoBeR5lj@^{KN~hi`1qZ46#cX6f3X;zgz7b&CA%QVOx6a7wdL{sHE)XYNJQKkX;ab0K6U8_ zY=5iB>D@fWZcZ_$S{a&0HLXmzk+cBOO)`DAaB!ug@-3?7{t{VON1N;yw)oPf58Tt3- z#YKyXRB|!bre0iegafay#({3(=`34_^&ZWjBJ9FKJBsk)Hb^cb015?QD5qaa_%*Uf|tI__jOOn#)cZHJ^~ zvRoncC|0p6jYz#)rV&VRI#o|lAjj@Nnk&9iiUe27Ag424sFwi*e0(9QUvKwF3qOSaCe?_#6 zUy2&)ewKLQ?sUnMk7VHOWuzkot9=906*`5tn_t1ka#=FY;$m*Z(ArdBwl(3~cb)Xj zwId7}5inWjh-F-0c)8i*d#BC)>`YUbROj%lX_CD|oXBfk_Ge-sxPB0zcLm2OCxSs# zt%D8pd*~8;R*WqrvdNZKQp-_S8&1zwjIGW7TX&{fvOk{ZR84N`6YwI!z1C)1ZHIHH zJz*Fv;>E9-AoIXEy!WLYp7q3%z82hTVG+zH0bmph{@&ow6GYT>c@K{I?eb9P_K%ffYjg8LXjcz?N zgtPvcsrWBx@}%ZEkm_BZiWF*CS6xQLl?mM)Dq}*OZ46K2hb9#1dE{`MJnM>YaL_r; zf{=!YXX6Klp?xWqH-Rxj_DOBE5cT5q3KfMjJP}~cH?8zVzu757TbLua0X*mf_$)D1 zQWo&?JQShs2z*?z@3eM5bhGy?IG$0M-HMKL)ne&48jv32xRaITr0cRa`1~?6W?*k= zm1EZNS&Qz5jJj+55+L+=T+OZw*zA5zUBSK3qM*5a{=(H;&r}9Sk3siythUw(P`TrJpd(qBRT|-86@rt@3~H&1?t^s}6a(~9P)4da zR+RTCSpG)^bsxn#qB%1FvlFx@`VnzY6;3FtoB}K0VhN*jn`5C9rP+NZ zjN#atctKykJsW64`26}j(0W==;YxB$v6ijkttB-iDsf9bbvbwTI%MB%Cl1VmTg&Iy z57PcJRZI}e=EZB=K2qc%sH&N=7an|r6+nt^*vff(*st8CFzJ{pv(@LhMtNz4DyBEJ zIZD2PT#U#B9}H5TB2sfSLc5VfOFm@A2Ax;g_-@4{nFx#}k2fn%*?36hofB(SWp@Yc zmvB$CZs*~H0=w{sz+vh_b`a-ux%jb}{4(C^9eyC;l5xewYS_zdn2^FZatVllW z{7<6CyN+3No@^io#6(&GZJUm(Pe|rRY=MP+ukgsLnkK1o3t$vv1q@d)HFne{c@azY z!-S|#nbVdS>J~mrk6b8s3D?mLxH~eB>T}j&Jv~%1y>)W&V#(Z+q1cwQ+0>T%4D*W5 zEM{;5>>(8eu@{*pFo>1))zqYyUcaaQCE-Svo=SGDG7MeWvQV+9a3Ay|eQ~9)z&cm> zy8j&6SE^-z>f$dv*sz*^^x!c@Z>_yOYB{TVy6}MB2NTr1($lbylI2h_BWC!r@tE3? z7SL8i4<6!oT4%!0t_8hOKb10{$tuZE2^I6?-i~>06aNN~d;c;2QL5wkyG(4X3uZ&pRrJ$ zFe}y)ilz22c7&lRgmxIsIXuEx!!OI3tPKb+oGv!mAKzRQ-CWyM)B<2Ys?TvVl&9;<#l~RnR+vi|X?##2 z-+I`{qgI;}Xa8M2pMVle+st$4NF8%VXXlriF~=nQM=@Igp19yf?^b0)$UALgcHKni z#BbhCzvWPTC}xyFe0I^iSvej|?I%9HPmNc`%{g6{XiP!HbasC$$d~D%LAf3gwS`3b zXo%QyN#|=;e}}^U<^_)l|8YOowxAw7H(uDl{r|Q@M;B~fC2On4zm<|{!$-8(sH?cZ zPHGXr@$uGTxqoBhWuDp4#EVn6j^llAr+Oq#q%M*BV>sv+6&d45r_SniQvj&uczbTR z*35lM`h2)QArN^-fk&*+$~ueW69;+{P~i*=-AxERf}ICr%E-6@8D8T%Bi>Pcgh7Zt~Q3eo6#O znD^~^S#70kbb~yMnuq1ydB~jF?Q*9u{k$n8v3Wx&($WZ&R`>VTn!hbhv$YeKW@!~<=G-jop@Z*FaLzN%3=`^lYOHwA^o$TZ3WU2r9bmw;j&!WiVKqAIQ=vFhMlWK;`H#dMiGq6@2)X#9SpD-KJb$Oz*^o<`Z{ z9O}Cjm@7V2gIlM_Z4?IDsARX6yc;*1x^_}aiyMJux&EZWb>R(G;-8hMnRxAOab>+C zr06Vgk*N`VW5cBFVGq`B26$)ppV)>FaT6@n{(TNLX+(S&DfaxA`1g!U7;6--sqjcg z|3^z}dc0Dmu-WPCU32~x*5ZfT$AheEBF;a_W`h{1Nla4tt2{b&xN z8j4r~o49y=Zgpgu#?SKmxGmT`q3u3)`A@6a_^Pv#h?R&Ty)}}1on@QPt(2Q@ME3)t zGump;Ov8hMmngSUM$|tp=nM=blGu3cql4PPhiyU6hMLfj@x9m<@nrgC<#-Hx2SPh* zjMAbAIWFPh;jMOT`!TcqpUx0TPSvID7%arTa5|Wkvdo3Q{biEi%fx<06T2TN-Zx41 zTKYJZr`MQBL}P(H-EQiDZQ(B6g(C=GO8(uytyVvY;shcV_ocY{-XS5a+^<4!`sotN z)=OLAMOpJ%Zg}O(fo`vMQHAt52^~~zk+{G3rFW_iRzdDTRNRt7=0d~#oPSD?Gpn)ABUYqi}E|k2C10a3Co*yQOo;2 zH_ZPw<)T>M39^j6p)-Gi3BYzjHn1m#zaPfWQhub zl68}_k|gJxZl73Io%7Cnx3%xy7rq~l)2t&~*n6$H#vG%M-usw$WzU~pL${fZLZPgY zJaa;hLRsNTp)3hYzJJrBEKPpe(~r95~2#fadwncO}0;`~2rSdE)QnYYELAS z2@BzGfBhEkfdk}UyDGDxS{l_OU0FkK67!K39ovS!Opr)%vVBp)=2R=VN}S>=@%1 zWBvY_09OAsCRPK*_Cj0Nx{;6j>ucfKm);G(zM)WDUG~!D{PXR)ld=@@yncSU?z_2U z@lt<&dtyDKE4j^|UpwwSr>y?<4TW-H8STY?zPqz)A6cirzK$>+rTP8WHx$atmH*2h zIcR~~nPFX58z^LXtiXNSQ`X8D&3cyQgUd~8;*NSKWY}ANbl-*-etp{dA4y7-h54C* zv!45&c(6%@Dn!V21==-zNgv3HD)!~$bR6$3^x}Lr++GwmJ2k@TGC#|q6zzkT!Zr&S z{lHIA`y12yQdTQfMXP!p&^g9-&PTSdE{SjC(RK-?AW;WT-b<(SihV9zDsWHI=ydNa z3s&zJFs|ZqyvJ+aB>MCj{<*|ouq~ z)g>86oew;G`1)j=-h9D!7loH6?zVMxMKH3HO3oNa%>Z}I?-7gNcG}W zR!Fnb!-6{uUmoEt3ls^v@aj~pe3MVXqwP@!WkJ`bhC{>{Bk^ug`Xzpmn(4{;513!I z7dNoPrx8}Ic^D;Vi4>(fcy5j}8RW#|*fV973txzVReF&c*SzqXp| z%F#O7i^_QqnB^^7^OIzxXl~!~6EMz_yK&w_e5RL=Dq+*@Io?})>2A8w^k`S8c9aq~ zn@msx1O3(R3UsI75nCMGsj?~eTqY2y(GZYR@;zU-|}kr~Jsad)1d zGI0FFEnrk}Ktkk3S!=nd<9M<7?S(i|hhZP4Zq0XB6P^T9@2tPY)UX~lUyGMgeUc$HKl(tizhJCVw91PFgEAvL=jo9$iT2LU zF!H1#F^z7FTagj zJ3B>oomXiN`xLF6?L0g>T5)&7-iibR8OOg9?1{$~+!Qx9+uoXM6ODG2iH7oXa}&+v z>D?tMs5(|Vr7b*gM zxXAwqIArQKJ2z*leCyHYr+Yb#tD@E7F5W;A6SnTS()i9sF6V67$NQV2j4C6?`IAg) zzrOu$Ge0{;HaFX88f}B;NTpXse|$Nr^cYXIbF=vMROa*qIYE9tgVKsnX@6Ep&2-x& zx8q4pGq(6!iTJjczI+CCVe_J~)XD9jOxkvM$l^l^gx`y4?Mi&gYCPBSX&l9#s8a%ApYc{M5tEzC)-4 z+?JAuEm~Z=L^d_`y}R1=QpocA8Jgy&2)BCqhVj-%5@%`Z%MedZENiJBZm;T--+9eT zS7cC|`tsx|s!JeW&EFwAdAEFt#f>Gjp8GEB;uA*_XY1$S;o-rx)jnvfT|SMlsY|mC zV~jgimG)exQ)A!!U@kqI`g(rRRXg0)Zj-~7;r`f5id3-6e0y3<`>LlW|On_L#0sLjadY#sZzZ{I%RL=vIc)?J-%K58~DzlMm` zFX~;sW^+yM`i|NJ1O1S9BN`X2PgXS{Wqg~V_y8*2faqV=+iTHD z^?*k!BQB3sRcYM2l4Rrh;`h?oI(on5x*0X3#(V6Mj^t8(8;+_Osr1J0-X@-yt-@Bf zrt|M3Tlkk50G|vG4Uw|1@bP}Uir!qRSI2u*XF{gO>&?CPNN)xnh&*wZf$SE# zP5Yx&;&s`P?MYtid$z3S)05rgvobO=NQ5%&lRYxlT`B)`kF)|{h~8(9lMcge@uJno zMz9{7$NyYi_>3nwQX%3cYCdYD3XsMvsi+d5B$nO@_Ht!yCp}%Hz9+0>UDVSN@?lB{ zeT5V=O~oiBX_D^0zLtFTWcTSh^Yzhb)}4`ZA!qeh{LxY3PqNLm&Op1-kDIj5&@BA9 z+_~3%n@F9x&icub$--E83a4&-xanNS5G~5o9=9oXd0vJtT8~x=T&8jIUb|!&$PR%-l!WjXJv}Dd2HlX zyV8<#V;Zk|9=0)Z6k6H&()7i#+g!Yo`bohiQTO}yNc+7`w;MR?!La9CIG$>qyhaBi z3ju$Ird}dl?$cUE5nB_2NFN~anA}~_InnGQIETP4z$QgTl$Ovh_VHFxu$&y!2|4R^ z7KL-oc(k#$wzhz2ox-crkCNSPBE{T_RE*jqAt6x&h}!7|NXeIVZ7dS$HbmTMdfDeS zS7#?JohHAzO?OAd_U^o|nxM}v6C~; zOzT4tFg-;)*@Rh5~!6}@ZyJGwG=R1%88ICK_FU`$8ju__U=IWw`(H}}d zBGMO^^5*hZd0rYQ(lb9dGdw)&Xk$>HfMy}kVTTq)3Jq9Jt;pRH1YzI0KUB(N5W>_LJ? zsbMn8xCk~$iP^zFsEV|wxUf)6b-|c ziXgCMX1tF&ki8H?vvm0x4;1F@Hhp!KZMPLEwoS{Y@w?xwngoAsF8ej^)p1w8>~dqJ zYJ3|Q82DYt;(0ETtX1FOx7@ynQ^+pov``27bXrkV_wby0@Vh%Qa*aF}%7D)B%*22> z{T311-UAMj9;}w4*G4}D=}v9Mk{HpuZEQnqDfwi|{j|sLSu6!B*RE5@V6}qTfiYnN82XFs*Y-}tkObD%$$#=A9@ooL~^G;F?4r6hFBi+r{#=6V`Tl&$ zqO<&i(kZJHf^tyTX$+sb}LpsO_8z&AYQcSA6WO#?_x0 zezd|8Dm0uI)30{v=D!Hsz4Z!k-Zj+ilEtk5Y=0nQ+`S5iknTI{cYE?^i}Tr5gQb}FZfF9$nB9^UuQsGwR%SZd@rhe^JYd^juUf&amKYZx*58Z1@B*+y zA2k;3KX0UJLa37(c3FanUN&H?nEham2cPq-ePgg(HaJN(y5}379H#? z9Lb&P{5sXi^lOJ@IDG@EnH+opsEv&%fDT6z-D@QP{?*sqH*%ERkuSTE#l{)gpUaAZec($rU)cI7&Gq`w_mfgz*x&YidF9sb zc;M;}SpmepN2>{T&e_L1j^0sk<7i2feRXQhSYLfLi&YUG2Uq74vP~H56tStLo2MRV z8(4P~^Um>J%2&~S@muV%==7$Jg1O#=GSA(o*Ult9;WIc-rSHmUI)dw68*dTm8eE?v zC=2XxY`gR13$c$M9C|vL`SgFFQPAJYt;P>@b&jOCM=W9rsTMjLR-JFDZSojJNtf!y zj#Eps3SqYEsVZSe0f-ND>h{W<=}+HjX8Gn~av4@QS+BRWvoS(`ul$2&_h~P71{WdW zsp$S%%$7dV5pwG0-Fbl`eFfuTKq+v)@M{^LJLkt>{`>pn&L21BIvL-P`~KAw@Hce3P6Pr5qcBmQtmZ zWo;>zIq_Xp_NlR7{HP5$+sppv$avYudrVX? zDaTDpwW$CxSJ9L6xtj!uJ4L>;>FIXc?tH)+b-WTgk;|c!+_mbLnOu5_E1--xG*aVq z3*5PBO=#=U;J(qyR73E2vZ6MZykSZz4Y)5E6cps`lTNph>&o=F>GN34bbTdB=fU?Y zyj4^**dNoB9cLb0*E{cdb)wFqe-IVAR zyRuhnyQVg5wX@0{K0IL+65h!L#4;YSPT2hY)_^#=oKj9#^Tu~fx}@U@p{0wU%%Z6+ ztglzc-c>;RRv99-N!sVYQLIH3uu@;w(NuMccVBN;^%E}DgEI>2N>6Hc4%`^YPSAP7 zx|XqO;#29A!3e_hX!6X8U%S)1YzsFacxQ2zJ18YzCKF)g3Iq+e zWoN>Tm{nARjH$z-_G-n z+1yLEjkV^ni(Mo7w3fh~(E>L*Uwrt*?QxY3DKJS>-NQd+cb z2CGJ=9ia6dwaX?AJZQ)k7}kNS4^Q%f7z)rxK_W|n>^ToWP7ybq0V z=mOy8kN1D9c;~W^)!&>QOLzgK%y-tE?xe=ejGF?xN(q`b5wgpsHGhq?@4=563n=mk zG$}x%L)L&*)dZq~Jox<0;tD8#zXLiq(WFIz=JpXy_hgs%o5y`~$3IT>+vFX`!gt%_ zDZBh0YPQ9Nmut_~^*5_gN+0k1!@*q~d7B{Ouw^>hC%0|v43_Q`H2t*zt|XPVji$pdAEbbMfwNR{ZdE%OCU#;IT^rMXHO<>yt}HqZFe;QC<8j zEszQgf)9ctiA2XMwb*vQxl)X`#bQDYohQ`tjge`qJAqh&f|q=8lGH5SSc5yXR{cno%(lp-k|ZeSLkG zjb-CZCPBY)m8IzSZHpeE zNHsTh7ZO$IfQG&8vym+aKB%j3y5`;!34VVas8hDA-Rm^~J^Fx4bzqc8+mi`#Y>(~q zs3FyEUYxcQjEnd8c+CJIOD^*r7S7Hq41Scq7>`iisAefBdqp_YapKS%Xqs+NY~HB} zL}v;6FbYz`U;JN|Oz9S*D_aDZ+(?8A^FU4facD0oZ5c5ruAFEw&@+@H^@;?qHxWcy zmp_M=@UE(4FUO2Pf_{mWal4T{5yF78F6V=AlDoO=H!k6r?z*HX)xb2D1?S3WRe70U z@uVe*!e)hc&mQ* zxH`}zhU|PE?O>u?g9q9OYWCGG59A|mOJ(8af~n43qPG-V)<{8f`pGMR&bxCPz&Ire z8lOJN2t`}gSalQ|PWua*A96|;vFi^9i#0ql!e@P>K18Gk*V)CR`Az|VG%;@};ZxL- zO?;iEz0!t#{#2q69#>jt4qP6Gc2ds_1@3*W+q58ZVB~0zjSf7zF@kzoC*nN~4hJCvSJzh8|5|?(`#;h$A>#9zH)=daH&UxZpH<~Tplik1^k>Kqj<-^>oVl<OVy#{hO2Jqukc7TQa(%Tw# zLb2jX%S@MM>J@FAsW65T9Es1poV>0n!y#dMVjvb3ys)T93J-|?B&#G*(41$7PKSx4 z4k)h50kRoq&z*N#V>lM8p+d8jM@!UctWw!KO|Qs10+C79D{Fq*SToJ)Bv4!hDl)f2 z|GjM@$)E+z^9kw&uZU}jf>Nnsb-eZZ{20<>BC>H`Q>Hqqc2ZkfVcL(LLZiw!dvu?u z+61rqs%O~8qsE2fw+R(Mo-n&;7-&o&g|%->?9sCFTeKYE17(o}JjyB~BZKZwCe5l{ zE?C^@>i4|gfLtVj6yl@ZvHrj1`LkTs@Cwx{Ux*fDMk@$5#J_C2w%<&QN3?>eR zGsxevKnNubV1oHkmPjiJ8uIay(v_tU={}Qm(zT)!nmSRRStGz~6AHPP{pUlWP+GX} zm81R>DjMt(U#pno_!a05-T~E5*rYakvCHQ_*uZ+`qVUqHQimBTXuWXU9uiEzs*=<0 zTfcx3m;3H4L2GPIm8s3 zB)F|F=sbJb%@Q`b9(O!{OOfLJ8uZ&0sIjCM5PJ=58IF$Vux+nTNZfd&Gg#e=63xh; z^?tdOqcnw=&B(Jj6P z4^@*Zms40xG`cHaUXDLzl>L31h;1|y>}v+Lv*C!7JsZDHe|xVEwfffA<@uY4h#Io~{w zmAHR}L()r43j?2boQX4>Esl05Q-R`6YV-5+g?<82K-;?zQjXoms*ocQeLEz(zlPjD z^@zn(hF8Ps{GWDO&yk$3EX>bMqeli*+=C5N{C3+IVDq_VEmxb)OMzmJN&uh=_*hRO zi8wg;`b14Q;IgheIoY6Agq#ml1Gwjv(2i-^{WyK9pCCK}chGkcoLBf{x0{}niSjJc z9I;{$uLFd?C?Ne<&DUZOxzK5Z6XG2qL==a+x5s>LF+JLcB6LVym(&SJ+;L&QpS-`Z zQrl&YO_IbcS{DPEe`vZd#+zJkq=eu->(cFFMa9Hy&?Y$keq?1`vT4*oy~2HntDP~P zw%(v(5}4)@VO;6_LiH%|;*%{)CFRf>jY6iWm;d!Ja@03|Q$Mmarn@06fwc>n$WPqq zo!IIf%kV}Qz+-q}O%9ap86O(r{n(6!`%`7s>++OvFRC)z@?USVYdP)3Sz~?(6^IpY zkgWWlS(dN6nZuXJW(dt|cw!NIuOE`jwC=yC}Hmo?g<{-QN;reo!_K`L{(9@SI19sN`fWv zV0FS>GrIhG)^{$e&^&Mn0T}K90S^bsq530BV_QJ(k+6Vuq^-T34KguGZ*kDZ==Hl# z?L!NVOc~VV(7EkjOaMFn-D4F;%m3?&DU|;PMy~%4ykr0WNB%!#%KPuVu-}M;GcNfn z`q!n|+uPS*`>tm__)JBW=KpmI-1q(I17mdQ- zYsJ3k=)A)}`Nu}e4x!m8I?6nK>@%7j!*_~fFVrsC{A-2(w+x>D-B#znU}If$+BzL% zg-mh*$||=t^g3_2Q}jyrgB}=<*zuuoXmC Zny0BMO^5#J*nq?bT^z2uOI1S2s@< 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 literal 0 HcmV?d00001 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 4f317a325..08a51bca7 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -8,8 +8,6 @@ * * NV3 bringup and device emulation. * - * Notes: - * xfree86 ref has INVERTED bit numbering? What? * * Authors: Connor Hyde, I need a better email address ;^) * @@ -747,13 +745,13 @@ uint8_t nv3_prom_read(uint32_t address) // Does this mirror on real hardware? if (rom_address >= real_rom_size) { - nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); + nv_log("NV3: PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); return 0xFF; } else { uint8_t val = nv3->nvbase.vbios.rom[rom_address]; - nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); + nv_log("NV3: PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); return val; } } @@ -761,7 +759,7 @@ uint8_t nv3_prom_read(uint32_t address) void nv3_prom_write(uint32_t address, uint32_t value) { uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); + nv_log("NV3: What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); } // Initialise the MMIO mappings diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index ca4c3bf38..d32a7dfd7 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -225,6 +225,14 @@ const device_config_t nv3_config[] = }, }, }, +#ifndef RELEASE_BUILD + { + .name = "nv_debug_fulllog", + .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", + .type = CONFIG_BINARY, + .default_int = 0, + }, +#endif { .type = CONFIG_END } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index db657b63e..aaf27ab25 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -35,6 +35,7 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_INTR, "PFIFO - Interrupt Status", NULL, NULL}, { NV3_PFIFO_INTR_EN, "PFIFO - Interrupt Enable", NULL, NULL,}, + { NV3_PFIFO_DEBUG_0, "PFIFO - Debug 0", NULL, NULL, }, { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index 96e011430..af17bd3a7 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -18,12 +18,14 @@ // Common NV1/3/4... init #define HAVE_STDARG_H // wtf is this crap #include +#include #include #include - -#include <86box/log.h> -#include <86box/86box.h> -#include <86box/nv/vid_nv.h> +#include <86Box/86box.h> +#ifndef RELEASE_BUILD +#include <86Box/device.h> +#endif +#include <86Box/log.h> // Common logging @@ -32,9 +34,19 @@ int nv_do_log = ENABLE_NV_LOG; // A bit of kludge so that in the future we can abstract this function acorss multiple generations of Nvidia GPUs void* nv_log_device; +bool nv_log_full = false; void nv_log_set_device(void* device) { + // in case the cyclical logger doesn't show you the full context of what went on, you can enable this debug feature + #ifndef RELEASE_BUILD + if (device + && device_get_config_int("nv_debug_fulllog")) + { + nv_log_full = true; + } + #endif + nv_log_device = device; } @@ -48,7 +60,14 @@ void nv_log(const char *fmt, ...) if (nv_do_log) { va_start(ap, fmt); - log_out_cyclic(nv_log_device, fmt, ap); + // If our debug config option is configured, full log. Otherwise log with cyclical detection. + #ifndef RELEASE_BUILD + if (nv_log_full) + log_out(nv_log_device, fmt, ap); + else + #endif + log_out_cyclic(nv_log_device, fmt, ap); + va_end(ap); } } From 84cb84ed2fa4e6c8f1017f9850981118e86e9150 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 27 Jan 2025 01:35:21 +0000 Subject: [PATCH 071/274] add numerous reigsters for pfifo --- .../How to optimise riva 128 applications.txt | 4 +- src/include/86box/nv/vid_nv3.h | 62 ++++--- src/video/nv/nv3/subsystems/nv3_pfifo.c | 171 +++++++++++++++++- 3 files changed, 204 insertions(+), 33 deletions(-) diff --git a/doc/nvidia_notes/How to optimise riva 128 applications.txt b/doc/nvidia_notes/How to optimise riva 128 applications.txt index 1c20aef2a..004593a21 100644 --- a/doc/nvidia_notes/How to optimise riva 128 applications.txt +++ b/doc/nvidia_notes/How to optimise riva 128 applications.txt @@ -1,4 +1,6 @@ How to optimise riva 128 applications: * Ensure any set of polygons with one texture is close to a multiple of 128 polygons. * Try to sort areas of a model with one texture to as close to 128 polygons as possible for efficient submission due to the lack of texturing. -* Try to have around (32*128) for nv3 or (64*128) for nv3t polygons \ No newline at end of file +* Try to have around (32*128) for nv3 or (64*128) for nv3t polygons +* Get coding +* Alcohol \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index f0286e2db..2842cd5da 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -243,24 +243,27 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_SIZE_REV_C 64 #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 -#define NV3_PFIFO_CACHE0_ACCESS 0x3000 -#define NV3_PFIFO_CACHE0_DMA_CHANNEL_ID 0x3004 +#define NV3_PFIFO_CACHE0_PUSH_ACCESS 0x3000 +#define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 #define NV3_PFIFO_CACHE0_STATUS_RANOUT 0 // 1 if we fucked up #define NV3_PFIFO_CACHE0_STATUS_LOW_MARK 4 // 1 if ramro is empty #define NV3_PFIFO_CACHE0_STATUS_HIGH_MARK 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_SUCCESS 4 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_DEVICE 8 -#define NV3_PFIFO_CACHE0_PULLER_STATE1 0x3050 -#define NV3_PFIFO_CACHE0_PULLER_STATE1_CTX_IS_CLEAN 4 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_SUCCESS 4 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY 0x3050 +#define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY_BOOL 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 #define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE1_ACCESS 0x3200 -#define NV3_PFIFO_CACHE1_DMA_CHANNEL_ID 0x3204 +#define NV3_PFIFO_CACHE0_METHOD 0x3100 +#define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 +#define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 +#define NV3_PFIFO_CACHE1_PUSH_ACCESS 0x3200 +#define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 #define NV3_PFIFO_CACHE1_STATUS 0x3214 @@ -276,14 +279,17 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULLER_CONTROL 0x3240 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_SUCCESS 4 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL 0x3240 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_SUCCESS 4 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_DEVICE 8 #define NV3_PFIFO_CACHE1_PULLER_STATE1 0x3250 -#define NV3_PFIFO_CACHE1_PULLER_STATE1_CTX_IS_CLEAN 4 +#define NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 #define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 +#define NV3_PFIFO_CACHE1_METHOD 0x3300 +#define NV3_PFIFO_CACHE1_METHOD_ADDRESS 2 // 12:2 +#define NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 // 15:13 #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem #define NV3_PRM_INTR 0x4100 @@ -588,7 +594,8 @@ extern const device_config_t nv3_config[]; #define NV3_PRAMIN_RAMHT_SIZE_2 0x3FFF #define NV3_PRAMIN_RAMHT_SIZE_3 0x7FFF -#define NV3_PRAMIN_RAMAU_START 0x1C01000 // Auxillary area +/* OBSOLETE AREA for AUDIO probably. DO NOT USE! */ +#define NV3_PRAMIN_RAMAU_START 0x1C01000 #define NV3_PRAMIN_RAMAU_END 0x1C01BFF #define NV3_PRAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels #define NV3_PRAMIN_RAMFC_END 0x1C01DFF @@ -767,9 +774,10 @@ typedef struct nv3_pbus_s typedef struct nv3_pfifo_cache_s { + bool access_enabled; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. - uint8_t channel_id; + uint8_t channel_id; // The DMA channel ID of this cache. uint32_t status; uint32_t status_puller; uint32_t control; @@ -778,15 +786,19 @@ typedef struct nv3_pfifo_cache_s /* cache1 only do we even need to emulate this? */ + uint32_t dma_status; // 0x3218 bool dma_enabled; // 0x3220 bit0 bool dma_is_busy; // 0x3220 bit4 - uint32_t dma_length; - uint32_t dma_address; - uint8_t dma_target_node; // depends on card bus - uint8_t dma_tlb_tag; - uint8_t tlb_pte; // DMA Engine - Translation Lookaside Buffer - uint8_t tlb_pt_base; // DMA Engine - TLB Pagetable Base Addres + uint32_t dma_state; // Corresponds to PFIFO_CACHE1_DMA0 + uint32_t dma_length; // Corresponds to PFIFO_CACHE1_DMA1 + uint32_t dma_address; // Corresponds to PFIFO_CACHE1_DMA2 + uint8_t dma_target_node; // Corresponds to PFIFO_CACHE1_DMA3 depends on card bus + uint8_t dma_tlb_tag; + uint8_t dma_tlb_pte; // DMA Engine - Translation Lookaside Buffer + uint8_t dma_tlb_pt_base; // DMA Engine - TLB Pagetable Base Addres + uint16_t method_address; // address of the method (i.e. what method it is) + uint16_t method_subchannel; // subchannel bool context_is_dirty; /* TODO */ @@ -1293,6 +1305,10 @@ uint32_t nv3_pfifo_read(uint32_t address); void nv3_pfifo_write(uint32_t address, uint32_t value); // NV3 PFIFO - Caches +void nv3_pfifo_cache0_push(); +void nv3_pfifo_cache0_pull(); +void nv3_pfifo_cache1_push(); +void nv3_pfifo_cache1_pull(); uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index aaf27ab25..7956632f4 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -39,17 +39,33 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, - { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller State0", NULL, NULL}, - { NV3_PFIFO_CACHE0_PULLER_STATE1, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, + { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller Control", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller Control"}, + { NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller State0", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_STATE1, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - + { NV3_PFIFO_CACHE0_PUSH_ACCESS, "PFIFO - Cache0 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PUSH_ACCESS, "PFIFO - Cache1 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 DMA Channel ID", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 DMA Channel ID", NULL, NULL, }, + { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, + //Cache1 exclusive stuff + { NV3_PFIFO_CACHE1_DMA_CONFIG_0, "PFIFO - Cache1 DMA Config0"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_1, "PFIFO - Cache1 DMA Config1"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_2, "PFIFO - Cache1 DMA Config2"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_3, "PFIFO - Cache1 DMA Config3"}, + { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status"}, + { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA Translation Lookaside Buffer - Pagetable Base"}, + { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA Status"}, + { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA Status"}, + + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -107,7 +123,7 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_DEBUG_0: ret = nv3->pfifo.debug_0; break; - // These may need to become functions. + // Some of these may need to become functions. case NV3_PFIFO_CONFIG_RAMFC: ret = nv3->pfifo.ramfc_config; break; @@ -117,6 +133,45 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CONFIG_RAMRO: ret = nv3->pfifo.ramro_config; break; + case NV3_PFIFO_CACHE0_PULLER_CONTROL: + ret = nv3->pfifo.cache0_settings.control; + break; + case NV3_PFIFO_CACHE1_PULLER_CONTROL: + ret = nv3->pfifo.cache1_settings.control; + break; + case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: + ret = nv3->pfifo.cache0_settings.context_is_dirty; + break; + case NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY: + ret = nv3->pfifo.cache1_settings.context_is_dirty; + break; + case NV3_PFIFO_CACHE0_PUSH_ACCESS: + ret = nv3->pfifo.cache0_settings.access_enabled; + break; + case NV3_PFIFO_CACHE1_PUSH_ACCESS: + ret = nv3->pfifo.cache1_settings.access_enabled; + break; + case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: + ret = nv3->pfifo.cache0_settings.channel_id; + break; + case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: + ret = nv3->pfifo.cache1_settings.channel_id; + break; + case NV3_PFIFO_CACHE0_STATUS: + /* Todo: Return values based on runout put/get*/ + ret = nv3->pfifo.cache0_settings.status; + break; + case NV3_PFIFO_CACHE1_STATUS: + ret = nv3->pfifo.cache1_settings.status; + break; + case NV3_PFIFO_CACHE0_METHOD: + ret = ((nv3->pfifo.cache0_settings.method_subchannel << 13) & 0x07) + | ((nv3->pfifo.cache0_settings.method_address << 2) & 0x7FF); + break; + case NV3_PFIFO_CACHE1_METHOD: + ret = ((nv3->pfifo.cache1_settings.method_subchannel << 13) & 0x07) + | ((nv3->pfifo.cache1_settings.method_address << 2) & 0x7FF); + break; case NV3_PFIFO_CACHE0_GET: //wa break; @@ -124,13 +179,34 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE_REASSIGNMENT: ret = nv3->pfifo.cache_reassignment & 0x01; //1bit meaningful break; + // Cache1 exclusive stuff // Control - case NV3_PFIFO_CACHE0_PULLER_CONTROL: - ret = nv3->pfifo.cache0_settings.control; // 8bits meaningful + case NV3_PFIFO_CACHE1_DMA_CONFIG_0: + ret = nv3->pfifo.cache1_settings.dma_state; + break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_1: + ret = nv3->pfifo.cache1_settings.dma_length; break; - case NV3_PFIFO_CACHE1_PULLER_CONTROL: - ret = nv3->pfifo.cache1_settings.control; // only 8bits are meaningful + case NV3_PFIFO_CACHE1_DMA_CONFIG_2: + ret = nv3->pfifo.cache1_settings.dma_address; break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_3: + ret = nv3->pfifo.cache1_settings.dma_target_node; + break; + case NV3_PFIFO_CACHE1_DMA_STATUS: + ret = nv3->pfifo.cache1_settings.dma_status; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE: + ret = nv3->pfifo.cache1_settings.dma_tlb_pt_base; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_PTE: + ret = nv3->pfifo.cache1_settings.dma_tlb_pte; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_TAG: + ret = nv3->pfifo.cache1_settings.dma_tlb_tag; + break; + + } } @@ -243,7 +319,64 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) break; case NV3_PFIFO_CACHE1_PULLER_CONTROL: nv3->pfifo.cache1_settings.control = value; // 8bits meaningful - + break; + case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: + nv3->pfifo.cache0_settings.context_is_dirty = value; + break; + case NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY: + nv3->pfifo.cache1_settings.context_is_dirty = value; + break; + case NV3_PFIFO_CACHE0_PUSH_ACCESS: + nv3->pfifo.cache0_settings.access_enabled = value; + break; + case NV3_PFIFO_CACHE1_PUSH_ACCESS: + nv3->pfifo.cache1_settings.access_enabled = value; + break; + case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: + nv3->pfifo.cache0_settings.channel_id = value; + break; + case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: + nv3->pfifo.cache1_settings.channel_id = value; + break; + case NV3_PFIFO_CACHE0_STATUS: + /* Todo: Return values based on runout put/get*/ + nv3->pfifo.cache0_settings.status = value; + break; + case NV3_PFIFO_CACHE1_STATUS: + nv3->pfifo.cache1_settings.status = value; + break; + case NV3_PFIFO_CACHE0_METHOD: + nv3->pfifo.cache0_settings.method_subchannel = (value >> 13) & 0x07; + nv3->pfifo.cache0_settings.method_address = (value >> 2) & 0x7FF; + + break; + case NV3_PFIFO_CACHE1_METHOD: + nv3->pfifo.cache1_settings.method_subchannel = (value >> 13) & 0x07; + nv3->pfifo.cache1_settings.method_address = (value >> 2) & 0x7FF; + break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_0: + nv3->pfifo.cache1_settings.dma_state = value; + break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_1: + nv3->pfifo.cache1_settings.dma_length = value; + break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_2: + nv3->pfifo.cache1_settings.dma_address = value; + break; + case NV3_PFIFO_CACHE1_DMA_CONFIG_3: + nv3->pfifo.cache1_settings.dma_target_node = value; + break; + case NV3_PFIFO_CACHE1_DMA_STATUS: + nv3->pfifo.cache1_settings.dma_status = value; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE: + nv3->pfifo.cache1_settings.dma_tlb_pt_base = value; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_PTE: + nv3->pfifo.cache1_settings.dma_tlb_pte = value; + break; + case NV3_PFIFO_CACHE1_DMA_TLB_TAG: + nv3->pfifo.cache1_settings.dma_tlb_tag = value; break; } } @@ -279,4 +412,24 @@ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) } return val; +} + +void nv3_pfifo_cache0_push() +{ + +} + +void nv3_pfifo_cache0_pull() +{ + +} + +void nv3_pfifo_cache1_push() +{ + +} + +void nv3_pfifo_cache1_pull() +{ + } \ No newline at end of file From 5b3617efe61d5f8666406017dad2a991a5ff1574 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 28 Jan 2025 23:35:25 +0000 Subject: [PATCH 072/274] standardise on "ramin" over "pramin", add cache status registers --- src/include/86box/nv/vid_nv3.h | 135 +++++++++--------- src/video/nv/nv3/nv3_core_arbiter.c | 8 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 40 ++++-- src/video/nv/nv3/subsystems/nv3_pramin.c | 40 +++--- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 2 +- 5 files changed, 127 insertions(+), 98 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 2842cd5da..2a635e8b4 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 2 January 2025 (STILL WORKING ON IT!!!) + * Last updated: 28 January 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -231,8 +231,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_RUNOUT_STATUS 0x2400 #define NV3_PFIFO_RUNOUT_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_RUNOUT_STATUS_LOW_MARK 4 // 1 if ramro is empty -#define NV3_PFIFO_RUNOUT_STATUS_HIGH_MARK 8 +#define NV3_PFIFO_RUNOUT_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_RUNOUT_STATUS_FULL 8 #define NV3_PFIFO_RUNOUT_PUT 0x2410 #define NV3_PFIFO_RUNOUT_PUT_ADDRESS 3 // 9:3 if small ramfc(?) otherwise 12:3 #define NV3_PFIFO_RUNOUT_GET 0x2420 @@ -247,9 +247,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 -#define NV3_PFIFO_CACHE0_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_CACHE0_STATUS_LOW_MARK 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE0_STATUS_HIGH_MARK 8 +#define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE0_STATUS_FULL 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit #define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 @@ -268,8 +267,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 #define NV3_PFIFO_CACHE1_STATUS 0x3214 #define NV3_PFIFO_CACHE1_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_CACHE1_STATUS_LOW_MARK 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE1_STATUS_HIGH_MARK 8 +#define NV3_PFIFO_CACHE1_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE1_STATUS_FULL 8 #define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 #define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 #define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 @@ -583,32 +582,30 @@ extern const device_config_t nv3_config[]; // 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_RAMIN_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_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects +#define NV3_RAMIN_RAMHT_END 0x1C00FFF +#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF +#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF +#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF +#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF /* OBSOLETE AREA for AUDIO probably. DO NOT USE! */ -#define NV3_PRAMIN_RAMAU_START 0x1C01000 -#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_RAMIN_RAMAU_START 0x1C01000 +#define NV3_RAMIN_RAMAU_END 0x1C01BFF +#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels +#define NV3_RAMIN_RAMFC_END 0x1C01DFF +#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF +#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF +#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions +#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF +#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF +#define NV3_RAMIN_RAMRO_END 0x1C01FFF +#define NV3_RAMIN_RAMRM_START 0x1C02000 +#define NV3_RAMIN_RAMRM_END 0x1C02FFF -#define NV3_PRAMIN_END 0x1FFFFFF +#define NV3_RAMIN_END 0x1FFFFFF // not done @@ -821,13 +818,19 @@ typedef struct nv3_pfifo_s uint32_t ramht_config; // RAMHT config uint32_t ramfc_config; // RAMFC config uint32_t ramro_config; // RAMRO config + // Runout stuff + uint32_t runout_put; + uint32_t runout_get; + + // Cache stuff uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? nv3_pfifo_cache_t cache0_settings; nv3_pfifo_cache_t cache1_settings; nv3_pfifo_cache_entry_t cache0_entries[1]; nv3_pfifo_cache_entry_t cache1_entries[NV3_PFIFO_CACHE1_SIZE_MAX]; // ONLY 32 USED ON REVISION A/B CARDS - + + } nv3_pfifo_t; // create_object(uint32_t type) here @@ -1047,7 +1050,7 @@ typedef struct nv3_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; -typedef struct nv3_pramin_name_s +typedef struct nv3_ramin_name_s { union { @@ -1062,9 +1065,9 @@ typedef struct nv3_pramin_name_s }; }; -} nv3_pramin_name_t; +} nv3_ramin_name_t; -typedef struct nv3_pramin_context_s +typedef struct nv3_ramin_context_s { union { @@ -1080,27 +1083,27 @@ typedef struct nv3_pramin_context_s }; }; -} nv3_pramin_context_t; +} nv3_ramin_context_t; // Graphics object hashtable for specific DMA [channel, subchannel] pair -typedef struct nv3_pramin_ramht_subchannel_s +typedef struct nv3_ramin_ramht_subchannel_s { - nv3_pramin_name_t name; // must be >4096 + nv3_ramin_name_t name; // must be >4096 // Contextual information. // See the above union. - nv3_pramin_context_t context; -} nv3_pramin_ramht_subchannel_t; + nv3_ramin_context_t context; +} nv3_ramin_ramht_subchannel_t; // Graphics object hashtable -typedef struct nv3_pramin_ramht_s +typedef struct nv3_ramin_ramht_s { - nv3_pramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; -} nv3_pramin_ramht_t; + nv3_ramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; +} nv3_ramin_ramht_t; -uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel); +uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel); -typedef enum nv3_pramin_ramro_reason_e +typedef enum nv3_ramin_ramro_reason_e { nv3_runout_reason_illegal_access = 0, @@ -1117,37 +1120,40 @@ typedef enum nv3_pramin_ramro_reason_e // Access reserved by pagetable nv3_runout_reason_reserved_access = 5, -} nv3_pramin_ramro_reason; +} nv3_ramin_ramro_reason; /* This is a gigantic error handling system */ -typedef struct nv3_pramin_ramro_entry_s +typedef struct nv3_ramin_ramro_entry_s { //todo -} nv3_pramin_ramro_entry_t; +} nv3_ramin_ramro_entry_t; // Anti-fuckup device -typedef struct nv3_pramin_ramro_s +typedef struct nv3_ramin_ramro_s { -} nv3_pramin_ramro_t; +} nv3_ramin_ramro_t; // context for unused channels -typedef struct nv3_pramin_ramfc_s +typedef struct nv3_ramin_ramfc_s { -} nv3_pramin_ramfc_t; +} nv3_ramin_ramfc_t; -// ????? ram auxillary -typedef struct nv_pramin_ramau_s +// RAM for AUDIO - RevisionA ONLY +typedef struct nv_ramin_ramau_s { -} nv3_pramin_ramau_t; +} nv3_ramin_ramau_t; -typedef struct nv3_pramin_s +typedef struct nv3_ramin_s { -} nv3_pramin_t; +} nv3_ramin_t; + +// RAMIN functions +void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id); typedef struct nv3_pvideo_s { @@ -1179,11 +1185,11 @@ typedef struct nv3_s 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_ramin_ramht_t ramht; // hashtable for PGRAPH objects + nv3_ramin_ramro_t ramro; // anti-fuckup mechanism for idiots who fucked up the FIFO submission + nv3_ramin_ramfc_t ramfc; // context for unused channels + nv3_ramin_ramau_t ramau; // auxillary weirdnes + nv3_ramin_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 @@ -1222,8 +1228,8 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv); 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 -bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value); // Read arbitration so we can read/write to the structures in the first 64k of ramin -bool nv3_pramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin +bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value); // Read arbitration so we can read/write to the structures in the first 64k of ramin +bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin uint32_t nv3_ramfc_read(uint32_t address); void nv3_ramfc_write(uint32_t address, uint32_t value); @@ -1284,7 +1290,7 @@ 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 -// TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_pramin_* +// TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_ramin_* // GPU subsystems @@ -1311,6 +1317,7 @@ void nv3_pfifo_cache1_push(); void nv3_pfifo_cache1_pull(); uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); +bool nv3_pfifo_cache1_is_free(); // NV3 PFB void nv3_pfb_init(); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 88ab74fe5..2022625fd 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -104,8 +104,8 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) else if (address >= NV3_USER_START && address <= NV3_USER_END) ret = nv3_user_read(address); // RAMIN is handled by a separate memory mapping in PCI BAR1 - //else if (address >= NV3_PRAMIN_START && address <= NV3_PRAMIN_END) - //ret = nv3_pramin_arbitrate_read(address); // RAMHT, RAMFC, RAMRO etc dettermined by nv3_ramin_* function + //else if (address >= NV3_RAMIN_START && address <= NV3_RAMIN_END) + //ret = nv3_ramin_arbitrate_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); @@ -167,8 +167,8 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) 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_arbitrate_write(address, value); // RAMHT, RAMFC, RAMRO etc is determined by the nv3_ramin_* functions + else if (address >= NV3_RAMIN_START && address <= NV3_RAMIN_END) + nv3_ramin_arbitrate_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); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 7956632f4..4638b066e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -158,10 +158,28 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.cache1_settings.channel_id; break; case NV3_PFIFO_CACHE0_STATUS: - /* Todo: Return values based on runout put/get*/ - ret = nv3->pfifo.cache0_settings.status; + uint32_t ret = 0x00; + + // CACHE0 has only one entry so it can only ever be empty or full + + if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache1_settings.get_address) + ret |= 1 << NV3_PFIFO_CACHE0_STATUS_EMPTY; + else + ret |= 1 << NV3_PFIFO_CACHE0_STATUS_FULL; + break; case NV3_PFIFO_CACHE1_STATUS: + if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) + ret |= 1 << NV3_PFIFO_CACHE1_STATUS_EMPTY; + + // Check if Cache1 (0x7C bytes in size depending on gpu?) is full + // Based on how the drivers do it + if (!nv3_pfifo_cache1_is_free()) + ret |= 1 << NV3_PFIFO_CACHE1_STATUS_FULL; + + if (nv3->pfifo.runout_put == nv3->pfifo.runout_get) + ret |= 1 << NV3_PFIFO_CACHE1_STATUS_RANOUT; + ret = nv3->pfifo.cache1_settings.status; break; case NV3_PFIFO_CACHE0_METHOD: @@ -338,13 +356,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: nv3->pfifo.cache1_settings.channel_id = value; break; - case NV3_PFIFO_CACHE0_STATUS: - /* Todo: Return values based on runout put/get*/ - nv3->pfifo.cache0_settings.status = value; - break; - case NV3_PFIFO_CACHE1_STATUS: - nv3->pfifo.cache1_settings.status = value; - break; + // CACHE0_STATUS and CACHE1_STATUS are not writable case NV3_PFIFO_CACHE0_METHOD: nv3->pfifo.cache0_settings.method_subchannel = (value >> 13) & 0x07; nv3->pfifo.cache0_settings.method_address = (value >> 2) & 0x7FF; @@ -432,4 +444,14 @@ void nv3_pfifo_cache1_push() void nv3_pfifo_cache1_pull() { +} + +bool nv3_pfifo_cache1_is_free() +{ + // convert to gray code + uint32_t real_get_address = nv3_pfifo_cache1_normal2gray(nv3->pfifo.cache1_settings.get_address); + uint32_t real_put_address = nv3_pfifo_cache1_normal2gray(nv3->pfifo.cache1_settings.put_address); + + // There is no hope of being able to understand it. Nobody can understand + return (real_get_address - real_put_address - 4) & 0x7C; // there are 64 entries what } \ 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 index 8203519c5..9e013f4aa 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -52,7 +52,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) uint32_t val = 0x00; - if (!nv3_pramin_arbitrate_read(addr, &val)) // Oh well + if (!nv3_ramin_arbitrate_read(addr, &val)) // Oh well { val = (uint8_t)nv3->nvbase.svga.vram[addr]; nv_log("NV3: Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); @@ -78,7 +78,7 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) uint32_t val = 0x00; - if (!nv3_pramin_arbitrate_read(addr, &val)) + if (!nv3_ramin_arbitrate_read(addr, &val)) { val = (uint16_t)vram_16bit[addr]; nv_log("NV3: Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); @@ -104,7 +104,7 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) uint32_t val = 0x00; - if (!nv3_pramin_arbitrate_read(addr, &val)) + if (!nv3_ramin_arbitrate_read(addr, &val)) { val = vram_32bit[addr]; @@ -130,7 +130,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) uint32_t val32 = 0x00; - if (!nv3_pramin_arbitrate_write(addr, val32)) + if (!nv3_ramin_arbitrate_write(addr, val32)) { nv3->nvbase.svga.vram[addr] = val; nv_log("NV3: Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); @@ -156,7 +156,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) uint32_t val32 = 0x00; - if (!nv3_pramin_arbitrate_write(addr, val32)) + if (!nv3_ramin_arbitrate_write(addr, val32)) { vram_16bit[addr] = val; nv_log("NV3: Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); @@ -182,7 +182,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) uint32_t val32 = 0x00; - if (!nv3_pramin_arbitrate_write(addr, val32)) + if (!nv3_ramin_arbitrate_write(addr, val32)) { vram_32bit[addr] = val; nv_log("NV3: Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); @@ -198,7 +198,7 @@ and generic RAMIN Takes a pointer to a result integer. This is because we need to check its result in our normal write function. Returns true if a valid "non-generic" address was found (e.g. RAMFC/RAMRO/RAMHT). False if the specified address is a generic RAMIN address */ -bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value) +bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value) { if (!nv3) return 0x00; @@ -220,26 +220,26 @@ bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value) switch (ramht_size) { case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_0; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_0; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_1; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_1; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_2; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_2; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_3; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_3; break; } switch (ramro_size) { case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: - ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_0; + ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_0; break; case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: - ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_1; + ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_1; break; } @@ -266,7 +266,7 @@ bool nv3_pramin_arbitrate_read(uint32_t address, uint32_t* value) return false; } -bool nv3_pramin_arbitrate_write(uint32_t address, uint32_t value) +bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) { if (!nv3) return 0x00; @@ -288,26 +288,26 @@ bool nv3_pramin_arbitrate_write(uint32_t address, uint32_t value) switch (ramht_size) { case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_0; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_0; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_1; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_1; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_2; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_2; break; case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - ramht_end = ramht_start + NV3_PRAMIN_RAMHT_SIZE_3; + ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_3; break; } switch (ramro_size) { case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: - ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_0; + ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_0; break; case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: - ramro_end = ramro_start + NV3_PRAMIN_RAMRO_SIZE_1; + ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_1; break; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index 7df99d697..6568e6db4 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -32,7 +32,7 @@ It is used to get the offset within RAMHT of a graphics object. */ -uint32_t nv3_ramht_hash(nv3_pramin_name_t name, uint32_t channel) +uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel) { uint32_t hash = (name.byte_high ^ name.byte_mid2 ^ name.byte_mid1 ^ name.byte_low ^ (uint8_t)channel); nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", name, channel); From 8c19d2682df259071a069b6104125c192804d6f2 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 29 Jan 2025 20:25:41 +0000 Subject: [PATCH 073/274] Create a bunch of placeholder c files for every single fucking class --- src/include/86box/nv/vid_nv3.h | 7 ++--- src/video/CMakeLists.txt | 26 +++++++++++++++++++ .../nv3/classes/nv3_class_001_beta_factor.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_002_rop.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_003_chroma_key.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_004_plane_mask.c | 16 ++++++++++++ .../nv3_class_005_clipping_rectangle.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_006_pattern.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_007_rectangle.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_008_point.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_009_line.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_00a_lin.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_00b_triangle.c | 16 ++++++++++++ .../classes/nv3_class_00c_win95_gdi_text.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 16 ++++++++++++ .../nv3_class_00e_scaled_image_from_mem.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_010_blit.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_011_image.c | 16 ++++++++++++ .../nv/nv3/classes/nv3_class_012_bitmap.c | 16 ++++++++++++ .../classes/nv3_class_014_transfer2memory.c | 16 ++++++++++++ .../nv3_class_015_stretched_image_from_cpu.c | 16 ++++++++++++ .../nv3_class_017_d3d5_tri_zeta_buffer.c | 16 ++++++++++++ .../classes/nv3_class_018_point_zeta_buffer.c | 16 ++++++++++++ .../classes/nv3_class_01c_image_in_memory.c | 16 ++++++++++++ src/video/nv/nv3/classes/nv3_class_names.c | 4 +-- src/video/nv/nv3/subsystems/nv3_pramin.c | 6 +++++ 26 files changed, 390 insertions(+), 5 deletions(-) create mode 100644 src/video/nv/nv3/classes/nv3_class_001_beta_factor.c create mode 100644 src/video/nv/nv3/classes/nv3_class_002_rop.c create mode 100644 src/video/nv/nv3/classes/nv3_class_003_chroma_key.c create mode 100644 src/video/nv/nv3/classes/nv3_class_004_plane_mask.c create mode 100644 src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c create mode 100644 src/video/nv/nv3/classes/nv3_class_006_pattern.c create mode 100644 src/video/nv/nv3/classes/nv3_class_007_rectangle.c create mode 100644 src/video/nv/nv3/classes/nv3_class_008_point.c create mode 100644 src/video/nv/nv3/classes/nv3_class_009_line.c create mode 100644 src/video/nv/nv3/classes/nv3_class_00a_lin.c create mode 100644 src/video/nv/nv3/classes/nv3_class_00b_triangle.c create mode 100644 src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c create mode 100644 src/video/nv/nv3/classes/nv3_class_00d_m2mf.c create mode 100644 src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c create mode 100644 src/video/nv/nv3/classes/nv3_class_010_blit.c create mode 100644 src/video/nv/nv3/classes/nv3_class_011_image.c create mode 100644 src/video/nv/nv3/classes/nv3_class_012_bitmap.c create mode 100644 src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c create mode 100644 src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c create mode 100644 src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c create mode 100644 src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c create mode 100644 src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 2a635e8b4..aa719a7d2 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1101,7 +1101,6 @@ typedef struct nv3_ramin_ramht_s nv3_ramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; } nv3_ramin_ramht_t; -uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel); typedef enum nv3_ramin_ramro_reason_e { @@ -1152,8 +1151,6 @@ typedef struct nv3_ramin_s } nv3_ramin_t; -// RAMIN functions -void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id); typedef struct nv3_pvideo_s { @@ -1231,6 +1228,10 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv); bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value); // Read arbitration so we can read/write to the structures in the first 64k of ramin bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin +// RAMIN functions +uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel); +void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id); + uint32_t nv3_ramfc_read(uint32_t address); void nv3_ramfc_write(uint32_t address, uint32_t value); uint32_t nv3_ramro_read(uint32_t address); diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index b99f4fc4b..2135213fb 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -11,8 +11,11 @@ # Authors: David Hrdlička, # # Copyright 2020-2021 David Hrdlička. +# Copyright 2025 Connor Hyde # +# todo: Split nv stuff into its own file... + add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c vid_compaq_cga.c vid_mda.c vid_hercules.c vid_herculesplus.c vid_incolor.c vid_colorplus.c vid_genius.c vid_pgc.c vid_im1024.c @@ -43,6 +46,29 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.c nv/nv3/subsystems/nv3_pvideo.c nv/nv3/classes/nv3_class_names.c + nv/nv3/classes/nv3_class_001_beta_factor.c + nv/nv3/classes/nv3_class_002_rop.c + nv/nv3/classes/nv3_class_003_chroma_key.c + nv/nv3/classes/nv3_class_004_plane_mask.c + nv/nv3/classes/nv3_class_005_clipping_rectangle.c + nv/nv3/classes/nv3_class_006_pattern.c + nv/nv3/classes/nv3_class_007_rectangle.c + nv/nv3/classes/nv3_class_008_point.c + nv/nv3/classes/nv3_class_009_line.c + nv/nv3/classes/nv3_class_00a_lin.c + nv/nv3/classes/nv3_class_00b_triangle.c + nv/nv3/classes/nv3_class_00c_win95_gdi_text.c + nv/nv3/classes/nv3_class_00d_m2mf.c + nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c + nv/nv3/classes/nv3_class_010_blit.c + nv/nv3/classes/nv3_class_011_image.c + nv/nv3/classes/nv3_class_012_bitmap.c + nv/nv3/classes/nv3_class_014_transfer2memory.c + nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c + nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c + nv/nv3/classes/nv3_class_018_point_zeta_buffer.c + nv/nv3/classes/nv3_class_01c_image_in_memory.c + ) if(G100) diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c new file mode 100644 index 000000000..2f9cdb066 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x01 (Beta factor) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c new file mode 100644 index 000000000..e3256d699 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x02 (Render operation) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c new file mode 100644 index 000000000..188f5f94e --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x02 (Chroma/color key) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c new file mode 100644 index 000000000..0797819d7 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x02 (Plane mask) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c new file mode 100644 index 000000000..c675be6e8 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x05 (Clipping rectangle) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c new file mode 100644 index 000000000..d75307a24 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x06 (Pattern) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c new file mode 100644 index 000000000..513f19496 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x07 (Rectangle) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c new file mode 100644 index 000000000..92561591a --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x08 (Point) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c new file mode 100644 index 000000000..d10501558 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x09 (Line) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c new file mode 100644 index 000000000..372a56e92 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x0A (Lin - a line without starting or ending pixels) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c new file mode 100644 index 000000000..1b8c23f03 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x0B (Basic triangle) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c new file mode 100644 index 000000000..9c58cf250 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x0C (Windows 95 GDI text acceleration) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c new file mode 100644 index 000000000..0b6c48cc1 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x0D (Reformat image in memory) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c new file mode 100644 index 000000000..6e90a552a --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x0E (Get image from vram and scale it) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c new file mode 100644 index 000000000..5e6f19aff --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x10 (Blit something) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c new file mode 100644 index 000000000..3334b4185 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x11 (Color image) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c new file mode 100644 index 000000000..b5c62f5f4 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x12 (Monochrome bitmap) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c new file mode 100644 index 000000000..c4b97a98a --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x14 (Transfer to Memory) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c new file mode 100644 index 000000000..951d77bce --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x15 (stretched image to memory) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c new file mode 100644 index 000000000..4e0463104 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x17 (Direct3D 5.0 accelerated triangle with zeta buffer) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c new file mode 100644 index 000000000..0440c39fe --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x18 (Point with zeta buffer) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c new file mode 100644 index 000000000..daea3e0bb --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -0,0 +1,16 @@ +/* + * 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: Methods for class 0x1C (Image in memory) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index 0ee56443b..3604d147b 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -12,7 +12,7 @@ * * Authors: Connor Hyde, I need a better email address ;^) * - * Copyright 2024-2025 starfrost + * Copyright 2024-2025 Connor Hyde */ #include #include @@ -34,7 +34,7 @@ const char* nv3_class_names[] = { "NV3 INVALID class 0x00", "NV3 class 0x01: Beta factor", - "NV3 class 0x02: ROP5 (32-bit) operation", + "NV3 class 0x02: Render operation", "NV3 class 0x03: Chroma key", "NV3 class 0x04: Plane mask", "NV3 class 0x05: Clipping rectangle", diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 9e013f4aa..f27ca0415 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -331,4 +331,10 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) } return false; +} + +// THIS IS THE MOST IMPORTANT FUNCTION! +void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id) +{ + // TODO: WRITE IT!!! } \ No newline at end of file From ce27b51ff5bd81cb2b307afbb516c85ab8d364a8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 29 Jan 2025 20:38:52 +0000 Subject: [PATCH 074/274] log.c in accordance with upstream --- src/log.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/log.c b/src/log.c index 6839e1243..129f2313f 100644 --- a/src/log.c +++ b/src/log.c @@ -40,7 +40,7 @@ typedef struct log_t { char *dev_name; int seen; int suppr_seen; - char cyclic_buff[LOG_SIZE_BUFFER_CYCLIC_LINES][LOG_SIZE_BUFFER]; // Cyclical log buffer. This is 32kb, might calloc? + char** cyclic_buff; int32_t cyclic_last_line; int32_t log_cycles; int32_t last_repeat_order; @@ -94,14 +94,6 @@ log_set_suppr_seen(void *priv, int suppr_seen) log->suppr_seen = suppr_seen; } -void -log_set_dev_name(void *priv, char *dev_name) -{ - log_t *log = (log_t *) priv; - - log->dev_name = dev_name; -} - /* Log something to the logfile or stdout. @@ -280,11 +272,14 @@ log_out_cyclic(void* priv, const char* fmt, va_list ap) fprintf(stdlog, "%s", temp); } - log->cyclic_last_line++; - - log->last_repeat_order = repeat_order; + log->cyclic_last_line++; + + log->last_repeat_order = repeat_order; + } +} #endif + void log_fatal(void *priv, const char *fmt, ...) { From 1e8c804cfdda0c87ff101198cb77fc98fe69daf3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 31 Jan 2025 01:21:28 +0000 Subject: [PATCH 075/274] Implement RAMIN lookup and start the pullers. I hate nvidia --- src/include/86box/nv/vid_nv3.h | 116 +++++++------ src/log.c | 17 +- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 14 ++ src/video/nv/nv3/subsystems/nv3_pramin.c | 163 +++++++++++++++++- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 12 +- 6 files changed, 254 insertions(+), 70 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index aa719a7d2..651693257 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,8 +14,8 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 28 January 2025 (STILL WORKING ON IT!!!) - * + * Last updated: 30 January 2025 (STILL WORKING ON IT!!!) + * * Authors: Connor Hyde * * Copyright 2024-2025 Connor Hyde @@ -43,9 +43,16 @@ extern const device_config_t nv3_config[]; #define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 10 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. +#define NV3_LAST_VALID_GRAPHICS_OBJECT_ID 0x1F + +// The class ids are represented with 5 bits in PGRAPH, but 7 bits in PFIFO! +// What... +#define NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID 0x40 +#define NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID 0x5F + // Default value for the boot information register. // Depends on the chip -#define NV3_BOOT_REG_REV_A00 0x00030100 +#define NV3_BOOT_REG_REV_A00 0x00030100 // todo: format is wrong(?) for nv3a, fix it later #define NV3_BOOT_REG_REV_B00 0x00030110 #define NV3_BOOT_REG_REV_C00 0x00030120 @@ -72,7 +79,7 @@ extern const device_config_t nv3_config[]; #define VRAM_SIZE_2MB 0x200000 // 2MB #define VRAM_SIZE_4MB 0x400000 // 4MB #define VRAM_SIZE_8MB 0x800000 // NV3T only - +// There is also 1mb supported by the card but it was never used // PCI config #define NV3_PCI_CFG_VENDOR_ID 0x0 @@ -194,6 +201,7 @@ extern const device_config_t nv3_config[]; #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 @@ -202,18 +210,25 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_DEBUG_0 0x2080 // PFIFO Debug Register #define NV3_PFIFO_CACHE0_ERROR_PENDING 0 -#define NV3_PFIFO_CACHE1_ERROR_PENDING 1 +#define NV3_PFIFO_CACHE1_ERROR_PENDING 4 #define NV3_PFIFO_INTR 0x2100 // FIFO - Interrupt Status #define NV3_PFIFO_INTR_EN 0x2140 // FIFO - Interrupt Enable +// PFIFO interrupts +#define NV3_PFIFO_INTR_CACHE_ERROR 0 +#define NV3_PFIFO_INTR_RUNOUT 4 +#define NV3_PFIFO_INTR_RUNOUT_OVERFLOW 8 +#define NV3_PFIFO_INTR_DMA_PUSHER 12 +#define NV3_PFIFO_INTR_DMA_PTE 16 + #define NV3_PFIFO_CONFIG_0 0x2200 #define NV3_PFIFO_CONFIG_0_DMA_FETCH 8 #define NV3_PFIFO_CONFIG_RAMHT 0x2210 // Hashtable for graphics objects config -#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS 12 +#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS 12 // 15:12 #define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS_DEFAULT 0x0 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE 16 +#define NV3_PFIFO_CONFIG_RAMHT_SIZE 16 // 17:16 #define NV3_PFIFO_CONFIG_RAMHT_SIZE_4K 0x0 #define NV3_PFIFO_CONFIG_RAMHT_SIZE_8K 0x1 #define NV3_PFIFO_CONFIG_RAMHT_SIZE_16K 0x2 @@ -231,8 +246,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_RUNOUT_STATUS 0x2400 #define NV3_PFIFO_RUNOUT_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_RUNOUT_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_RUNOUT_STATUS_FULL 8 +#define NV3_PFIFO_RUNOUT_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_RUNOUT_STATUS_FULL 8 #define NV3_PFIFO_RUNOUT_PUT 0x2410 #define NV3_PFIFO_RUNOUT_PUT_ADDRESS 3 // 9:3 if small ramfc(?) otherwise 12:3 #define NV3_PFIFO_RUNOUT_GET 0x2420 @@ -243,17 +258,18 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_SIZE_REV_C 64 #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 + #define NV3_PFIFO_CACHE0_PUSH_ACCESS 0x3000 #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 -#define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE0_STATUS_FULL 8 +#define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE0_STATUS_FULL 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit #define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_SUCCESS 4 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD 8 #define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY 0x3050 #define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY_BOOL 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 @@ -267,8 +283,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 #define NV3_PFIFO_CACHE1_STATUS 0x3214 #define NV3_PFIFO_CACHE1_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_CACHE1_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE1_STATUS_FULL 8 +#define NV3_PFIFO_CACHE1_STATUS_EMPTY 4 // 1 if ramro is empty +#define NV3_PFIFO_CACHE1_STATUS_FULL 8 #define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 #define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 #define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 @@ -279,9 +295,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA #define NV3_PFIFO_CACHE1_PULLER_CONTROL 0x3240 +//todo: merge stuff #define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_SUCCESS 4 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_DEVICE 8 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD 8 // 0=software, 1=hardware #define NV3_PFIFO_CACHE1_PULLER_STATE1 0x3250 #define NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 @@ -289,6 +306,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_METHOD 0x3300 #define NV3_PFIFO_CACHE1_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 // 15:13 + #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem #define NV3_PRM_INTR 0x4100 @@ -684,9 +702,8 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_BANKED_32K_B0000 0x08 #define NV3_CRTC_BANKED_32K_B8000 0x0C - - #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 @@ -696,13 +713,11 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F - /* STRUCTURES FOR THE GPU START HERE OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H */ - //todo: pixel format // Master Control @@ -712,12 +727,15 @@ 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 + Revision A: + 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# + Revision B/c: + write this later */ int32_t boot; - int32_t interrupt_status; // Determines if interrupts are pending for specific subsystems. + 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. @@ -776,9 +794,9 @@ typedef struct nv3_pfifo_cache_s uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. uint8_t channel_id; // The DMA channel ID of this cache. uint32_t status; - uint32_t status_puller; + uint32_t puller_control; uint32_t control; - uint32_t context[8]; + uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; /* cache1 only do we even need to emulate this? @@ -1050,23 +1068,9 @@ typedef struct nv3_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv3_ptimer_t; -typedef struct nv3_ramin_name_s -{ - union - { - uint32_t name; - - struct - { - uint8_t byte_high; - uint8_t byte_mid2; - uint8_t byte_mid1; - uint8_t byte_low; - }; - }; - -} nv3_ramin_name_t; - +// Object name is just a uint32_t identifier it doesn't need a struct +// This is howt he cotnext is represented in ramin +// IN PGRAPH IT IS DIFFERENT! ONLY 5 BITS FOR THE CLASS ID! WHY? typedef struct nv3_ramin_context_s { union @@ -1075,11 +1079,11 @@ typedef struct nv3_ramin_context_s struct { - - uint8_t dma_channel; - uint8_t render_object; //0=sw, 1=hw accelerated render - uint8_t class_id; - uint8_t ramin_offset; //find + bool reserved : 1; + uint8_t channel_id : 7; + bool is_rendering : 1; + uint8_t class_id : 7; + uint16_t ramin_offset; }; }; @@ -1088,7 +1092,7 @@ typedef struct nv3_ramin_context_s // Graphics object hashtable for specific DMA [channel, subchannel] pair typedef struct nv3_ramin_ramht_subchannel_s { - nv3_ramin_name_t name; // must be >4096 + uint32_t name; // must be >4096 // Contextual information. // See the above union. @@ -1193,10 +1197,13 @@ typedef struct nv3_s } nv3_t; -// device objects +// device object extern nv3_t* nv3; -// NV3 stuff +/* + *FUNCTIONS* for the GPU core start here + Functions for PGRAPH objects are in vid_nv3_classes.h +*/ // Device Core void* nv3_init(const device_t *info); @@ -1229,8 +1236,8 @@ bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value); / bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin // RAMIN functions -uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel); -void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id); +uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel); +bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_id, uint32_t subchannel_id); uint32_t nv3_ramfc_read(uint32_t address); void nv3_ramfc_write(uint32_t address, uint32_t value); @@ -1310,6 +1317,7 @@ void nv3_pgraph_vblank_start(svga_t* svga); void nv3_pfifo_init(); uint32_t nv3_pfifo_read(uint32_t address); void nv3_pfifo_write(uint32_t address, uint32_t value); +void nv3_pfifo_interrupt(uint32_t id, bool fire_now); // NV3 PFIFO - Caches void nv3_pfifo_cache0_push(); diff --git a/src/log.c b/src/log.c index 129f2313f..fa39a6897 100644 --- a/src/log.c +++ b/src/log.c @@ -36,14 +36,15 @@ #include <86box/log.h> typedef struct log_t { - char buff[1024]; - char *dev_name; - int seen; - int suppr_seen; - char** cyclic_buff; - int32_t cyclic_last_line; - int32_t log_cycles; - int32_t last_repeat_order; + char buff[1024]; + char dev_name[1024]; + int seen; + int suppr_seen; + /* Cyclical log buffer. */ + char **cyclic_buff; + int32_t cyclic_last_line; + int32_t log_cycles; + int32_t last_repeat_order; } log_t; /* File to log output to. */ diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 57d19fc42..3aa1d497b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * NV3 PBUS DMA: DMA Engine + * NV3 PBUS DMA: DMA & Notifier Engine * * * diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 4638b066e..b9e7e4cdd 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -433,7 +433,15 @@ void nv3_pfifo_cache0_push() void nv3_pfifo_cache0_pull() { + // Do nothing if PFIFO CACHE0 is disabled + if (!nv3->pfifo.cache0_settings.puller_control & (1 >> NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED)) + return; + // Do nothing if there is nothing in cache0 to pull + if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache0_settings.get_address) + return; + + // There is only one entry } void nv3_pfifo_cache1_push() @@ -443,7 +451,13 @@ void nv3_pfifo_cache1_push() void nv3_pfifo_cache1_pull() { + // Do nothing if PFIFO CACHE1 is disabled + if (!nv3->pfifo.cache1_settings.puller_control & (1 >> NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED)) + return; + // Do nothing if there is nothing in cache1 to pull + if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) + return; } bool nv3_pfifo_cache1_is_free() diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index f27ca0415..d9ab438d7 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -23,11 +23,17 @@ #include <86Box/86box.h> #include <86Box/device.h> #include <86Box/mem.h> -#include <86box/pci.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> +#include <86box/nv/classes/vid_nv3_classes.h> + +// Functions only used in this translation unit +#ifndef RELEASE_BUILD +void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); +#endif // 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 @@ -190,6 +196,12 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) } +void nv3_pfifo_interrupt(uint32_t id, bool fire_now) +{ + nv3->pfifo.interrupt_status |= (1 << id); + nv3_pmc_handle_interrupts(fire_now); +} + /* RAMIN access arbitration functions Arbitrates reads and writes to RAMFC (unused dma context storage), RAMRO (invalid object submission location), RAMHT (hashtable for graphics objectstorage) (RAMAU?) @@ -311,6 +323,7 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) break; } + // send the addresses to the right part if (address >= ramht_start && address <= ramht_end) { @@ -334,7 +347,151 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) } // THIS IS THE MOST IMPORTANT FUNCTION! -void nv3_ramin_find_object(uint32_t name, uint32_t cache_id, uint32_t channel_id, uint32_t subchannel_id) +bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_id, uint32_t subchannel_id) { // TODO: WRITE IT!!! -} \ No newline at end of file + // Set the number of entries to search based on the ramht size (2*(size+1)) + // Not a switch statement in case newer gpus have larger ramins + uint32_t bucket_entries = 2 * (((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03) + 1); + + // Calculate the address in the hashtable + uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; + uint32_t ramht_cur_address = ramht_base; + + nv_log("NV3: Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel_id, subchannel_id); + + bool found_object = false; + + // set up some variables + uint32_t found_obj_name; + nv3_ramin_context_t obj_context_struct; + + for (uint32_t bucket_entry = 0; bucket_entry < bucket_entries; bucket_entry++) + { + found_obj_name = nv3_ramin_read32(ramht_cur_address, NULL); + ramht_cur_address += 0x04; + uint32_t obj_context = nv3_ramin_read32(ramht_cur_address, NULL); + ramht_cur_address += 0x04; + obj_context_struct = *(nv3_ramin_context_t*)&obj_context; + + // see if the object is in the right channel + if (found_obj_name == name + && obj_context_struct.channel_id == channel_id) + { + found_object = true; + break; + } + } + + if (!found_object) + { + if (!cache_num) + { + nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; + nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; + + //It turns itself off on failure, the drivers turn it back on + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + } + else + { + nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; + nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; + + //It turns itself off on failure, the drivers turn it back on + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; + } + + nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + + return false; + } + + // So we did find an object. + // Now try to read some of this... + + // Class ID is 5 bits in all other parts of the gpu but 7 bits here. A move in a direction that didn't pan out? + // Represented as 0x40-0x5f? Some other meaning + + // Perform more validation + + if (obj_context_struct.class_id > NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID + || obj_context_struct.class_id < NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID) + { + fatal("NV3: Invalid graphics object class ID name=0x%04x type=%04x, interpreted by pgraph as: %04x (Contact starfrost)", + name, obj_context_struct.class_id, obj_context_struct.class_id & 0x1F); + } + else if (obj_context_struct.channel_id > NV3_DMA_CHANNELS) + fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-8", obj_context_struct.channel_id); + + // Illegal accesses sent to RAMRO, so ignore here + // TODO: SEND THESE TO RAMRO!!!!! + + #ifndef RELEASE_BUILD + nv3_debug_ramin_print_context_info(name, obj_context_struct); + #endif + + // By definition we can't have a cache error by here so take it off + if (!cache_num) + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; + else + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; + + // Caches store all the subchannels for our current dma channel and basically get stale every context switch + // Also we have to check that a osftware object didn't end up in here... + + bool is_software = false; + if (!cache_num) + is_software = (nv3->pfifo.cache0_settings.context[subchannel_id] & 0x800000); + else + is_software = (nv3->pfifo.cache1_settings.context[subchannel_id] & 0x800000); + + // This isn't an error but it's sent as an interrupt so the drivers can sync + if (is_software) + { + // handle it as an error + if (!cache_num) + { + nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + } + else + { + nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; + } + + // It's an error but it isn't lol + nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + + } + else + { + // obviously turn off the "is software" if it's not + if (!cache_num) + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + else + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD; + } + + // Ok we found it. Lol + return true; + +} + +#ifndef RELEASE_BUILD +// Prints out some informaiton about the object +void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) +{ + nv_log("NV3: Found object:"); + nv_log("Name: 0x%04x", name); + + nv_log("Context:"); + nv_log("DMA Channel %d (0-7 valid)", context.channel_id); + nv_log("Class ID: as repreesnted in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)", context.class_id, + context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); + nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)", context.is_rendering); + nv_log("PRAMIN Offset 0x%08x", context.ramin_offset << 4); +} + +#endif \ 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 index 6568e6db4..fc0190c2e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -22,7 +22,7 @@ #include <86Box/86box.h> #include <86Box/device.h> #include <86Box/mem.h> -#include <86box/pci.h> +#include <86Box/pci.h> #include <86Box/rom.h> // DEPENDENT!!! #include <86Box/video.h> #include <86Box/nv/vid_nv.h> @@ -32,10 +32,14 @@ It is used to get the offset within RAMHT of a graphics object. */ -uint32_t nv3_ramht_hash(nv3_ramin_name_t name, uint32_t channel) +uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) { - uint32_t hash = (name.byte_high ^ name.byte_mid2 ^ name.byte_mid1 ^ name.byte_low ^ (uint8_t)channel); - nv_log("NV3: Generating RAMHT hash (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", name, channel); + // convert the name to an array of bytes + uint8_t* hash_bytes = (uint8_t*)name; + + // is this the right endianness? + uint32_t hash = (hash_bytes[0] ^ hash_bytes[1] ^ hash_bytes[2] ^ hash_bytes[3] ^ (uint8_t)channel); + nv_log("NV3: Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, name, channel); return hash; } From 3a77b631b453229f6feeb7e50087b508f2fb2766 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 22:30:46 +0000 Subject: [PATCH 076/274] Write the pullers and some graphics object skeletons. --- .../86box/nv/classes/vid_nv3_classes.h | 62 +++++++++++++- src/include/86box/nv/vid_nv3.h | 15 ++-- .../nv3/classes/nv3_class_001_beta_factor.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_002_rop.c | 23 ++++- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 23 ++++- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 23 ++++- .../nv3_class_005_clipping_rectangle.c | 22 ++++- .../nv/nv3/classes/nv3_class_006_pattern.c | 23 ++++- .../nv/nv3/classes/nv3_class_007_rectangle.c | 23 ++++- .../nv/nv3/classes/nv3_class_008_point.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_009_line.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 24 +++++- .../nv/nv3/classes/nv3_class_00b_triangle.c | 23 ++++- .../classes/nv3_class_00c_win95_gdi_text.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 23 ++++- .../nv3_class_00e_scaled_image_from_mem.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_010_blit.c | 23 ++++- .../nv/nv3/classes/nv3_class_011_image.c | 23 ++++- .../nv/nv3/classes/nv3_class_012_bitmap.c | 24 +++++- .../classes/nv3_class_014_transfer2memory.c | 24 +++++- .../nv3_class_015_stretched_image_from_cpu.c | 25 +++++- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 23 ++++- .../classes/nv3_class_018_point_zeta_buffer.c | 23 ++++- .../classes/nv3_class_01c_image_in_memory.c | 23 ++++- src/video/nv/nv3/classes/nv3_class_names.c | 1 - .../nv/nv3/classes/nv3_class_shared_methods.c | 35 ++++++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 85 +++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 5 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 16 ++-- 29 files changed, 683 insertions(+), 46 deletions(-) create mode 100644 src/video/nv/nv3/classes/nv3_class_shared_methods.c diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index afe3fe18d..b794e43b9 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -50,7 +50,7 @@ typedef struct nv3_class_ctx_switch_method_s uint32_t data; uint16_t instance; - uint8_t channel_id : 6; + uint8_t channel : 6; uint16_t reserved : 9; bool reset_if_volatile; // ???? } set_notify_ctx_dma; // Set notifier context for DMA (context switch) @@ -1085,5 +1085,63 @@ typedef struct nv3_object_class_01C uint8_t reserved3[0x1C3F]; } nv3_image_in_memory_t; +// See envytools. This is where we finally end up after this mess, it allows parameters to be passed to the methods. +typedef struct nv3_grobj_s +{ + uint32_t grobj_0; + uint32_t grobj_1; + uint32_t grobj_2; + uint32_t grobj_3; +} nv3_grobj_t; // TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! -#pragma pack(pop) // return packing to whatever it was before this disaster \ No newline at end of file +#pragma pack(pop) // return packing to whatever it was before this disaster + +// Class methods +void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_001_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_002_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_003_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_004_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_005_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_006_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_007_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_009_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_00c_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_00d_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_00e_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_010_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_011_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_012_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_014_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_015_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_017_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_018_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_class_01c_method(uint32_t method_id, nv3_grobj_t grobj); + +// This area is used for holding universal representations of the U* registers... +extern struct nv3_object_class_001 nv3_beta_factor; +extern struct nv3_object_class_002 nv3_rop; +extern struct nv3_object_class_003 nv3_chroma_key; +extern struct nv3_object_class_004 nv3_plane_mask; +extern struct nv3_object_class_005 nv3_clipping_rectangle; +extern struct nv3_object_class_006 nv3_pattern; +extern struct nv3_object_class_007 nv3_triangle; +extern struct nv3_object_class_008 nv3_point; +extern struct nv3_object_class_009 nv3_line; +extern struct nv3_object_class_00a nv3_lin; +extern struct nv3_object_class_00b nv3_triangle; +extern struct nv3_object_class_00c nv3_win95_gdi_text; +extern struct nv3_object_class_00d nv3_m2mf; +extern struct nv3_object_class_00e nv3_scaled_image_from_memory; +extern struct nv3_object_class_010 nv3_blit; +extern struct nv3_object_class_011 nv3_image; +extern struct nv3_object_class_012 nv3_bitmap; +extern struct nv3_object_class_014 nv3_transfer2memory; +extern struct nv3_object_class_015 nv3_stretched_image_from_cpu; +extern struct nv3_object_class_017 nv3_d3d5_tri; +extern struct nv3_object_class_018 nv3_point_zeta_buffer; +extern struct nv3_object_class_01c nv3_image_in_memory; \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 651693257..275671f4f 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -41,7 +41,7 @@ extern const device_config_t nv3_config[]; #define NV3_DMA_CHANNELS 8 #define NV3_DMA_SUBCHANNELS_PER_CHANNEL 8 -#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 10 // The amount by which we have to ration out the memory clock because it's not fast enough... +#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. #define NV3_LAST_VALID_GRAPHICS_OBJECT_ID 0x1F @@ -298,7 +298,7 @@ extern const device_config_t nv3_config[]; //todo: merge stuff #define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 #define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD 8 // 0=software, 1=hardware +#define NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD 8 // 0=software, 1=hardware #define NV3_PFIFO_CACHE1_PULLER_STATE1 0x3250 #define NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 @@ -792,7 +792,7 @@ typedef struct nv3_pfifo_cache_s bool access_enabled; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. - uint8_t channel_id; // The DMA channel ID of this cache. + uint8_t channel; // The DMA channel ID of this cache. uint32_t status; uint32_t puller_control; uint32_t control; @@ -821,7 +821,7 @@ typedef struct nv3_pfifo_cache_s typedef struct nv3_pfifo_cache_entry_s { - uint8_t subchannel_id : 3; + uint8_t subchannel : 3; uint16_t method : 11; // method id depending on class (offset from entry channel start in ramin) uint32_t data; // is this the context @@ -845,7 +845,7 @@ typedef struct nv3_pfifo_s nv3_pfifo_cache_t cache0_settings; nv3_pfifo_cache_t cache1_settings; - nv3_pfifo_cache_entry_t cache0_entries[1]; + nv3_pfifo_cache_entry_t cache0_entry; // It only has 1 entry nv3_pfifo_cache_entry_t cache1_entries[NV3_PFIFO_CACHE1_SIZE_MAX]; // ONLY 32 USED ON REVISION A/B CARDS @@ -1080,7 +1080,7 @@ typedef struct nv3_ramin_context_s struct { bool reserved : 1; - uint8_t channel_id : 7; + uint8_t channel : 7; bool is_rendering : 1; uint8_t class_id : 7; uint16_t ramin_offset; @@ -1238,6 +1238,9 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); / // RAMIN functions uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel); bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_id, uint32_t subchannel_id); +#ifndef RELEASE_BUILD +void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); +#endif uint32_t nv3_ramfc_read(uint32_t address); void nv3_ramfc_write(uint32_t address, uint32_t value); diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 2f9cdb066..b6b97ee3a 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_001 beta_factor; + +void nv3_class_001_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index e3256d699..747d4b017 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_002 nv3_rop; + +void nv3_class_002_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 188f5f94e..08f854529 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_003 nv3_chroma_key; + +void nv3_class_003_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 0797819d7..0388bc35a 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_004 nv3_plane_mask; + +void nv3_class_004_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index c675be6e8..01f395040 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -13,4 +13,24 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_005 nv3_clipping_rectangle; + +void nv3_class_005_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index d75307a24..282557b95 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_006 nv3_pattern; + +void nv3_class_006_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 513f19496..3d378df91 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_007 nv3_rectangle; + +void nv3_class_007_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 92561591a..55fae702c 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_008 nv3_point; + +void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index d10501558..cd0a8b768 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_009 nv3_line; + +void nv3_class_009_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 372a56e92..4f44d4e80 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -13,4 +13,26 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_00a nv3_lin; + +void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 1b8c23f03..4c668c275 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_00b nv3_triangle; + +void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 9c58cf250..d0116b910 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_00c nv3_win95_gdi_text; + +void nv3_class_00c_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 0b6c48cc1..a121abcde 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_00d nv3_m2mf; + +void nv3_class_00d_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index 6e90a552a..a3daae7fc 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_00e nv3_scaled_image_from_mem; + +void nv3_class_00e_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 5e6f19aff..2bcdc2234 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_010 nv3_blit; + +void nv3_class_010_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 3334b4185..9a2f73486 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_011 nv3_image; + +void nv3_class_011_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index b5c62f5f4..f2d49f6b0 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -13,4 +13,26 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_012 nv3_bitmap; + +void nv3_class_012_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index c4b97a98a..ada899cf6 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -13,4 +13,26 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_014 nv3_transfer2memory; + +void nv3_class_014_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 951d77bce..c0a4f492f 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -6,11 +6,32 @@ * * This file is part of the 86Box distribution. * - * NV3: Methods for class 0x15 (stretched image to memory) + * NV3: Methods for class 0x15 (stretched image from cpu to memory) * * * * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_014 nv3_stretched_image_from_cpu; + +void nv3_class_015_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 4e0463104..b45ef8968 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_017 nv3_d3d5_tri; + +void nv3_class_017_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 0440c39fe..23e09904f 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; + +void nv3_class_018_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index daea3e0bb..a0e454fd4 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -13,4 +13,25 @@ * Authors: Connor Hyde, I need a better email address ;^) * * Copyright 2024-2025 Connor Hyde - */ \ No newline at end of file + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +struct nv3_object_class_01c nv3_image_in_memory; + +void nv3_class_01c_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index 3604d147b..e05ddc1eb 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -64,5 +64,4 @@ const char* nv3_class_names[] = "NV3 INVALID class 0x1D", "NV3 INVALID class 0x1E", "NV3 INVALID class 0x1F", - }; diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c new file mode 100644 index 000000000..e336168c6 --- /dev/null +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -0,0 +1,35 @@ +/* + * 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: Methods shared across multiple classes + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> + +void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj) +{ + +} \ 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 index b9e7e4cdd..a83d055cf 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -152,10 +152,10 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.cache1_settings.access_enabled; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: - ret = nv3->pfifo.cache0_settings.channel_id; + ret = nv3->pfifo.cache0_settings.channel; break; case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: - ret = nv3->pfifo.cache1_settings.channel_id; + ret = nv3->pfifo.cache1_settings.channel; break; case NV3_PFIFO_CACHE0_STATUS: uint32_t ret = 0x00; @@ -351,10 +351,10 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv3->pfifo.cache1_settings.access_enabled = value; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: - nv3->pfifo.cache0_settings.channel_id = value; + nv3->pfifo.cache0_settings.channel = value; break; case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: - nv3->pfifo.cache1_settings.channel_id = value; + nv3->pfifo.cache1_settings.channel = value; break; // CACHE0_STATUS and CACHE1_STATUS are not writable case NV3_PFIFO_CACHE0_METHOD: @@ -441,7 +441,38 @@ void nv3_pfifo_cache0_pull() if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache0_settings.get_address) return; - // There is only one entry + // There is only one entry for cache0 + uint16_t current_channel = nv3->pfifo.cache0_settings.channel; + uint32_t current_subchannel = nv3->pfifo.cache0_entry.subchannel; + uint32_t current_name = nv3->pfifo.cache0_entry.data; + uint32_t current_method = nv3->pfifo.cache0_entry.method; + + // i.e. there is no method in cache0, so we have to find the object. + if (!current_method) + { + if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) + return; // interrupt was fired, and we went to ramro + } + + uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? + + // Tell the CPU if we found a software method + if (current_context & 0x800000) + { + nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + } + + // Is this needed? + nv3->pfifo.cache0_settings.get_address ^= 0x04; + + #ifndef RELEASE_BUILD + nv_log("NV3: ***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE0 PULLED ****** Contextual information below\n"); + + nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); + #endif + } void nv3_pfifo_cache1_push() @@ -458,6 +489,50 @@ void nv3_pfifo_cache1_pull() // Do nothing if there is nothing in cache1 to pull if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) return; + + // There is only one entry for cache0 + uint32_t get_address = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably + + uint16_t current_channel = nv3->pfifo.cache1_settings.channel; + uint32_t current_subchannel = nv3->pfifo.cache1_entries[get_address].subchannel; + uint32_t current_name = nv3->pfifo.cache1_entries[get_address].data; + uint32_t current_method = nv3->pfifo.cache1_entries[get_address].method; + + // i.e. there is no method in cache0, so we have to find the object. + if (!current_method) + { + if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) + return; // interrupt was fired, and we went to ramro + } + + uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? + + // Tell the CPU if we found a software method + if (current_context & 0x800000) + { + nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + } + + // start by incrementing + uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_address) + 1; + + if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# + next_get_address &= NV3_PFIFO_CACHE1_SIZE_REV_C; + else + next_get_address &= NV3_PFIFO_CACHE1_SIZE_REV_AB; + + // Is this needed? + nv3->pfifo.cache0_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; + + #ifndef RELEASE_BUILD + nv_log("NV3: ***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE1 PULLED ****** Contextual information below\n"); + + nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); + #endif + + //Todo: finish it } bool nv3_pfifo_cache1_is_free() diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 9c44105b9..f913e2917 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -49,16 +49,19 @@ void nv3_pramdac_init() } // Polls the pixel clock. -// This updates the 2D/3D engine PGRAPH void nv3_pramdac_pixel_clock_poll(double real_time) { // TODO: ???? } // Polls the memory clock. +// This updates the 2D/3D engine PGRAPH, PTIMER and more void nv3_pramdac_memory_clock_poll(double real_time) { nv3_ptimer_tick(real_time); + + nv3_pfifo_cache0_pull(); + nv3_pfifo_cache1_pull(); // TODO: UPDATE PGRAPH! } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index d9ab438d7..3730765ab 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -347,7 +347,7 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) } // THIS IS THE MOST IMPORTANT FUNCTION! -bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_id, uint32_t subchannel_id) +bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel, uint32_t subchannel) { // TODO: WRITE IT!!! // Set the number of entries to search based on the ramht size (2*(size+1)) @@ -358,7 +358,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_i uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; uint32_t ramht_cur_address = ramht_base; - nv_log("NV3: Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel_id, subchannel_id); + nv_log("NV3: Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel, subchannel); bool found_object = false; @@ -376,7 +376,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_i // see if the object is in the right channel if (found_obj_name == name - && obj_context_struct.channel_id == channel_id) + && obj_context_struct.channel == channel) { found_object = true; break; @@ -421,8 +421,8 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_i fatal("NV3: Invalid graphics object class ID name=0x%04x type=%04x, interpreted by pgraph as: %04x (Contact starfrost)", name, obj_context_struct.class_id, obj_context_struct.class_id & 0x1F); } - else if (obj_context_struct.channel_id > NV3_DMA_CHANNELS) - fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-8", obj_context_struct.channel_id); + else if (obj_context_struct.channel > NV3_DMA_CHANNELS) + fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-8", obj_context_struct.channel); // Illegal accesses sent to RAMRO, so ignore here // TODO: SEND THESE TO RAMRO!!!!! @@ -442,9 +442,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_i bool is_software = false; if (!cache_num) - is_software = (nv3->pfifo.cache0_settings.context[subchannel_id] & 0x800000); + is_software = (nv3->pfifo.cache0_settings.context[subchannel] & 0x800000); else - is_software = (nv3->pfifo.cache1_settings.context[subchannel_id] & 0x800000); + is_software = (nv3->pfifo.cache1_settings.context[subchannel] & 0x800000); // This isn't an error but it's sent as an interrupt so the drivers can sync if (is_software) @@ -487,7 +487,7 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte nv_log("Name: 0x%04x", name); nv_log("Context:"); - nv_log("DMA Channel %d (0-7 valid)", context.channel_id); + nv_log("DMA Channel %d (0-7 valid)", context.channel); nv_log("Class ID: as repreesnted in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)", context.class_id, context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)", context.is_rendering); From 3c92d67b891a369003662fd887192bf6ba145656 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 22:37:51 +0000 Subject: [PATCH 077/274] fix compile --- src/include/86box/nv/classes/vid_nv3_classes.h | 14 +++++++------- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- src/video/nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- .../nv/nv3/classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../classes/nv3_class_00e_scaled_image_from_mem.c | 2 +- .../nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv/nv3/classes/nv3_class_01c_image_in_memory.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 5 +++++ 9 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index b794e43b9..04fda9dbe 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1129,14 +1129,14 @@ extern struct nv3_object_class_003 nv3_chroma_key; extern struct nv3_object_class_004 nv3_plane_mask; extern struct nv3_object_class_005 nv3_clipping_rectangle; extern struct nv3_object_class_006 nv3_pattern; -extern struct nv3_object_class_007 nv3_triangle; +extern struct nv3_object_class_007 nv3_rectangle; extern struct nv3_object_class_008 nv3_point; extern struct nv3_object_class_009 nv3_line; -extern struct nv3_object_class_00a nv3_lin; -extern struct nv3_object_class_00b nv3_triangle; -extern struct nv3_object_class_00c nv3_win95_gdi_text; -extern struct nv3_object_class_00d nv3_m2mf; -extern struct nv3_object_class_00e nv3_scaled_image_from_memory; +extern struct nv3_object_class_00A nv3_lin; +extern struct nv3_object_class_00B nv3_triangle; +extern struct nv3_object_class_00C nv3_win95_gdi_text; +extern struct nv3_object_class_00D nv3_m2mf; +extern struct nv3_object_class_00E nv3_scaled_image_from_memory; extern struct nv3_object_class_010 nv3_blit; extern struct nv3_object_class_011 nv3_image; extern struct nv3_object_class_012 nv3_bitmap; @@ -1144,4 +1144,4 @@ extern struct nv3_object_class_014 nv3_transfer2memory; extern struct nv3_object_class_015 nv3_stretched_image_from_cpu; extern struct nv3_object_class_017 nv3_d3d5_tri; extern struct nv3_object_class_018 nv3_point_zeta_buffer; -extern struct nv3_object_class_01c nv3_image_in_memory; \ No newline at end of file +extern struct nv3_object_class_01C nv3_image_in_memory; \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 4f44d4e80..f9c23dc21 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -30,7 +30,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_00a nv3_lin; +struct nv3_object_class_00A nv3_lin; void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 4c668c275..a65b3d600 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_00b nv3_triangle; +struct nv3_object_class_00B nv3_triangle; void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index d0116b910..54c013783 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_00c nv3_win95_gdi_text; +struct nv3_object_class_00C nv3_win95_gdi_text; void nv3_class_00c_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index a121abcde..10ccb9bc3 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_00d nv3_m2mf; +struct nv3_object_class_00D nv3_m2mf; void nv3_class_00d_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index a3daae7fc..9a4e86dca 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_00e nv3_scaled_image_from_mem; +struct nv3_object_class_00E nv3_scaled_image_from_mem; void nv3_class_00e_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index c0a4f492f..24480624d 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_014 nv3_stretched_image_from_cpu; +struct nv3_object_class_015 nv3_stretched_image_from_cpu; void nv3_class_015_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index a0e454fd4..e6533627d 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -29,7 +29,7 @@ #include <86Box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_01c nv3_image_in_memory; +struct nv3_object_class_01C nv3_image_in_memory; void nv3_class_01c_method(uint32_t method_id, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index fbdf058ec..19e9f33b0 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -453,4 +453,9 @@ void nv3_pgraph_interrupt_valid(uint32_t num) void nv3_pgraph_vblank_start(svga_t* svga) { nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); +} + +void nv3_pgraph_submit() +{ + } \ No newline at end of file From 7909c5e1b9d3984cea94e9d86349500ef0b32ecf Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 23:05:00 +0000 Subject: [PATCH 078/274] Add PFIFO CONFIG_0, DELAY_0, RUNOUT_GET, RUNOUT_PUT --- src/include/86box/nv/vid_nv3.h | 7 ++-- src/video/nv/nv3/nv3_core.c | 5 ++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 44 ++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 275671f4f..2df06a68d 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -208,6 +208,7 @@ extern const device_config_t nv3_config[]; #define NV3_PBUS_END 0x1FFF #define NV3_PFIFO_START 0x2000 // FIFO for DMA Object Submission (uses hashtable to store the objects) +#define NV3_PFIFO_DELAY_0 0x2040 // PFIFO Config Register #define NV3_PFIFO_DEBUG_0 0x2080 // PFIFO Debug Register #define NV3_PFIFO_CACHE0_ERROR_PENDING 0 #define NV3_PFIFO_CACHE1_ERROR_PENDING 4 @@ -832,13 +833,15 @@ typedef struct nv3_pfifo_s { uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable + uint32_t dma_delay_retry; // DMA Delay/Retry uint32_t debug_0; // Cache Debug register + uint32_t config_0; uint32_t ramht_config; // RAMHT config uint32_t ramfc_config; // RAMFC config uint32_t ramro_config; // RAMRO config // Runout stuff - uint32_t runout_put; - uint32_t runout_get; + uint32_t runout_put; // 8:3 if RAMRO=512b, otherwise 12:3 + uint32_t runout_get; // 8:3 if RAMRO=512b, otherwise 12:3 // Cache stuff uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 08a51bca7..c6944ad28 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -946,7 +946,10 @@ void nv3_update_mappings() // void* nv3_init(const device_t *info) { - nv3->nvbase.log = log_open("NV3"); + if (device_get_config_int("nv_debug_fulllog")) + nv3->nvbase.log = log_open("NV3"); + else + nv3->nvbase.log = log_open_cyclic("NV3"); // Allows nv_log to be used for multiple nvidia devices nv_log_set_device(nv3->nvbase.log); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index a83d055cf..f399b919d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -35,7 +35,9 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_INTR, "PFIFO - Interrupt Status", NULL, NULL}, { NV3_PFIFO_INTR_EN, "PFIFO - Interrupt Enable", NULL, NULL,}, + { NV3_PFIFO_DELAY_0, "PFIFO - DMA Delay/Retry Register", NULL, NULL}, { NV3_PFIFO_DEBUG_0, "PFIFO - Debug 0", NULL, NULL, }, + { NV3_PFIFO_CONFIG_0, "PFIFO - Config 0", NULL, NULL, }, { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, @@ -64,8 +66,9 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA Translation Lookaside Buffer - Pagetable Base"}, { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA Status"}, { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA Status"}, - - + //Runout + { NV3_PFIFO_RUNOUT_GET, "PFIFO Runout Get Address [8:3 if 512b, otherwise 12:3]"}, + { NV3_PFIFO_RUNOUT_PUT, "PFIFO Runout Put Address [8:3 if 512b, otherwise 12:3]"}, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -119,10 +122,15 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_INTR_EN: ret = nv3->pfifo.interrupt_enable; break; + case NV3_PFIFO_DELAY_0: + ret = nv3->pfifo.dma_delay_retry; + break; // Debug case NV3_PFIFO_DEBUG_0: ret = nv3->pfifo.debug_0; break; + case NV3_PFIFO_CONFIG_0: + ret = nv3->pfifo.config_0; // Some of these may need to become functions. case NV3_PFIFO_CONFIG_RAMFC: ret = nv3->pfifo.ramfc_config; @@ -223,7 +231,12 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE1_DMA_TLB_TAG: ret = nv3->pfifo.cache1_settings.dma_tlb_tag; break; - + case NV3_PFIFO_RUNOUT_GET: + ret = nv3->pfifo.runout_get; + break; + case NV3_PFIFO_RUNOUT_PUT: + ret = nv3->pfifo.runout_put; + break; } } @@ -284,6 +297,13 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_INTR_EN: nv3->pbus.interrupt_enable = value & 0x00001111; break; + case NV3_PFIFO_DELAY_0: + nv3->pfifo.dma_delay_retry = value; + break; + case NV3_PFIFO_CONFIG_0: + nv3->pfifo.config_0 = value; + break; + case NV3_PFIFO_CONFIG_RAMHT: nv3->pfifo.ramht_config = value; // This code sucks a bit fix it later @@ -313,7 +333,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CONFIG_RAMRO: nv3->pfifo.ramro_config = value; - uint32_t new_size_ramro = ((value >> 16) & 0x01); + uint32_t new_size_ramro = ((value >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (new_size_ramro == 0) new_size_ramro = 0x200; @@ -390,6 +410,22 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CACHE1_DMA_TLB_TAG: nv3->pfifo.cache1_settings.dma_tlb_tag = value; break; + case NV3_PFIFO_RUNOUT_GET: + uint32_t size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + + if (size == 0) //512b + nv3->pfifo.runout_get = ((value & 0x3F) << 3); + else + nv3->pfifo.runout_get = ((value & 0x3FF) << 3); + break; + case NV3_PFIFO_RUNOUT_PUT: + uint32_t size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + + if (size == 0) //512b + nv3->pfifo.runout_put = ((value & 0x3F) << 3); + else + nv3->pfifo.runout_put = ((value & 0x3FF) << 3); + break; } } } From 5ebc5f550503359e651f749c7a90a6066e73cbe8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 23:43:00 +0000 Subject: [PATCH 079/274] NV_USER and the other half of submitting (to cache1) --- src/include/86box/nv/vid_nv3.h | 10 +- src/video/CMakeLists.txt | 9 +- src/video/nv/nv3/nv3_core_arbiter.c | 2 - src/video/nv/nv3/subsystems/nv3_pfifo.c | 95 ++++++++++++++++++- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 5 + src/video/nv/nv3/subsystems/nv3_user.c | 47 +++++++++ 6 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 src/video/nv/nv3/subsystems/nv3_user.c diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 2df06a68d..0b4813cd1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -254,6 +254,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_RUNOUT_GET 0x2420 #define NV3_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 +#define NV3_PFIFO_RUNOUT_RAMIN_ERR 28 + #define NV3_PFIFO_CACHE0_SIZE 1 // This is for software-injected notified only! #define NV3_PFIFO_CACHE1_SIZE_REV_AB 32 #define NV3_PFIFO_CACHE1_SIZE_REV_C 64 @@ -589,6 +591,8 @@ extern const device_config_t nv3_config[]; // easier name #define NV3_OBJECT_SUBMIT_START NV3_USER_START +#define NV3_OBJECT_SUBMIT_SUBCHANNEL 13 +#define NV3_OBJECT_SUBMIT_CHANNEL 16 #define NV3_OBJECT_SUBMIT_END NV3_USER_END // also PDFB (Debug Framebuffer?) @@ -850,12 +854,8 @@ typedef struct nv3_pfifo_s nv3_pfifo_cache_entry_t cache0_entry; // It only has 1 entry nv3_pfifo_cache_entry_t cache1_entries[NV3_PFIFO_CACHE1_SIZE_MAX]; // ONLY 32 USED ON REVISION A/B CARDS - - } nv3_pfifo_t; -// create_object(uint32_t type) here - // RAMDAC typedef struct nv3_pramdac_s { @@ -1328,7 +1328,7 @@ void nv3_pfifo_interrupt(uint32_t id, bool fire_now); // NV3 PFIFO - Caches void nv3_pfifo_cache0_push(); void nv3_pfifo_cache0_pull(); -void nv3_pfifo_cache1_push(); +void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val); void nv3_pfifo_cache1_pull(); uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 2135213fb..2a90957b2 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -31,8 +31,13 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.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 + nv/nv_base.c nv/nv_rivatimer.c - nv/nv3/nv3_core.c nv/nv3/nv3_core_config.c nv/nv3/nv3_core_arbiter.c + + nv/nv3/nv3_core.c + nv/nv3/nv3_core_config.c + nv/nv3/nv3_core_arbiter.c + nv/nv3/subsystems/nv3_pramdac.c nv/nv3/subsystems/nv3_pfifo.c nv/nv3/subsystems/nv3_pgraph.c @@ -44,8 +49,10 @@ add_library(vid OBJECT agpgart.c video.c vid_table.c vid_cga.c vid_cga_comp.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 + nv/nv3/subsystems/nv3_user.c nv/nv3/classes/nv3_class_names.c + nv/nv3/classes/nv3_class_shared_methods.c nv/nv3/classes/nv3_class_001_beta_factor.c nv/nv3/classes/nv3_class_002_rop.c nv/nv3/classes/nv3_class_003_chroma_key.c diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 2022625fd..e10382feb 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -200,5 +200,3 @@ 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) {}; \ 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 index f399b919d..1d9543fd9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -462,11 +462,13 @@ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) return val; } +// Submits graphics objects INTO cache0 void nv3_pfifo_cache0_push() { - + } +// Pulls graphics objects OUT of cache0 void nv3_pfifo_cache0_pull() { // Do nothing if PFIFO CACHE0 is disabled @@ -511,11 +513,98 @@ void nv3_pfifo_cache0_pull() } -void nv3_pfifo_cache1_push() +void nv3_pfifo_context_switch(uint32_t new_channel) { - + /* Send our contexts to RAMFC. Load the new ones from RAMFC. */ } +// NV_USER writes go here! +// Pushes graphics objects into cache1 +void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) +{ + bool oh_shit = false; // RAMRO needed + nv3_ramin_ramro_reason oh_shit_reason = 0x00; // It's all good for now + + // bit 23 of a ramin dword means it's a write... + uint32_t new_address = 0; + + uint32_t method_offset = (addr & 0x1FFC); // size of dma object is 0x2000 and some universal methods are implemented at this point, like free + + // Up to 128 per envytools? + uint32_t channel = (addr >> NV3_OBJECT_SUBMIT_CHANNEL) & 0x7F; + uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & 0x07; + + // first make sure there is even any cache available + if (!nv3->pfifo.cache1_settings.access_enabled) + { + oh_shit = true; + oh_shit_reason = nv3_runout_reason_no_cache_available; + } + + // Check if runout is full + if (nv3->pfifo.runout_get != nv3->pfifo.runout_put) + { + oh_shit = true; + oh_shit_reason = nv3_runout_reason_cache_ran_out; // ? really ? I guess this means we already ran out.. + new_address |= (nv3_runout_reason_cache_ran_out << NV3_PFIFO_RUNOUT_RAMIN_ERR); + } + + if (!nv3_pfifo_cache1_is_free()) + { + oh_shit = true; + oh_shit_reason = nv3_runout_reason_free_count_overrun; + new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); + } + + if (method_offset > 0 && method_offset <= 0x100) + { + // Reserved NVIDIA Objects + oh_shit = true; + oh_shit_reason = nv3_runout_reason_reserved_access; + new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); + + } + + // Now check for context switching + + if (channel != nv3->pfifo.cache1_settings.channel) + { + if (!nv3->pfifo.cache_reassignment + || (nv3->pfifo.cache0_settings.get_address != nv3->pfifo.cache0_settings.get_address)) + { + oh_shit = true; + oh_shit_reason = nv3_runout_reason_no_cache_available; + new_address |= (nv3_runout_reason_no_cache_available << NV3_PFIFO_RUNOUT_RAMIN_ERR); + } + } + + // Did we fuck up? + if (oh_shit) + { + nv_log("NV3: WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); + return; + } + + // We didn't. Let's put it in CACHE1 + uint32_t current_put_address = nv3->pfifo.cache1_settings.put_address >> 2; + nv3->pfifo.cache1_entries[current_put_address].subchannel = subchannel; + nv3->pfifo.cache1_entries[current_put_address].method = method_offset; + nv3->pfifo.cache1_entries[current_put_address].data = val; + + // now we have to recalculate the cache1 put address + uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address); + + if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# + next_put_address &= NV3_PFIFO_CACHE1_SIZE_REV_C; + else + next_put_address &= NV3_PFIFO_CACHE1_SIZE_REV_AB; + + nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address); + + // Now we're done. Phew! +} + +// Pulls graphics objects OUT of cache1 void nv3_pfifo_cache1_pull() { // Do nothing if PFIFO CACHE1 is disabled diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index b7d8f4f57..a206343c9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -36,4 +36,9 @@ uint32_t nv3_ramro_read(uint32_t address) void nv3_ramro_write(uint32_t address, uint32_t value) { nv_log("NV3: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)\n", value, address); +} + +void nv3_ramro_send() +{ + } \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c new file mode 100644 index 000000000..eee994208 --- /dev/null +++ b/src/video/nv/nv3/subsystems/nv3_user.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. + * + * NV3 User Submission Area (NV_USER, conceptually considered "Cache1 Pusher") + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + +#include +#include +#include +#include +#include <86Box/86box.h> +#include <86Box/device.h> +#include <86Box/mem.h> +#include <86box/pci.h> +#include <86Box/rom.h> // DEPENDENT!!! +#include <86Box/video.h> +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv3.h> + +// 128 channels conceptually supported - a hangover from nv1 where multiple windows all directly programming the gpu were supported? total lunacy. +uint32_t nv3_user_read(uint32_t address) +{ + uint8_t method_offset = (address & 0x1FFC); + + nv_log("NV3: User Submission Area method_offset=0x%04x OH SHIT!! NOT IMPLEMENTED!!!", method_offset); + + // 0x10 is free CACHE1 object, other stuff? + return 0x00; +}; + +// Although NV3 doesn't have DMA mode unlike NV4 and later, it's conceptually similar to a "pusher" that pushes graphics commands that you write into CACHE1 that are then pulled out. +// So we send the writes here. This might do other stuff, so we keep this function +void nv3_user_write(uint32_t address, uint32_t value) +{ + nv3_pfifo_cache1_push(address, value); +} \ No newline at end of file From 1f1b88a119cc3e8352b074c1a5931839badf85ad Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 23:43:52 +0000 Subject: [PATCH 080/274] Whoops, fix the compile --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1d9543fd9..138f5f4bd 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -411,17 +411,17 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv3->pfifo.cache1_settings.dma_tlb_tag = value; break; case NV3_PFIFO_RUNOUT_GET: - uint32_t size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + uint32_t size_get = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - if (size == 0) //512b + if (size_get == 0) //512b nv3->pfifo.runout_get = ((value & 0x3F) << 3); else nv3->pfifo.runout_get = ((value & 0x3FF) << 3); break; case NV3_PFIFO_RUNOUT_PUT: - uint32_t size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + uint32_t size_put = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - if (size == 0) //512b + if (size_put == 0) //512b nv3->pfifo.runout_put = ((value & 0x3F) << 3); else nv3->pfifo.runout_put = ((value & 0x3FF) << 3); From 782a4a058b5c708add64ef76c1b5a9e83ff5aaa7 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 23:44:21 +0000 Subject: [PATCH 081/274] actually call the context switch function even if it doesn't exist yet --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 138f5f4bd..1508bc092 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -576,6 +576,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) oh_shit_reason = nv3_runout_reason_no_cache_available; new_address |= (nv3_runout_reason_no_cache_available << NV3_PFIFO_RUNOUT_RAMIN_ERR); } + + nv3_pfifo_context_switch(channel); } // Did we fuck up? From 4e001ae17167b671138024bb3ee1407d21850e12 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 1 Feb 2025 23:44:39 +0000 Subject: [PATCH 082/274] indicate the right error --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1508bc092..6c98cf85f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -561,7 +561,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) // Reserved NVIDIA Objects oh_shit = true; oh_shit_reason = nv3_runout_reason_reserved_access; - new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); + new_address |= (nv3_runout_reason_reserved_access << NV3_PFIFO_RUNOUT_RAMIN_ERR); } From f5687370831c83ae6bd8aac05c8e79102a2dff35 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 2 Feb 2025 00:43:27 +0000 Subject: [PATCH 083/274] various logging/pfifo fixes, add pfifo ctx --- .../nv3_object_classes_driver.txt | 57 ------------------- src/include/86box/nv/vid_nv3.h | 16 ++++-- src/video/nv/nv3/subsystems/nv3_pbus.c | 2 +- src/video/nv/nv3/subsystems/nv3_pextdev.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 49 +++++++++++++--- src/video/nv/nv3/subsystems/nv3_pgraph.c | 6 +- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- src/video/nv/nv3/subsystems/nv3_pme.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 2 +- src/video/nv/nv3/subsystems/nv3_ptimer.c | 9 +-- src/video/nv/nv3/subsystems/nv3_pvideo.c | 2 +- 12 files changed, 66 insertions(+), 85 deletions(-) delete mode 100644 doc/nvidia_notes/nv3_object_classes_driver.txt diff --git a/doc/nvidia_notes/nv3_object_classes_driver.txt b/doc/nvidia_notes/nv3_object_classes_driver.txt deleted file mode 100644 index 3e4955a80..000000000 --- a/doc/nvidia_notes/nv3_object_classes_driver.txt +++ /dev/null @@ -1,57 +0,0 @@ -Object classes - -NOT represented by the GPU. These are the ones the drivers recognise. Most of them are faked. - -Version 0.77_win95 (!!!) - -object class 1 = "general" methods shared across all objects -object class 2 = DMA from memory -object class 3 = DMA to memory -object class 4 = timer -object class 5 = chip ID -object class 6 = "context ordinal" -object class 10/0Ah = patchcord (links objects together) -object class 11/0Bh = video sink -object class 12/0Ch = video switch -object class 13/0Dh = video colormap -object class 14/0Eh = patchcord...again -object class 15/0Fh = image to video -object class 16/10h = image stencil buffer -object class 17/11h = image blending methods -object class 18/12h = beta solid -object class 19/13h = image -> render operation -object class 20/14h = ROP5 (2^5 = 32bit rop) -object class 21/15h = color key -object class 22/16h = plane switch -object class 23/17h = "solid image" -object class 24/18h = pattern -object class 25/19h = black rectangle -object class 26/1Ah = solid point -object class 27/1Bh = solid line -object class 28/1Ch = solid lin (line without its ending or starting pixel) -object class 29/1Dh = triangle -object class 30/1Eh = solid rectangle -object class 31/1Fh = blit image -object class 33/21h = get image from cpu -object class 34/22h = get image from cpu, monochrome -object class 37/25h = get image from local GPU memory -object class 49/31h and 51/33h = patchcord, again -object class 54/36h = get image from cpu, stretched -object class 55/37h = get image from vram, scaled -object class 56/38h = get image from vram, scaled, YUV 420 color space (probably internal colour space used for rendering) -object class 57/39h = convert image(?) between formats in place in vram, allows you to set an in and out buffer and all that jazz -object class 61/3Dh = In-Memory DMA (vram to vram?) -object class 62/3Eh = get image from vram -object class 63/3Fh = get video from vram -object class 64/40h = video scaler -object class 65/41h = video color key (as opposed to image) -object class 66/42h = capture video to memory -object class 67/43h = Solid ROP5 -object class 68/44h = zeta buffer (combined z and stencil buffer) from CPU(?) -object class 69/45h = zeta buffer in VRAM -object class 70/46h = zeta buffer patchcord -object class 71/47h = render solid point into zeta buffer (Also rectangle0 -object class 72/48h = render Direct3D 5.x ("D3D0") accelerated triangle into zeta buffer -object class 73/49h = render Direct3D 5.x ("D3D0") accelerated triangle -object class 74/4Ah = video source -object class 75/4Bh = render Windows GDI accelerated rectangle and text \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 0b4813cd1..a00699a62 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 30 January 2025 (STILL WORKING ON IT!!!) + * Last updated: 2 February 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -272,11 +272,14 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD 8 +#define NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD 8 #define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY 0x3050 #define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY_BOOL 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 #define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit +// Current channel context - cache1 +#define NV3_PFIFO_CACHE0_CTX 0x3080 + #define NV3_PFIFO_CACHE0_METHOD 0x3100 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 @@ -306,6 +309,11 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 #define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 + +// Current channel context - cache1 +#define NV3_PFIFO_CACHE1_CTX_START 0x3280 +#define NV3_PFIFO_CACHE1_CTX_END 0x32F0 + #define NV3_PFIFO_CACHE1_METHOD 0x3300 #define NV3_PFIFO_CACHE1_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 // 15:13 @@ -797,11 +805,11 @@ typedef struct nv3_pfifo_cache_s bool access_enabled; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. - uint8_t channel; // The DMA channel ID of this cache. + uint8_t channel; // The DMA channel ID of this cache. uint32_t status; uint32_t puller_control; uint32_t control; - uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; + uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; // Only one of these exists for cache0 /* cache1 only do we even need to emulate this? diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 61385b8f2..ffe2c57cf 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -75,7 +75,7 @@ uint32_t nv3_pbus_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 2fbbd3489..3e0d63b5e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -113,7 +113,7 @@ uint32_t nv3_pextdev_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 4de4d5069..510c61e6a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -90,7 +90,7 @@ uint32_t nv3_pfb_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 6c98cf85f..1e52175c5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -41,6 +41,7 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, + { NV3_PFIFO_CACHE_REASSIGNMENT, "PFIFO - Allow Cache Channel Reassignment", NULL, NULL }, { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller Control", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller Control"}, { NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, @@ -231,21 +232,34 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE1_DMA_TLB_TAG: ret = nv3->pfifo.cache1_settings.dma_tlb_tag; break; + // Runout case NV3_PFIFO_RUNOUT_GET: ret = nv3->pfifo.runout_get; break; case NV3_PFIFO_RUNOUT_PUT: ret = nv3->pfifo.runout_put; break; + /* Cache1 is handled below */ + case NV3_PFIFO_CACHE0_CTX: + ret = nv3->pfifo.cache0_settings.context[0]; + break; } } if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } + /* Handle some special memory areas */ + else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) + { + uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; + ret = nv3->pfifo.cache1_settings.context[ctx_entry_id]; + + nv_log("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x", ctx_entry_id, ret); + } else { nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); @@ -267,16 +281,11 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) 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); + nv_log("NV3: PFIFO 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); @@ -293,9 +302,15 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_INTR: nv3->pfifo.interrupt_status &= ~value; nv3_pmc_clear_interrupts(); + + // update the internal cache error state + if (!nv3->pfifo.interrupt_status & NV3_PFIFO_INTR_CACHE_ERROR) + nv3->pfifo.debug_0 &= ~NV3_PFIFO_INTR_CACHE_ERROR; + break; case NV3_PFIFO_INTR_EN: - nv3->pbus.interrupt_enable = value & 0x00001111; + nv3->pfifo.interrupt_enable = value & 0x00001111; + nv3_pmc_handle_interrupts(true); break; case NV3_PFIFO_DELAY_0: nv3->pfifo.dma_delay_retry = value; @@ -426,10 +441,26 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) else nv3->pfifo.runout_put = ((value & 0x3FF) << 3); break; + /* Cache1 is handled below */ + case NV3_PFIFO_CACHE0_CTX: + nv3->pfifo.cache0_settings.context[0] = value; + break; } } - } + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + } + /* Handle some special memory areas */ + else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) + { + uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; + nv3->pfifo.cache1_settings.context[ctx_entry_id] = value; + + nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x", ctx_entry_id, value); + } } /* diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 19e9f33b0..8782abd8a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -140,9 +140,11 @@ uint32_t nv3_pgraph_read(uint32_t address) break; case NV3_PGRAPH_INTR_EN_0: ret = nv3->pgraph.interrupt_enable_0; + nv3_pmc_handle_interrupts(true); break; case NV3_PGRAPH_INTR_EN_1: ret = nv3->pgraph.interrupt_enable_1; + nv3_pmc_handle_interrupts(true); break; // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like // during the driver initialisation process @@ -243,7 +245,7 @@ uint32_t nv3_pgraph_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } @@ -457,5 +459,5 @@ void nv3_pgraph_vblank_start(svga_t* svga) void nv3_pgraph_submit() { - + } \ 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 index 7313a7554..4d8ef7c4f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -211,7 +211,7 @@ uint32_t nv3_pmc_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index 2ba4af426..d059eb05f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -77,7 +77,7 @@ uint32_t nv3_pme_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index f913e2917..c87a7e14e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -285,7 +285,7 @@ uint32_t nv3_pramdac_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 938389054..30dd96f23 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -76,14 +76,11 @@ void nv3_ptimer_tick(double real_time) double current_time = freq_base * ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? - // Logging is suppressed when reading this register because it is read many times - // So we only log when we update. - // truncate it nv3->ptimer.time += (uint64_t)current_time; - - nv_log("PTIMER time ticked (The value is now 0x%08x)\n", nv3->ptimer.time); + // Only log on ptimer alarm. Otherwise, it's too much spam. + //nv_log("PTIMER time ticked (The value is now 0x%08x)\n", nv3->ptimer.time); // Check if the alarm has actually triggered... if (nv3->ptimer.time >= nv3->ptimer.alarm) @@ -153,7 +150,7 @@ uint32_t nv3_ptimer_read(uint32_t address) && reg->address != NV3_PTIMER_TIME_1_NSEC) { if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index dc8651fb8..dacf984c9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -82,7 +82,7 @@ uint32_t nv3_pvideo_read(uint32_t address) } if (reg->friendly_name) - nv_log(": %s (value = 0x%08x)\n", reg->friendly_name, ret); + nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); else nv_log("\n"); } From 908ae99cd8551b971641838b4e9abc228231d21e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 2 Feb 2025 01:39:17 +0000 Subject: [PATCH 084/274] fix puller_control --- src/include/86box/nv/vid_nv3.h | 1 - src/video/nv/nv3/subsystems/nv3_pfifo.c | 9 ++++----- src/video/nv/nv3/subsystems/nv3_pgraph.c | 5 ++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a00699a62..3d4bd9b0a 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -808,7 +808,6 @@ typedef struct nv3_pfifo_cache_s uint8_t channel; // The DMA channel ID of this cache. uint32_t status; uint32_t puller_control; - uint32_t control; uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; // Only one of these exists for cache0 /* cache1 only diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1e52175c5..b788f0e94 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -143,10 +143,10 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.ramro_config; break; case NV3_PFIFO_CACHE0_PULLER_CONTROL: - ret = nv3->pfifo.cache0_settings.control; + ret = nv3->pfifo.cache0_settings.puller_control; break; case NV3_PFIFO_CACHE1_PULLER_CONTROL: - ret = nv3->pfifo.cache1_settings.control; + ret = nv3->pfifo.cache1_settings.puller_control; break; case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: ret = nv3->pfifo.cache0_settings.context_is_dirty; @@ -368,10 +368,10 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) break; // Control case NV3_PFIFO_CACHE0_PULLER_CONTROL: - nv3->pfifo.cache0_settings.control = value; // 8bits meaningful + nv3->pfifo.cache0_settings.puller_control = value; // 8bits meaningful break; case NV3_PFIFO_CACHE1_PULLER_CONTROL: - nv3->pfifo.cache1_settings.control = value; // 8bits meaningful + nv3->pfifo.cache1_settings.puller_control = value; // 8bits meaningful break; case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: nv3->pfifo.cache0_settings.context_is_dirty = value; @@ -648,7 +648,6 @@ void nv3_pfifo_cache1_pull() if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) return; - // There is only one entry for cache0 uint32_t get_address = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably uint16_t current_channel = nv3->pfifo.cache1_settings.channel; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 8782abd8a..7d294d820 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -134,9 +134,11 @@ uint32_t nv3_pgraph_read(uint32_t address) //interrupt status and enable regs case NV3_PGRAPH_INTR_0: ret = nv3->pgraph.interrupt_status_0; + nv3_pmc_clear_interrupts(); break; case NV3_PGRAPH_INTR_1: ret = nv3->pgraph.interrupt_status_1; + nv3_pmc_clear_interrupts(); break; case NV3_PGRAPH_INTR_EN_0: ret = nv3->pgraph.interrupt_enable_0; @@ -323,7 +325,8 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // 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; + value |= (1 << NV3_PGRAPH_INTR_EN_0_VBLANK); + nv3->pgraph.interrupt_enable_0 = value & 0x11111111; nv3_pmc_handle_interrupts(true); break; case NV3_PGRAPH_INTR_EN_1: From aa8a5a6d0e90d751cd305812cde3d554252b3de0 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 5 Feb 2025 01:28:06 +0000 Subject: [PATCH 085/274] IT ATTEMPTS TO RENDER THE DESKTOP HOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOLY SHIT IT ONLY TOOK ME 6 MONTHS --- .../nv3 driver status_CURRENT.txt | 4 ++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 44 +++++++++++++++---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 - 3 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 doc/nvidia_notes/nv3 driver status_CURRENT.txt diff --git a/doc/nvidia_notes/nv3 driver status_CURRENT.txt b/doc/nvidia_notes/nv3 driver status_CURRENT.txt new file mode 100644 index 000000000..f284f7457 --- /dev/null +++ b/doc/nvidia_notes/nv3 driver status_CURRENT.txt @@ -0,0 +1,4 @@ +Failure condition: + Failed check, causes fifoService call in fifo service init in RM, FifoState STATE_LOAD + + CHECK NVSTARTIO! DEBUG TOMORROW \ 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 index b788f0e94..17d1f464b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -70,6 +70,7 @@ nv_register_t pfifo_registers[] = { //Runout { NV3_PFIFO_RUNOUT_GET, "PFIFO Runout Get Address [8:3 if 512b, otherwise 12:3]"}, { NV3_PFIFO_RUNOUT_PUT, "PFIFO Runout Put Address [8:3 if 512b, otherwise 12:3]"}, + { NV3_PFIFO_RUNOUT_STATUS, "PFIFO Runout Status"}, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -132,6 +133,7 @@ uint32_t nv3_pfifo_read(uint32_t address) break; case NV3_PFIFO_CONFIG_0: ret = nv3->pfifo.config_0; + break; // Some of these may need to become functions. case NV3_PFIFO_CONFIG_RAMFC: ret = nv3->pfifo.ramfc_config; @@ -166,9 +168,7 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: ret = nv3->pfifo.cache1_settings.channel; break; - case NV3_PFIFO_CACHE0_STATUS: - uint32_t ret = 0x00; - + case NV3_PFIFO_CACHE0_STATUS: // CACHE0 has only one entry so it can only ever be empty or full if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache1_settings.get_address) @@ -178,6 +178,8 @@ uint32_t nv3_pfifo_read(uint32_t address) break; case NV3_PFIFO_CACHE1_STATUS: + // CACHE1 doesn't... + if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) ret |= 1 << NV3_PFIFO_CACHE1_STATUS_EMPTY; @@ -186,10 +188,9 @@ uint32_t nv3_pfifo_read(uint32_t address) if (!nv3_pfifo_cache1_is_free()) ret |= 1 << NV3_PFIFO_CACHE1_STATUS_FULL; - if (nv3->pfifo.runout_put == nv3->pfifo.runout_get) + if (nv3->pfifo.runout_put != nv3->pfifo.runout_get) ret |= 1 << NV3_PFIFO_CACHE1_STATUS_RANOUT; - ret = nv3->pfifo.cache1_settings.status; break; case NV3_PFIFO_CACHE0_METHOD: ret = ((nv3->pfifo.cache0_settings.method_subchannel << 13) & 0x07) @@ -212,7 +213,7 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.cache1_settings.dma_state; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_1: - ret = nv3->pfifo.cache1_settings.dma_length; + ret = nv3->pfifo.cache1_settings.dma_length & (VRAM_SIZE_8MB) - 4; //MAX vram size break; case NV3_PFIFO_CACHE1_DMA_CONFIG_2: ret = nv3->pfifo.cache1_settings.dma_address; @@ -239,6 +240,27 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_RUNOUT_PUT: ret = nv3->pfifo.runout_put; break; + case NV3_PFIFO_RUNOUT_STATUS: + if (nv3->pfifo.runout_put == nv3->pfifo.runout_get) + ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_EMPTY; /* good news */ + else + ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_RANOUT; /* bad news */ + + /* TODO: the following code sucks (move to a functio?) */ + + uint32_t new_size_ramro = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + + if (new_size_ramro == 0) + new_size_ramro = 0x200; + else if (new_size_ramro == 1) + new_size_ramro = 0x2000; + + // WTF? + if (nv3->pfifo.runout_put + 0x08 & (new_size_ramro - 0x08) == nv3->pfifo.runout_get) + ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_FULL; /* VERY BAD news */ + + break; + /* Cache1 is handled below */ case NV3_PFIFO_CACHE0_CTX: ret = nv3->pfifo.cache0_settings.context[0]; @@ -306,10 +328,9 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) // update the internal cache error state if (!nv3->pfifo.interrupt_status & NV3_PFIFO_INTR_CACHE_ERROR) nv3->pfifo.debug_0 &= ~NV3_PFIFO_INTR_CACHE_ERROR; - break; case NV3_PFIFO_INTR_EN: - nv3->pfifo.interrupt_enable = value & 0x00001111; + nv3->pfifo.interrupt_enable = value & 0x00011111; nv3_pmc_handle_interrupts(true); break; case NV3_PFIFO_DELAY_0: @@ -395,7 +416,6 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CACHE0_METHOD: nv3->pfifo.cache0_settings.method_subchannel = (value >> 13) & 0x07; nv3->pfifo.cache0_settings.method_address = (value >> 2) & 0x7FF; - break; case NV3_PFIFO_CACHE1_METHOD: nv3->pfifo.cache1_settings.method_subchannel = (value >> 13) & 0x07; @@ -547,6 +567,11 @@ void nv3_pfifo_cache0_pull() void nv3_pfifo_context_switch(uint32_t new_channel) { /* Send our contexts to RAMFC. Load the new ones from RAMFC. */ + if (new_channel >= NV3_DMA_CHANNELS) + fatal("Tried to switch to invalid dma channel"); + + uint16_t ramfc_base = nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS & 0xF; + } // NV_USER writes go here! @@ -600,6 +625,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) if (channel != nv3->pfifo.cache1_settings.channel) { + // Cache reassignment required if (!nv3->pfifo.cache_reassignment || (nv3->pfifo.cache0_settings.get_address != nv3->pfifo.cache0_settings.get_address)) { diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 7d294d820..9835a788d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -325,14 +325,12 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // Only bits divisible by 4 matter // and only bit0-16 is defined in intr_1 case NV3_PGRAPH_INTR_EN_0: - value |= (1 << NV3_PGRAPH_INTR_EN_0_VBLANK); nv3->pgraph.interrupt_enable_0 = value & 0x11111111; nv3_pmc_handle_interrupts(true); break; case NV3_PGRAPH_INTR_EN_1: nv3->pgraph.interrupt_enable_1 = value & 0x00011111; nv3_pmc_handle_interrupts(true); - break; // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like // during the driver initialisation process From acd0dc40bb1e6b68fa270dd49bbcdfe93ff2693e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 5 Feb 2025 18:46:38 +0000 Subject: [PATCH 086/274] Implement pixel mask registers. --- src/include/86box/nv/vid_nv3.h | 2 ++ src/video/nv/nv3/nv3_core.c | 3 ++- src/video/nv/nv3/subsystems/nv3_pfb.c | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 3d4bd9b0a..5bc967911 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -593,6 +593,8 @@ extern const device_config_t nv3_config[]; #define NV3_PRAMDAC_END 0x680FFF #define NV3_PDAC_END 0x680FFF // OPTIONAL external DAC +#define NV3_VGA_DAC_START 0x6813C6 +#define NV3_VGA_DAC_END 0x6813C9 #define NV3_USER_START 0x800000 // Mapping for the area where objects are submitted into the FIFO (up to 0x880000?) #define NV3_USER_END 0xFFFFFF diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index c6944ad28..c185af738 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -46,7 +46,8 @@ bool nv3_is_svga_redirect_address(uint32_t addr) || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_COLOR || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_MONO - || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO); + || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO + || (addr >= NV3_VGA_DAC_START && addr <= NV3_VGA_DAC_END)); } // All MMIO regs are 32-bit i believe internally diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 510c61e6a..ba42c1734 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -144,10 +144,15 @@ void nv3_pfb_config0_write(uint32_t val) // so we don't update things here for now uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; + // i don't think 16:9 is supported + uint32_t new_pfb_vtotal = new_pfb_htotal * (4/3); + uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; nv_log("NV3: Framebuffer Config Change\n"); nv_log("NV3: Horizontal Size=%d pixels\n", new_pfb_htotal); + nv_log("NV3: Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); + if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) nv_log("NV3: Bit Depth=8bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_16BPP) From f3c90b3ef2baf12b95cde32771ba99d3b431308b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 5 Feb 2025 20:01:21 +0000 Subject: [PATCH 087/274] Fix PTIMER speed, which fixes 1 minute of being frozen on the win2k bootscreen --- src/video/nv/nv3/subsystems/nv3_ptimer.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 30dd96f23..b182644ec 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -59,22 +59,21 @@ void nv3_ptimer_interrupt(uint32_t num) // Ticks the timer. void nv3_ptimer_tick(double real_time) { - // do not divide by zero + // prevent a divide by zero if (nv3->ptimer.clock_numerator == 0 || nv3->ptimer.clock_denominator == 0) return; // get the current time - // Due to timer system limitations, the timer system is not capable of running at 100 megahertz. Therefore, we have to scale it down and then scale up the level of changes - // to the state. // See Envytools. We need to use the frequency as a source. - // But we need to figure out how many cycles actually occurred because this counts up every cycle... + // We need to figure out how many cycles actually occurred because this counts up every cycle... + // However it seems that their formula is wrong. I can't be bothered to figure out what's going on and, based on documentation from NVIDIA, + // timer_0 is meant to roll over every 4 seconds. Multiplying by 10 basically does the job. // Convert to microseconds - double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv3->nvbase.memory_clock_frequency); - - double current_time = freq_base * ((double)nv3->ptimer.clock_numerator * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)nv3->ptimer.clock_denominator; // *10.0? + double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv3->nvbase.memory_clock_frequency) * 10.0f; + double current_time = freq_base * ((double)nv3->ptimer.clock_numerator) / (double)nv3->ptimer.clock_denominator; // *10.0? // truncate it nv3->ptimer.time += (uint64_t)current_time; From d5393a0575a1792410fc15f8828332eec7d3a037 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 5 Feb 2025 21:40:41 +0000 Subject: [PATCH 088/274] Now that the log is device specific, let's not log it twice. --- .../86box/nv/classes/vid_nv3_classes.h | 3 ++ src/include/86box/nv/vid_nv3.h | 2 +- src/video/nv/nv3/nv3_core.c | 54 +++++++++---------- src/video/nv/nv3/nv3_core_arbiter.c | 4 +- src/video/nv/nv3/subsystems/nv3_pbus.c | 10 ++-- src/video/nv/nv3/subsystems/nv3_pextdev.c | 16 +++--- src/video/nv/nv3/subsystems/nv3_pfb.c | 18 +++---- src/video/nv/nv3/subsystems/nv3_pfifo.c | 22 ++++---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 14 ++--- src/video/nv/nv3/subsystems/nv3_pmc.c | 18 +++---- src/video/nv/nv3/subsystems/nv3_pme.c | 6 +-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 12 ++--- src/video/nv/nv3/subsystems/nv3_pramin.c | 16 +++--- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 4 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 6 +-- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 4 +- src/video/nv/nv3/subsystems/nv3_ptimer.c | 6 +-- src/video/nv/nv3/subsystems/nv3_pvideo.c | 4 +- src/video/nv/nv3/subsystems/nv3_user.c | 15 +++++- 19 files changed, 124 insertions(+), 110 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 04fda9dbe..36bac6e66 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1096,6 +1096,9 @@ typedef struct nv3_grobj_s // TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! #pragma pack(pop) // return packing to whatever it was before this disaster +// Method IDs +#define NV3_GENERIC_METHOD_IS_PFIFO_FREE 0x0010 + // Class methods void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_001_method(uint32_t method_id, nv3_grobj_t grobj); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 5bc967911..7602361fc 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 2 February 2025 (STILL WORKING ON IT!!!) + * Last updated: 5 February 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index c185af738..ff844c02d 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -68,7 +68,7 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3); - nv_log("NV3: Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x\n", addr, ret); + nv_log("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x\n", addr, ret); return ret; } @@ -94,7 +94,7 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); - nv_log("NV3: Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + nv_log("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); return ret; } @@ -121,7 +121,7 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - nv_log("NV3: Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x\n", addr, ret); + nv_log("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x\n", addr, ret); return ret; } @@ -142,7 +142,7 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + nv_log("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); return; @@ -168,7 +168,7 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x\n", addr, val); + nv_log("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -195,7 +195,7 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("NV3: Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x\n", addr, val); + nv_log("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -360,7 +360,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) } - nv_log("NV3: nv3_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); + nv_log("nv3_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); return ret; } @@ -377,7 +377,7 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) && 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); + nv_log("nv3_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); nv3->pci_config.pci_regs[addr] = val; @@ -746,13 +746,13 @@ uint8_t nv3_prom_read(uint32_t address) // Does this mirror on real hardware? if (rom_address >= real_rom_size) { - nv_log("NV3: PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); + nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); return 0xFF; } else { uint8_t val = nv3->nvbase.vbios.rom[rom_address]; - nv_log("NV3: PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); + nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); return val; } } @@ -760,13 +760,13 @@ uint8_t nv3_prom_read(uint32_t address) void nv3_prom_write(uint32_t address, uint32_t value) { uint32_t real_addr = address & 0x1FFFF; - nv_log("NV3: What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); } // Initialise the MMIO mappings void nv3_init_mappings_mmio() { - nv_log("NV3: Initialising 32MB MMIO area\n"); + nv_log("Initialising 32MB MMIO area\n"); // 0x0 - 1000000: regs // 0x1000000-2000000 @@ -806,7 +806,7 @@ void nv3_init_mappings_mmio() void nv3_init_mappings_svga() { - nv_log("NV3: Initialising SVGA core memory mapping\n"); + nv_log("Initialising SVGA core memory mapping\n"); // setup the svga mappings mem_mapping_set(&nv3->nvbase.framebuffer_mapping, 0, 0, @@ -873,7 +873,7 @@ void nv3_update_mappings() 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"); + nv_log("The memory was turned off, not much is going to happen.\n"); return; } @@ -885,7 +885,7 @@ void nv3_update_mappings() // first map bar0 - nv_log("NV3: BAR0 (MMIO Base) = 0x%08x\n", nv3->nvbase.bar0_mmio_base); + nv_log("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 @@ -894,7 +894,7 @@ void nv3_update_mappings() // 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); + nv_log("BAR1 (Linear Framebuffer / NV_USER Base & RAMIN) = 0x%08x\n", nv3->nvbase.bar1_lfb_base); // this is likely mirrored // 4x on 2mb cards @@ -920,22 +920,22 @@ void nv3_update_mappings() switch (nv3->nvbase.svga.gdcreg[0x06] & 0x0c) { case NV3_CRTC_BANKED_128K_A0000: - nv_log("NV3: SVGA Banked Mode = 128K @ A0000h\n"); + nv_log("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"); + nv_log("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"); + nv_log("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"); + nv_log("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; @@ -954,7 +954,7 @@ void* nv3_init(const device_t *info) // Allows nv_log to be used for multiple nvidia devices nv_log_set_device(nv3->nvbase.log); - nv_log("NV3: initialising core\n"); + nv_log("initialising core\n"); // Figure out which vbios the user selected const char* vbios_id = device_get_config_bios("vbios"); @@ -975,7 +975,7 @@ void* nv3_init(const device_t *info) return NULL; } else - nv_log("NV3: Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); + nv_log("Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); // set the vram amount and gpu revision uint32_t vram_amount = device_get_config_int("vram_size"); @@ -984,7 +984,7 @@ void* nv3_init(const device_t *info) // 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"); + nv_log("using PCI bus\n"); pci_add_card(PCI_ADD_NORMAL, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); @@ -993,7 +993,7 @@ void* nv3_init(const device_t *info) } else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) { - nv_log("NV3: using AGP 1X bus\n"); + nv_log("using AGP 1X bus\n"); pci_add_card(PCI_ADD_AGP, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); @@ -1002,7 +1002,7 @@ void* nv3_init(const device_t *info) } // set vram - nv_log("NV3: VRAM=%d bytes\n", nv3->nvbase.svga.vram_max); + nv_log("VRAM=%d bytes\n", nv3->nvbase.svga.vram_max); // init memory mappings nv3_init_mappings(); @@ -1012,7 +1012,7 @@ void* nv3_init(const device_t *info) 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"); + nv_log("Initialising GPU core...\n"); nv3_pextdev_init(); // Initialise Straps nv3_pmc_init(); // Initialise Master Control @@ -1024,7 +1024,7 @@ void* nv3_init(const device_t *info) nv3_ptimer_init(); // Initialise programmable interval timer nv3_pvideo_init(); // Initialise video overlay engine - nv_log("NV3: Initialising I2C..."); + nv_log("Initialising I2C..."); nv3->nvbase.i2c = i2c_gpio_init("nv3_i2c"); nv3->nvbase.ddc = ddc_init(i2c_gpio_get_bus(nv3->nvbase.i2c)); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index e10382feb..de3bc2eec 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -108,7 +108,7 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) //ret = nv3_ramin_arbitrate_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); + nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); return 0x00; } @@ -171,7 +171,7 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) nv3_ramin_arbitrate_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); + nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); return; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index ffe2c57cf..e9d8723ad 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -40,7 +40,7 @@ nv_register_t pbus_registers[] = { void nv3_pbus_init() { - nv_log("NV3: Initialising PBUS..."); + nv_log("Initialising PBUS..."); nv_log("Done\n"); } @@ -53,7 +53,7 @@ uint32_t nv3_pbus_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PBUS Read from 0x%08x", address); + nv_log("PBUS Read from 0x%08x", address); // if the register actually exists if (reg) @@ -91,7 +91,7 @@ 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); + nv_log("PBUS Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) @@ -163,7 +163,7 @@ uint8_t nv3_pbus_rma_read(uint16_t addr) } // 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", + nv_log("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; @@ -228,7 +228,7 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val) 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", + nv_log("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) diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 3e0d63b5e..c62911fb2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -31,7 +31,7 @@ void nv3_pextdev_init() { - nv_log("NV3: Initialising PEXTDEV....\n"); + nv_log("Initialising PEXTDEV....\n"); // Set the chip straps // Make these configurable in the future... @@ -43,7 +43,7 @@ void nv3_pextdev_init() // Bus width 128-Bit (some gpus were sold as 64bit for cost reduction) // - nv_log("NV3: Initialising straps...\n"); + nv_log("Initialising straps...\n"); nv3->pextdev.straps = (NV3_PSTRAPS_AGP2X_DISABLED << NV3_PSTRAPS_AGP2X) | @@ -62,8 +62,8 @@ void nv3_pextdev_init() (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"); + nv_log("Straps = 0x%04x\n", nv3->pextdev.straps); + nv_log("Initialising PEXTDEV: Done\n"); } // @@ -89,11 +89,11 @@ uint32_t nv3_pextdev_read(uint32_t address) // special consideration for straps if (address == NV3_PSTRAPS) { - nv_log("NV3: Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); + nv_log("Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); } else { - nv_log("NV3: PEXTDEV Read from 0x%08x", address); + nv_log("PEXTDEV Read from 0x%08x", address); } // if the register actually exists @@ -129,12 +129,12 @@ 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); + nv_log("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); + nv_log("Huh? Tried to write to the straps. Something is wrong...\n", nv3->pextdev.straps); return; } diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index ba42c1734..2334e6d6c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -41,7 +41,7 @@ nv_register_t pfb_registers[] = { void nv3_pfb_init() { - nv_log("NV3: Initialising PFB..."); + nv_log("Initialising PFB..."); // initial configuration: // ram 4mb @@ -67,7 +67,7 @@ uint32_t nv3_pfb_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PFB Read from 0x%08x", address); + nv_log("PFB Read from 0x%08x", address); // if the register actually exists if (reg) @@ -106,7 +106,7 @@ 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", value, address); + nv_log("PFB Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) @@ -148,16 +148,16 @@ void nv3_pfb_config0_write(uint32_t val) uint32_t new_pfb_vtotal = new_pfb_htotal * (4/3); uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; - nv_log("NV3: Framebuffer Config Change\n"); - nv_log("NV3: Horizontal Size=%d pixels\n", new_pfb_htotal); - nv_log("NV3: Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); + nv_log("Framebuffer Config Change\n"); + nv_log("Horizontal Size=%d pixels\n", new_pfb_htotal); + nv_log("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) - nv_log("NV3: Bit Depth=8bpp\n"); + nv_log("Bit Depth=8bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_16BPP) - nv_log("NV3: Bit Depth=16bpp\n"); + nv_log("Bit Depth=16bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_32BPP) - nv_log("NV3: Bit Depth=32bpp\n"); + nv_log("Bit Depth=32bpp\n"); } \ 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 index 17d1f464b..d5a18af03 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -77,7 +77,7 @@ nv_register_t pfifo_registers[] = { // PFIFO init code void nv3_pfifo_init() { - nv_log("NV3: Initialising PFIFO..."); + nv_log("Initialising PFIFO..."); nv_log("Done!\n"); } @@ -89,7 +89,7 @@ uint32_t nv3_pfifo_read(uint32_t address) 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"); + nv_log("Repressing PFIFO read. The subsystem is disabled according to pmc_enable, returning 0\n"); return 0x00; } @@ -99,7 +99,7 @@ uint32_t nv3_pfifo_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PFIFO Read from 0x%08x", address); + nv_log("PFIFO Read from 0x%08x", address); // if the register actually exists if (reg) @@ -297,13 +297,13 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) 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"); + nv_log("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", value, address); + nv_log("PFIFO Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) @@ -355,7 +355,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) else if (new_size_ramht == 3) new_size_ramht = 0x8000; - nv_log("NV3: RAMHT Reconfiguration\n" + nv_log("RAMHT Reconfiguration\n" "Base Address in RAMIN: %d\n" "Size: 0x%08x bytes\n", ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12, new_size_ramht); #endif @@ -363,7 +363,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CONFIG_RAMFC: nv3->pfifo.ramfc_config = value; - nv_log("NV3: RAMFC Reconfiguration\n" + nv_log("RAMFC Reconfiguration\n" "Base Address in RAMIN: %d\n", ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9); break; case NV3_PFIFO_CONFIG_RAMRO: @@ -376,7 +376,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) else if (new_size_ramro == 1) new_size_ramro = 0x2000; - nv_log("NV3: RAMRO Reconfiguration\n" + nv_log("RAMRO Reconfiguration\n" "Base Address in RAMIN: %d\n" "Size: 0x%08x bytes\n", ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9, new_size_ramro); break; @@ -557,7 +557,7 @@ void nv3_pfifo_cache0_pull() nv3->pfifo.cache0_settings.get_address ^= 0x04; #ifndef RELEASE_BUILD - nv_log("NV3: ***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE0 PULLED ****** Contextual information below\n"); + nv_log("***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE0 PULLED ****** Contextual information below\n"); nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); #endif @@ -640,7 +640,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) // Did we fuck up? if (oh_shit) { - nv_log("NV3: WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); + nv_log("WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); return; } @@ -710,7 +710,7 @@ void nv3_pfifo_cache1_pull() nv3->pfifo.cache0_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; #ifndef RELEASE_BUILD - nv_log("NV3: ***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE1 PULLED ****** Contextual information below\n"); + nv_log("***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE1 PULLED ****** Contextual information below\n"); nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); #endif diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 9835a788d..facc75d2f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -32,7 +32,7 @@ // Initialise the PGRAPH subsystem. void nv3_pgraph_init() { - nv_log("NV3: Initialising PGRAPH..."); + nv_log("Initialising PGRAPH..."); // Set up the vblank interrupt nv3->nvbase.svga.vblank_start = nv3_pgraph_vblank_start; nv_log("Done!\n"); @@ -98,7 +98,7 @@ uint32_t nv3_pgraph_read(uint32_t address) 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"); + nv_log("Repressing PGRAPH read. The subsystem is disabled according to pmc_enable, returning 0\n"); return 0x00; } @@ -108,7 +108,7 @@ uint32_t nv3_pgraph_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PGRAPH Read from 0x%08x", address); + nv_log("PGRAPH Read from 0x%08x", address); // if the register actually exists if (reg) @@ -260,7 +260,7 @@ uint32_t nv3_pgraph_read(uint32_t address) // Addresses should be aligned to 4 bytes. uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - nv_log("NV3: PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); + nv_log("PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); } else /* Completely unknown */ { @@ -276,13 +276,13 @@ 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"); + nv_log("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); + nv_log("PGRAPH Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) @@ -439,7 +439,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // Addresses should be aligned to 4 bytes. uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - nv_log("NV3: PGRAPH Context Cache Write (Entry=%04x Value=%04x)\n", entry, value); + nv_log("PGRAPH Context Cache Write (Entry=%04x Value=%04x)\n", entry, value); nv3->pgraph.context_cache[entry] = value; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 4d8ef7c4f..f81dd8876 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -32,7 +32,7 @@ void nv3_pmc_init() { - nv_log("NV3: Initialising PMC....\n"); + nv_log("Initialising PMC....\n"); if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_A00) nv3->pmc.boot = NV3_BOOT_REG_REV_A00; @@ -43,7 +43,7 @@ void nv3_pmc_init() nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_SOFTWARE; - nv_log("NV3: Initialising PMC: Done\n"); + nv_log("Initialising PMC: Done\n"); } // @@ -60,7 +60,7 @@ nv_register_t pmc_registers[] = { uint32_t nv3_pmc_clear_interrupts() { - nv_log("NV3: Clearing IRQs\n"); + nv_log("Clearing IRQs\n"); pci_clear_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); } @@ -144,21 +144,21 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) { 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); + nv_log("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); + nv_log("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); + nv_log("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); + nv_log("NOT firing software-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE SOFTWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); } } @@ -178,7 +178,7 @@ uint32_t nv3_pmc_read(uint32_t address) uint32_t ret = 0x00; // todo: friendly logging - nv_log("NV3: PMC Read from 0x%08x", address); + nv_log("PMC Read from 0x%08x", address); // if the register actually exists if (reg) @@ -227,7 +227,7 @@ 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); + nv_log("PMC Write 0x%08x -> 0x%08x", value, address); // if the register actually exists... if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index d059eb05f..c16b97641 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -36,7 +36,7 @@ nv_register_t pme_registers[] = { void nv3_pme_init() { - nv_log("NV3: Initialising PME..."); + nv_log("Initialising PME..."); nv_log("Done\n"); } @@ -49,7 +49,7 @@ uint32_t nv3_pme_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PME Read from 0x%08x", address); + nv_log("PME Read from 0x%08x", address); // if the register actually exists if (reg) @@ -93,7 +93,7 @@ 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); + nv_log("PME Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index c87a7e14e..95cc2afe2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -33,7 +33,7 @@ void nv3_pramdac_init() { - nv_log("NV3: Initialising PRAMDAC\n"); + nv_log("Initialising PRAMDAC\n"); // defaults, these come from vbios in reality // driver defaults are nonsensical(?), or the algorithm is wrong @@ -45,7 +45,7 @@ void nv3_pramdac_init() nv3_pramdac_set_pixel_clock(); nv3_pramdac_set_vram_clock(); - nv_log("NV3: Initialising PRAMDAC: Done\n"); + nv_log("Initialising PRAMDAC: Done\n"); } // Polls the pixel clock. @@ -119,7 +119,7 @@ void nv3_pramdac_set_vram_clock() double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box - nv_log("NV3: Memory clock = %.2f MHz\n", frequency / 1000000.0f); + nv_log("Memory clock = %.2f MHz\n", frequency / 1000000.0f); nv3->nvbase.memory_clock_frequency = frequency; @@ -167,7 +167,7 @@ void nv3_pramdac_set_pixel_clock() double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box - nv_log("NV3: Pixel clock = %.2f MHz\n", frequency / 1000000.0f); + nv_log("Pixel clock = %.2f MHz\n", frequency / 1000000.0f); nv3->nvbase.pixel_clock_frequency = frequency; @@ -220,7 +220,7 @@ uint32_t nv3_pramdac_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PRAMDAC Read from 0x%08x", address); + nv_log("PRAMDAC Read from 0x%08x", address); // if the register actually exists if (reg) @@ -301,7 +301,7 @@ 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); + nv_log("PRAMDAC Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 3730765ab..16655779b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -61,7 +61,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) if (!nv3_ramin_arbitrate_read(addr, &val)) // Oh well { val = (uint8_t)nv3->nvbase.svga.vram[addr]; - nv_log("NV3: Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); } return (uint8_t)val; @@ -87,7 +87,7 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) if (!nv3_ramin_arbitrate_read(addr, &val)) { val = (uint16_t)vram_16bit[addr]; - nv_log("NV3: Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); } return val; @@ -114,7 +114,7 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { val = vram_32bit[addr]; - nv_log("NV3: Read dword from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("Read dword from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); } return val; @@ -139,7 +139,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val32)) { nv3->nvbase.svga.vram[addr] = val; - nv_log("NV3: Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); } @@ -165,7 +165,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val32)) { vram_16bit[addr] = val; - nv_log("NV3: Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); } @@ -191,7 +191,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val32)) { vram_32bit[addr] = val; - nv_log("NV3: Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } } @@ -358,7 +358,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel, uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; uint32_t ramht_cur_address = ramht_base; - nv_log("NV3: Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel, subchannel); + nv_log("Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel, subchannel); bool found_object = false; @@ -483,7 +483,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel, // Prints out some informaiton about the object void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) { - nv_log("NV3: Found object:"); + nv_log("Found object:"); nv_log("Name: 0x%04x", name); nv_log("Context:"); diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index a3872c40a..47db7b3b5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -30,10 +30,10 @@ uint32_t nv3_ramfc_read(uint32_t address) { - nv_log("NV3: RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramfc_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); + nv_log("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); } \ 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 index fc0190c2e..b471f3a87 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -39,17 +39,17 @@ uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) // is this the right endianness? uint32_t hash = (hash_bytes[0] ^ hash_bytes[1] ^ hash_bytes[2] ^ hash_bytes[3] ^ (uint8_t)channel); - nv_log("NV3: Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, name, channel); + nv_log("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, name, channel); return hash; } uint32_t nv3_ramht_read(uint32_t address) { - nv_log("NV3: RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramht_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); + nv_log("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index a206343c9..3ba6b9d7a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -30,12 +30,12 @@ uint32_t nv3_ramro_read(uint32_t address) { - nv_log("NV3: RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); } void nv3_ramro_write(uint32_t address, uint32_t value) { - nv_log("NV3: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)\n", value, address); + nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)\n", value, address); } void nv3_ramro_send() diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index b182644ec..366f2510f 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -43,7 +43,7 @@ nv_register_t ptimer_registers[] = { // ptimer init code void nv3_ptimer_init() { - nv_log("NV3: Initialising PTIMER..."); + nv_log("Initialising PTIMER..."); nv_log("Done!\n"); } @@ -99,7 +99,7 @@ uint32_t nv3_ptimer_read(uint32_t address) if (address != NV3_PTIMER_TIME_0_NSEC && address != NV3_PTIMER_TIME_1_NSEC) { - nv_log("NV3: PTIMER Read from 0x%08x", address); + nv_log("PTIMER Read from 0x%08x", address); } uint32_t ret = 0x00; @@ -167,7 +167,7 @@ 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", value, address); + nv_log("PTIMER Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index dacf984c9..9d3a9b8b3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -52,7 +52,7 @@ uint32_t nv3_pvideo_read(uint32_t address) // todo: friendly logging - nv_log("NV3: PVIDEO Read from 0x%08x", address); + nv_log("PVIDEO Read from 0x%08x", address); // if the register actually exists if (reg) @@ -99,7 +99,7 @@ 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); + nv_log("PVIDEO Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index eee994208..b07bf63b5 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -33,9 +33,20 @@ uint32_t nv3_user_read(uint32_t address) { uint8_t method_offset = (address & 0x1FFC); - nv_log("NV3: User Submission Area method_offset=0x%04x OH SHIT!! NOT IMPLEMENTED!!!", method_offset); + nv_log("User Submission Area method_offset=0x%04x\n", method_offset); + + // 0x10 is free CACHE1 object + // TODO: THERE ARE OTHER STUFF! + switch (method_offset) + { + case NV3_GENERIC_METHOD_IS_PFIFO_FREE: + return nv3_pfifo_cache1_is_free(); + + } + + nv_log("IT'S NOT IMPLEMENTED!!!!\n", method_offset); + - // 0x10 is free CACHE1 object, other stuff? return 0x00; }; From fea8cd649af3bbf7f2e72c77885dfcca35c2eda8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 6 Feb 2025 00:49:19 +0000 Subject: [PATCH 089/274] add various missing registers --- src/include/86box/nv/vid_nv3.h | 36 +++++++++++++++++++++--- src/video/nv/nv3/subsystems/nv3_pfb.c | 27 ++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 9 ++++++ src/video/nv/nv3/subsystems/nv3_pvideo.c | 23 +++++++++++++++ 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 7602361fc..47a1d10a0 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -362,7 +362,15 @@ extern const device_config_t nv3_config[]; #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_DELAY 0x100044 +#define NV3_PFB_GREEN_0 0x1000C0 + #define NV3_PFB_CONFIG_0 0x100200 // Framebuffer interface config register 0 + +// What is this lol +// ??? Part of the memory timings +#define NV3_PFB_RTL 0x100300 #define NV3_PFB_CONFIG_0_RESOLUTION 0 // 1=40 horiz. resolution // i assume it can be divided by some kind of divisor to produce the vertical resolution (e.g. 3/2 or multiply by 2/3) to get the final @@ -487,7 +495,12 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_TRAPPED_DATA 0x4006B8 #define NV3_PGRAPH_TRAPPED_INSTANCE 0x4006BC -#define NV3_PGRAPH_DMA_INTR_0 0x401000 // PGRAPH DMA Interrupt Status +#define NV3_PGRAPH_DMA_INTR_0 0x401100 // PGRAPH DMA Interrupt Status +#define NV3_PGRAPH_DMA_INTR_INSTANCE 0 +#define NV3_PGRAPH_DMA_INTR_PRESENT 4 +#define NV3_PGRAPH_DMA_INTR_PROTECTION 8 +#define NV3_PGRAPH_DMA_INTR_LINEAR 12 +#define NV3_PGRAPH_DMA_INTR_NOTIFY 16 #define NV3_PGRAPH_DMA_INTR_EN_0 0x401140 // PGRAPH DMA Interrupt Enable 0 // not sure about the class ids @@ -559,6 +572,13 @@ extern const device_config_t nv3_config[]; #define NV3_PVIDEO_START 0x680000 // Video Generation / overlay configuration #define NV3_PVIDEO_INTR 0x680100 #define NV3_PVIDEO_INTR_EN 0x680140 +#define NV3_PVIDEO_FIFO_THRESHOLD 0x680238 +#define NV3_PVIDEO_FIFO_BURST_LENGTH 0x68023C +#define NV3_PVIDEO_OVERLAY 0x680244 +#define NV3_PVIDEO_OVERLAY_VIDEO_IS_ON 0 +#define NV3_PVIDEO_OVERLAY_KEY_ENABLED 4 +#define NV3_PVIDEO_OVERLAY_FORMAT 8 // 0 = CCIR, 1 = YUY2 + #define NV3_PVIDEO_END 0x6802FF #define NV3_PRAMDAC_START 0x680300 @@ -780,8 +800,11 @@ typedef struct nv3_straps_s typedef struct nv3_pfb_s { uint32_t boot; - uint32_t config_0; + uint32_t config_0; // Framebuffer width, etc. uint32_t config_1; + uint32_t green; + uint32_t delay; + uint32_t rtl; // Part of the memory timings } nv3_pfb_t; #define NV3_RMA_NUM_REGS 4 @@ -974,6 +997,8 @@ typedef struct nv3_pgraph_s uint32_t interrupt_enable_0; // Interrupt enable 0 uint32_t interrupt_status_1; // Interrupt status 1 uint32_t interrupt_enable_1; // Interrupt enable 1 + uint32_t interrupt_status_dma; // Interrupt status for DMA + uint32_t interrupt_enable_dma; // Interrupt enable for DMA uint32_t context_switch; // TODO: Make this a struct, it's just going to be enormous lol. nv3_pgraph_context_control_t context_control; @@ -986,10 +1011,12 @@ typedef struct nv3_pgraph_s uint32_t abs_uclip_xmax; uint32_t abs_uclip_ymin; uint32_t abs_uclip_ymax; + // Canvas stuff nv3_position_16_bigy_t src_canvas_min; nv3_position_16_bigy_t src_canvas_max; nv3_position_16_bigy_t dst_canvas_min; nv3_position_16_bigy_t dst_canvas_max; + // Pattern stuff nv3_color_x3a10g10b10_t pattern_color_0_0; uint32_t pattern_color_0_1; // only 7:0 relevant nv3_color_x3a10g10b10_t pattern_color_1_0; @@ -1012,8 +1039,6 @@ typedef struct nv3_pgraph_s uint32_t trapped_address; uint32_t trapped_data; uint32_t trapped_instance; - uint32_t interrupt_status_dma; - uint32_t interrupt_enable_dma; } nv3_pgraph_t; // GPU Manufacturing Configuration (again) @@ -1172,6 +1197,9 @@ typedef struct nv3_pvideo_s { uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable + uint32_t fifo_threshold; // FIFO threshold + uint32_t fifo_burst_size; // FIFO burst size + uint32_t overlay_settings; // Overlay settings } nv3_pvideo_t; typedef struct nv3_pme_s // Mediaport diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 2334e6d6c..b2eae7c55 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -34,8 +34,11 @@ void nv3_pfb_config0_write(uint32_t val); nv_register_t pfb_registers[] = { { NV3_PFB_BOOT, "PFB Boot Config", NULL, NULL}, + { NV3_PFB_DELAY, "PFB Delay", NULL, NULL}, + { NV3_PFB_GREEN_0, "PFB Green / Power Saving", NULL, NULL,}, { NV3_PFB_CONFIG_0, "PFB Framebuffer Config 0", nv3_pfb_config0_read, nv3_pfb_config0_write }, { NV3_PFB_CONFIG_1, "PFB Framebuffer Config 1", NULL, NULL }, + { NV3_PFB_RTL, "PFB RTL (Part of memory timings?)", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -86,6 +89,16 @@ uint32_t nv3_pfb_read(uint32_t address) case NV3_PFB_CONFIG_1: ret = nv3->pfb.config_1; break; + case NV3_PFB_GREEN_0: + ret = nv3->pfb.green; + break; + case NV3_PFB_DELAY: + ret = nv3->pfb.delay; + break; + case NV3_PFB_RTL: + ret = nv3->pfb.rtl; + break; + } } @@ -123,9 +136,19 @@ void nv3_pfb_write(uint32_t address, uint32_t value) { switch (reg->address) { - // Config 0 has a read/write function + // Config 0 has a read/write function case NV3_PFB_CONFIG_1: // Config Register 1 nv3->pfb.config_1 = value; + break; + case NV3_PFB_GREEN_0: + nv3->pfb.green = value; + break; + case NV3_PFB_DELAY: + nv3->pfb.delay = value; + break; + case NV3_PFB_RTL: + nv3->pfb.rtl = value; + break; } } } @@ -145,7 +168,7 @@ void nv3_pfb_config0_write(uint32_t val) uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; // i don't think 16:9 is supported - uint32_t new_pfb_vtotal = new_pfb_htotal * (4/3); + uint32_t new_pfb_vtotal = new_pfb_htotal * (4.0/3.0); uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; nv_log("Framebuffer Config Change\n"); diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index facc75d2f..d6ae35465 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -332,6 +332,15 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) nv3->pgraph.interrupt_enable_1 = value & 0x00011111; nv3_pmc_handle_interrupts(true); break; + case NV3_PGRAPH_DMA_INTR_0: + nv3->pgraph.interrupt_status_dma &= ~value; + nv3_pmc_clear_interrupts(); + break; + case NV3_PGRAPH_DMA_INTR_EN_0: + nv3->pgraph.interrupt_enable_dma = value & 0x000111111; + nv_log("Handling PGRAPH_DMA interrupts not implemented"); + nv3_pmc_handle_interrupts(true); + break; // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like // during the driver initialisation process diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index 9d3a9b8b3..6709c92a2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -32,6 +32,9 @@ nv_register_t pvideo_registers[] = { { NV3_PVIDEO_INTR, "PVIDEO - Interrupt Status", NULL, NULL}, { NV3_PVIDEO_INTR_EN, "PVIDEO - Interrupt Enable", NULL, NULL,}, + { NV3_PVIDEO_FIFO_THRESHOLD, "PVIDEO - FIFO Fill Threshold", NULL, NULL}, + { NV3_PVIDEO_FIFO_BURST_LENGTH, "PVIDEO - FIFO Burst Length (1=32, 2=64, 3=128)", NULL, NULL}, + { NV3_PVIDEO_OVERLAY, "PVIDEO - Overlay Info (Bit0 = Video On, Bit4 = Key On, Bit8 = Format, 0=CCIR, 1=YUV2)", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -78,6 +81,16 @@ uint32_t nv3_pvideo_read(uint32_t address) case NV3_PVIDEO_INTR_EN: ret = nv3->pvideo.interrupt_enable; break; + case NV3_PVIDEO_FIFO_THRESHOLD: + ret = nv3->pvideo.fifo_threshold; + break; + case NV3_PVIDEO_FIFO_BURST_LENGTH: + ret = nv3->pvideo.fifo_burst_size & 0x03; + break; + case NV3_PVIDEO_OVERLAY: + ret = nv3->pvideo.overlay_settings & 0xFF; + break; + } } @@ -126,6 +139,16 @@ void nv3_pvideo_write(uint32_t address, uint32_t value) case NV3_PVIDEO_INTR_EN: nv3->pvideo.interrupt_enable = value & 0x00000001; break; + case NV3_PVIDEO_FIFO_THRESHOLD: + // only bits 6:3 matter + nv3->pvideo.fifo_threshold = ((value >> 3) & 0x0F) << 3; + break; + case NV3_PVIDEO_FIFO_BURST_LENGTH: + nv3->pvideo.fifo_burst_size = value & 0x03; + break; + case NV3_PVIDEO_OVERLAY: + nv3->pvideo.overlay_settings = value & 0xFF; + break; } } } From c816f7560dfaeed71ac01fae5380acc2fc9ff93a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 9 Feb 2025 18:10:17 +0000 Subject: [PATCH 090/274] more defines --- doc/nvidia_notes/NV3 DMA Engine.txt | 29 +++++++++++ src/include/86box/nv/vid_nv3.h | 62 ++++++++++++++++++++++++ src/video/nv/nv3/nv3_core.c | 4 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 1 + src/video/nv/nv3/subsystems/nv3_pmc.c | 10 ++-- src/video/nv/nv3/subsystems/nv3_pme.c | 4 +- 6 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 doc/nvidia_notes/NV3 DMA Engine.txt diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt new file mode 100644 index 000000000..50d34cfa9 --- /dev/null +++ b/doc/nvidia_notes/NV3 DMA Engine.txt @@ -0,0 +1,29 @@ +NV3 DMA Engine +(DirectDraw Driver) + +Initially set CACHES, CACHE1_PULL0, CACHE1_PULL1, CACHE1_DMA0 to 1 + +Same for other areas + +CACHE1_PUSH1 contains CHID + +If it's different: + +If RmFifoFlushContext failed: Do nothing + +Set PULL0, PUSH0, Caches to 1, return false + + +If it's not: +DMA TLB PTE seems to be 1 for direct programming, maybe RM does it differently +Tag=FFFFFFFF +CACHE1_DMA1 - Number of bytes to send +CACHE1_DMA2 - Get offset +CACHE1_DMA3 - Bus address space (Area BAR0 mapped to? Or bar1?) + +TO START: +To set up DMA for for Cache1 Puller: CACHE1_PULL0 -> 1, changes to 0 when done +To set up DMA Cache1 Push: CACHE1_PULL0 -> 1, changes to 0 when done +Set CACHES to 1 + +GO: Set DMA0 to 1 diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 47a1d10a0..6b6e0e5d6 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -443,10 +443,72 @@ extern const device_config_t nv3_config[]; #define NV3_PME_END 0x200FFF #define NV3_PGRAPH_START 0x400000 // Scene graph for 2d/3d rendering...the most important part // PGRAPH Core + +// For these debug registers, 0=Disabled, 1=Enabled + +// Debug 0: General #define NV3_PGRAPH_DEBUG_0 0x400080 +#define NV3_PGRAPH_DEBUG_0_STATE_IN_RESET 0 +#define NV3_PGRAPH_DEBUG_0_AP_PIPE_IN_RESET 1 +#define NV3_PGRAPH_DEBUG_0_CACHE_IN_RESET 2 +#define NV3_PGRAPH_DEBUG_0_3D_PIPE_IN_RESET 3 +#define NV3_PGRAPH_DEBUG_0_BULK_READS 4 +#define NV3_PGRAPH_DEBUG_0_TILING 16 +#define NV3_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D 20 +#define NV3_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D 21 +#define NV3_PGRAPH_DEBUG_0_DRAWDIR_AUT 24 +#define NV3_PGRAPH_DEBUG_0_DRAWDIR_Y 25 +#define NV3_PGRAPH_DEBUG_0_ALPHA_ABORT 28 + +// Debug 1: Registers #define NV3_PGRAPH_DEBUG_1 0x400084 +#define NV3_PGRAPH_DEBUG_1_VOLATILE_RESET_LAST 0 +#define NV3_PGRAPH_DEBUG_1_DMA_ACTIVITY_CANCEL 4 +#define NV3_PGRAPH_DEBUG_1_TURBO3D_2X 8 +#define NV3_PGRAPH_DEBUG_1_TURBO3D_4X 9 +#define NV3_PGRAPH_DEBUG_1_TRIANGLE_OPS 12 +#define NV3_PGRAPH_DEBUG_1_TRIANGLE_CLIP_OPS 13 +#define NV3_PGRAPH_DEBUG_1_INSTANCE 16 +#define NV3_PGRAPH_DEBUG_1_CONTEXT 20 +#define NV3_PGRAPH_DEBUG_1_CACHE_FLUSH 24 +#define NV3_PGRAPH_DEBUG_1_ZCLAMP 28 + +// Debug 2: 3D Pipeline #define NV3_PGRAPH_DEBUG_2 0x400088 +#define NV3_PGRAPH_DEBUG_2_AVOID_READMODIFYWRITE_BLEND 0 +#define NV3_PGRAPH_DEBUG_2_DPWR_FIFO 8 +#define NV3_PGRAPH_DEBUG_2_BILINEAR_FILTERING_3D 12 +#define NV3_PGRAPH_DEBUG_2_ANISOTROPIC_FILTERING_3D 13 +#define NV3_PGRAPH_DEBUG_2_FOG 14 +#define NV3_PGRAPH_DEBUG_2_LIGHTING 15 // Not sure what this does, maybe hardware t&l was planned +#define NV3_PGRAPH_DEBUG_2_BILINEAR_FILTERING_2D 16 +#define NV3_PGRAPH_DEBUG_2_ANISOTROPIC_FILTERING_2D 17 +#define NV3_PGRAPH_DEBUG_2_D3D_COALESCE 20 // coalesce reads/writes for d3d class 0x17 +#define NV3_PGRAPH_DEBUG_2_D3D_COALESCE_POINT_ZETA 22 // class 0x18 coalesce +#define NV3_PGRAPH_DEBUG_2_PREFETCH 24 +#define NV3_PGRAPH_DEBUG_2_VOLATILE_RESET 28 + +// Debug 3: Zeta & Alpha Buffer #define NV3_PGRAPH_DEBUG_3 0x40008C +#define NV3_PGRAPH_DEBUG_3_CULLING 0 +#define NV3_PGRAPH_DEBUG_3_FAST_DATA_D3D 4 +#define NV3_PGRAPH_DEBUG_3_FAST_DATA_STRETCH 5 +#define NV3_PGRAPH_DEBUG_3_ZFLUSH 7 +#define NV3_PGRAPH_DEBUG_3_AUTOZFLUSH_POINT_ZETA 8 +#define NV3_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D 9 +#define NV3_PGRAPH_DEBUG_3_SLOT_CONFLICT_POINT_ZETA 10 // Slot conflict handling for POINT_ZETA (class 0x18) +#define NV3_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D 11 // Slot conflict handling for D3D5_TRI (class 0x17) +#define NV3_PGRAPH_DEBUG_3_EARLY_ZABORT 12 +#define NV3_PGRAPH_DEBUG_3_TRIANGLE_END_FLUSH 13 +#define NV3_PGRAPH_DEBUG_3_ZFIFO_NOOP 14 // ??? +#define NV3_PGRAPH_DEBUG_3_DITHER 15 +#define NV3_PGRAPH_DEBUG_3_FORCE_COLOR_BUFFER_READ 16 +#define NV3_PGRAPH_DEBUG_3_FORCE_ZETA_BUFFER_READ 17 +#define NV3_PGRAPH_DEBUG_3_DATA_CHECK 20 +#define NV3_PGRAPH_DEBUG_3_DATA_CHECK_FAIL 21 +#define NV3_PGRAPH_DEBUG_3_FORMAT_CHECK 22 +#define NV3_PGRAPH_DEBUG_3_ALPHA_CHECK 24 + #define NV3_PGRAPH_INTR_0 0x400100 #define NV3_PGRAPH_INTR_1 0x400104 #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ff844c02d..d190c838e 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -952,8 +952,10 @@ void* nv3_init(const device_t *info) else nv3->nvbase.log = log_open_cyclic("NV3"); +#ifdef ENABLE_NV_LOG // Allows nv_log to be used for multiple nvidia devices - nv_log_set_device(nv3->nvbase.log); + nv_log_set_device(nv3->nvbase.log); +#endif nv_log("initialising core\n"); // Figure out which vbios the user selected diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index d6ae35465..b1743c04d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -131,6 +131,7 @@ uint32_t nv3_pgraph_read(uint32_t address) break; case NV3_PGRAPH_DEBUG_3: ret = nv3->pgraph.debug_3; + break; //interrupt status and enable regs case NV3_PGRAPH_INTR_0: ret = nv3->pgraph.interrupt_status_0; diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index f81dd8876..f7f91e87d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -232,10 +232,6 @@ void nv3_pmc_write(uint32_t address, uint32_t value) // 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) @@ -266,5 +262,11 @@ void nv3_pmc_write(uint32_t address, uint32_t value) break; } } + + if (reg->friendly_name) + nv_log(": %s\n", reg->friendly_name); + else + nv_log("\n"); + } } \ 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 index c16b97641..392bcfd63 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -61,7 +61,7 @@ uint32_t nv3_pme_read(uint32_t address) { // Interrupt state: // Bit 0 - Image Notifier - // Bit 4 - Vertical Blank Interfal Notifier + // Bit 4 - Vertical Blank Interval Notifier // Bit 8 - Video Notifier // Bit 12 - Audio Notifier // Bit 16 - VMI Notifer @@ -128,4 +128,4 @@ void nv3_pme_write(uint32_t address, uint32_t value) } } - } \ No newline at end of file +} \ No newline at end of file From 35cb8a4cf2f6ca1c04df2250e2ce69407ccdd6f8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 10 Feb 2025 02:32:02 +0000 Subject: [PATCH 091/274] Fix subchannel free detection. --- .../nv3 driver init status_2025-02-10.txt | 96 +++++++++++++++++++ .../nv3 driver status_CURRENT.txt | 4 - src/include/86box/nv/vid_nv3.h | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 7 +- src/video/nv/nv3/subsystems/nv3_user.c | 2 +- 5 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 doc/nvidia_notes/nv3 driver init status_2025-02-10.txt delete mode 100644 doc/nvidia_notes/nv3 driver status_CURRENT.txt diff --git a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt new file mode 100644 index 000000000..c76e79743 --- /dev/null +++ b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt @@ -0,0 +1,96 @@ +nv3_disp: + +0x10fe -> DrvBitBlt +0x19be -> DrvCopyBits +0x597c -> DrvCreateDeviceBitmap +0x5a6e -> DrvDeleteDeviceBitmap +0x60b8 -> DrvTextOut +0x6248 -> DrvDestroyFont +0x64b8 -> DrvRealizeBrush +0x6a46 -> DrvDitherColor +0x797a -> DrvGetDirectDrawInfo +0x7b14 -> DrvEnableDirectDraw +0x7b70 -> DrvDisableDirectDraw +0x817c -> DrvPaint +0x81c2 -> DrvResetPDEV +0x82dc -> DrvEnableDriver +0x8312 -> DrvEnablePDEV +0x83ee -> DrvDisablePDEV +0x840a -> DrvCompletePDEV +0x8418 -> DrvSynchronise +0x845a -> DrvEnableSurface +0x851a -> DrvDisableSurface +0x8554 -> DrvAssertMode +0x8690 -> DrvGetModes +0xe59a -> DrvEscape +0xf3ee -> DrvFillPath +0xf3f6 -> DrvStrokePath +0xfa08 -> DrvLineTo +0x12fee -> DrvSetPalette +0x132a4 -> DrvMovePointer +0x13d20 -> DrvSetPointerShape +0x13dea -> DrvStretchBlt +0x147f2 -> DrvSetPixelFormat +0x1483c -> DrvDescribePixelFormat +0x1495a -> DrvClipChanged +0x255b8 -> DrvSwapBuffers + +DrvEnableDriver SUCCESS +DrvEnablePDEV SUCCESS 23:28 09/02/2025 + Check for cjCaps >= 0x130 && cjDevInfo >= 0x12C SUCCESS 23:31 09/02/2025 + EngAllocMem call SUCCESS 23:38 09/02/2025 + CreateOglGlobalMemory call SUCCESS 23:40 09/02/2025 + bInitializeModeFields call SUCCESS 23:41 09/02/2025 + bInitializePalette call SUCCESS 23:42 09/02/2025 + EngDeviceIoControl IOCTL 0x232020 (CHECK mini) SUCCESS + EngDeviceIoControl IOCTL 0x232044 (CHECK mini) SUCCESS (eax=0) +DrvCompletePDEV SUCCESS 23:52 09/02/2025 +DrvEnableSurface + bEnableHardware call + EngCreateSemaphore call #1 csCrtc SUCCESS 00:55 10/02/2025 + EngCreateSemaphore call #2 csFifo SUCCESS 00:57 10/02/2025 + EngDeviceIoControl IOCTL 0x230460 SUCCESS 00:57 10/02/2025 + EngDeviceIoControl IOCTL 0x230458 SUCCESS 00:57 10/02/2025 + NvAllocRoot SUCCESS 01:03 10/02/2025 + NvAllocDevice SUCCESS 01:04 10/02/2025 + NV3/NV4 architecture check SUCCESS 01:14 10/02/2025 + bAssertModeHardware call (bEnable=1) FAILURE - HANG/LOOP + EngDeviceIoControl IOCTL 0x23040C] FAILURE 01:27 10/02/2025 + nv3_mini NVStartIO ioctlcode=0x23040C + NVSetMode + NV3SetMode SUCCESS 01:53 10/02/2025 + RmUnloadState SUCCESS 01:48 10/02/2025 + VBESetModeEx SUCCESS 01:51 10/02/2025 + NV_OEMEnableExtensions SUCCESS 01:52 10/02/2025 + UpdateArbitrationSettings SUCCESS 01:52 10/02/2025 + RmLoadState SUCCESS 01:53 10/02/2025 + NV3EnableCursor SUCCESS 01:54 10/02/2025 + NV3WaitUntilFinished SUCCESS Passing 02:23 10/02/2025 + EngDeviceIoControl IOCTL 0x230408 + EngDeviceIoControl IOCTL 0x232024 + NvAllocHardware + bCreateStdPatches(?) + CHECK - NV4 + vDestroyStdPatches(?) + NV3_WaitForOneVerticalRefresh + EngDeviceIoControl IOCTL 0x230410 + + + SET UP CORRECT FUNCTION POINTERS + + _heap_init call + bEnableOffscreenHeap call + bEnablePointer call + bEnableText call + bEnablePalette call + bEnableDirectDraw call + EngCreateBitmap call + EngAssociateBitmap call + + + + + +DrvDisableSurface: ONLY IN THE CASE OF FAILURE + + diff --git a/doc/nvidia_notes/nv3 driver status_CURRENT.txt b/doc/nvidia_notes/nv3 driver status_CURRENT.txt deleted file mode 100644 index f284f7457..000000000 --- a/doc/nvidia_notes/nv3 driver status_CURRENT.txt +++ /dev/null @@ -1,4 +0,0 @@ -Failure condition: - Failed check, causes fifoService call in fifo service init in RM, FifoState STATE_LOAD - - CHECK NVSTARTIO! DEBUG TOMORROW \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 6b6e0e5d6..aeff3a8ce 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1431,7 +1431,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val); void nv3_pfifo_cache1_pull(); uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); -bool nv3_pfifo_cache1_is_free(); +uint32_t nv3_pfifo_cache1_num_free_spaces(); // NV3 PFB void nv3_pfb_init(); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index d5a18af03..5fe709ec6 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -185,7 +185,7 @@ uint32_t nv3_pfifo_read(uint32_t address) // Check if Cache1 (0x7C bytes in size depending on gpu?) is full // Based on how the drivers do it - if (!nv3_pfifo_cache1_is_free()) + if (!nv3_pfifo_cache1_num_free_spaces()) ret |= 1 << NV3_PFIFO_CACHE1_STATUS_FULL; if (nv3->pfifo.runout_put != nv3->pfifo.runout_get) @@ -605,7 +605,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) new_address |= (nv3_runout_reason_cache_ran_out << NV3_PFIFO_RUNOUT_RAMIN_ERR); } - if (!nv3_pfifo_cache1_is_free()) + if (!nv3_pfifo_cache1_num_free_spaces()) { oh_shit = true; oh_shit_reason = nv3_runout_reason_free_count_overrun; @@ -718,7 +718,8 @@ void nv3_pfifo_cache1_pull() //Todo: finish it } -bool nv3_pfifo_cache1_is_free() +// THIS IS PER SUBCHANNEL! +uint32_t nv3_pfifo_cache1_num_free_spaces() { // convert to gray code uint32_t real_get_address = nv3_pfifo_cache1_normal2gray(nv3->pfifo.cache1_settings.get_address); diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index b07bf63b5..43fa72a94 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -40,7 +40,7 @@ uint32_t nv3_user_read(uint32_t address) switch (method_offset) { case NV3_GENERIC_METHOD_IS_PFIFO_FREE: - return nv3_pfifo_cache1_is_free(); + return nv3_pfifo_cache1_num_free_spaces(); } From 13597fc39ec8eb44948f5482c90ace6c4b8a2ede Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Feb 2025 23:25:06 +0000 Subject: [PATCH 092/274] fix SetNotifyCtxDma --- src/include/86box/nv/classes/vid_nv3_classes.h | 7 ++++--- src/video/CMakeLists.txt | 2 ++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 5 +++-- src/video/nv/nv3/subsystems/nv3_user.c | 10 ++++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 36bac6e66..0e638f17e 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1096,8 +1096,10 @@ typedef struct nv3_grobj_s // TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! #pragma pack(pop) // return packing to whatever it was before this disaster -// Method IDs -#define NV3_GENERIC_METHOD_IS_PFIFO_FREE 0x0010 +// PIO Subchannel info +#define NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE 0x0010 +#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START 0x0012 +#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 // Class methods void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj); @@ -1109,7 +1111,6 @@ void nv3_class_005_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_006_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_007_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_009_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj); void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj); diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index cf65f2519..388681f88 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -80,6 +80,7 @@ add_library(vid OBJECT vid_att2xc498_ramdac.c vid_xga.c vid_bochs_vbe.c + nv/nv_base.c nv/nv_rivatimer.c nv/nv3/nv3_core.c @@ -123,6 +124,7 @@ add_library(vid OBJECT nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c nv/nv3/classes/nv3_class_018_point_zeta_buffer.c nv/nv3/classes/nv3_class_01c_image_in_memory.c + ) if(G100) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 5fe709ec6..ad4d4469f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -612,7 +612,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); } - if (method_offset > 0 && method_offset <= 0x100) + // 0x0 is used for the context + if (method_offset > 0 && method_offset < 0x100) { // Reserved NVIDIA Objects oh_shit = true; @@ -651,7 +652,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) nv3->pfifo.cache1_entries[current_put_address].data = val; // now we have to recalculate the cache1 put address - uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address); + uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address) + 1; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# next_put_address &= NV3_PFIFO_CACHE1_SIZE_REV_C; diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index 43fa72a94..c26df9b40 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -28,19 +28,25 @@ #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv3.h> + +// PIO Method Submission // 128 channels conceptually supported - a hangover from nv1 where multiple windows all directly programming the gpu were supported? total lunacy. uint32_t nv3_user_read(uint32_t address) { + // Get the address within the subchannel + //todo: print out the subchannel uint8_t method_offset = (address & 0x1FFC); - nv_log("User Submission Area method_offset=0x%04x\n", method_offset); + nv_log("User Submission Area PIO Subchannel method_offset=0x%04x\n (Trying to read...)", method_offset); // 0x10 is free CACHE1 object // TODO: THERE ARE OTHER STUFF! switch (method_offset) { - case NV3_GENERIC_METHOD_IS_PFIFO_FREE: + case NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE: return nv3_pfifo_cache1_num_free_spaces(); + case NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START ... NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END: + return 0x00; } From 289768a93f76ea94280564b2bad2dffe3a01bf36 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 12 Feb 2025 00:36:56 +0000 Subject: [PATCH 093/274] Fix include header capitalisation --- .../86box/nv/classes/vid_nv3_classes.h | 6 ++---- src/include/86box/nv/vid_nv3.h | 2 +- .../nv3/classes/nv3_class_001_beta_factor.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_002_rop.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 14 ++++++------- .../nv3_class_005_clipping_rectangle.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_006_pattern.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_007_rectangle.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_008_point.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_009_line.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_00b_triangle.c | 14 ++++++------- .../classes/nv3_class_00c_win95_gdi_text.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 14 ++++++------- .../nv3_class_00e_scaled_image_from_mem.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_010_blit.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_011_image.c | 14 ++++++------- .../nv/nv3/classes/nv3_class_012_bitmap.c | 14 ++++++------- .../classes/nv3_class_014_transfer2memory.c | 14 ++++++------- .../nv3_class_015_stretched_image_from_cpu.c | 14 ++++++------- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 14 ++++++------- .../classes/nv3_class_018_point_zeta_buffer.c | 14 ++++++------- .../classes/nv3_class_01c_image_in_memory.c | 14 ++++++------- src/video/nv/nv3/classes/nv3_class_names.c | 12 +++++------ .../nv/nv3/classes/nv3_class_shared_methods.c | 14 ++++++------- src/video/nv/nv3/nv3_core.c | 16 +++++++-------- src/video/nv/nv3/nv3_core_arbiter.c | 14 ++++++------- src/video/nv/nv3/nv3_core_config.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pbus.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pextdev.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pfb.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pfifo.c | 20 +++++++++---------- src/video/nv/nv3/subsystems/nv3_pgraph.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pmc.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pme.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pramdac.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pramin.c | 16 +++++++-------- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 14 ++++++------- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 16 +++++++-------- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_ptimer.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_pvideo.c | 14 ++++++------- src/video/nv/nv3/subsystems/nv3_user.c | 14 ++++++------- src/video/nv/nv_base.c | 6 +++--- 46 files changed, 312 insertions(+), 314 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 0e638f17e..aa23d29dc 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -11,8 +11,7 @@ * Note: These uint32_ts are basically object methods that are being submitted * They have different names so the user can use them more easily but different versions of the same class can be distinguished * ALL of these structures HAVE(?) to be a size of exactly 0x2000 bytes because that's what the hashtable expects and they need to actually map into the vram address space - * (they are converted to pointers). - * directly to the PHYSICAL PGRAPH REGISTERS while sitting in RAMHT!!!!. + * (they are converted to pointers). In the case of NV3, these map directly to the PHYSICAL PGRAPH REGISTERS while sitting in RAMHT!!!!. * * Also, these class IDs don't relate to the internal architecture of the GPU. * Effectively, the NVIDIA drivers are faking shit. There are only 16 classes but the drivers recognise many more. See nv3_object_classes_driver.txt for the list of @@ -1065,8 +1064,7 @@ typedef enum nv3_object_class_01C_pixel_format_e // 16-bits (Y16) - Planar YUV 16 bits (Y value only), 2 bits of indexed colour too? nv3_m2mt_pixel_format_le_y16_p2 = 0x1010101, - /* 1 unused bit, 555 15-bit format, p2(?) - */ + /* 1 unused bit, 555 15-bit format, p2(?) */ nv3_m2mt_pixel_format_x1r5g5b5_p2 = 0x1000000, // X8G8B8R8, 24-bit colour (or 24-bit colour with alpha) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index aeff3a8ce..72d6d5a3d 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -22,7 +22,7 @@ */ #pragma once -#include <86Box/nv/classes/vid_nv3_classes.h> +#include <86box/nv/classes/vid_nv3_classes.h> // The GPU base structure extern const device_config_t nv3_config[]; diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index b6b97ee3a..7a700bf6e 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_001 beta_factor; diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index 747d4b017..ac06ef010 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_002 nv3_rop; diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 08f854529..887e2a8bd 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_003 nv3_chroma_key; diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 0388bc35a..95e5e70c4 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_004 nv3_plane_mask; diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 01f395040..76ab1cfec 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -18,14 +18,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_005 nv3_clipping_rectangle; diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 282557b95..32561e977 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_006 nv3_pattern; diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 3d378df91..2b194960b 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_007 nv3_rectangle; diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 55fae702c..eaa12f5c4 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_008 nv3_point; diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index cd0a8b768..a17cf0f90 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_009 nv3_line; diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index f9c23dc21..66d3d949b 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -20,14 +20,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_00A nv3_lin; diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index a65b3d600..f1d41a816 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_00B nv3_triangle; diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 54c013783..d8dbc4513 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_00C nv3_win95_gdi_text; diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 10ccb9bc3..4f51f187a 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_00D nv3_m2mf; diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index 9a4e86dca..165c1e9d3 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_00E nv3_scaled_image_from_mem; diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 2bcdc2234..f32309343 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_010 nv3_blit; diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 9a2f73486..e42ea8071 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_011 nv3_image; diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index f2d49f6b0..a0ff8d537 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -20,14 +20,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_012 nv3_bitmap; diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index ada899cf6..bc4e3f879 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -20,14 +20,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_014 nv3_transfer2memory; diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 24480624d..1954724d1 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_015 nv3_stretched_image_from_cpu; diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index b45ef8968..7c669a1e5 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_017 nv3_d3d5_tri; diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 23e09904f..bbf29694f 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index e6533627d..f40924deb 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_01C nv3_image_in_memory; diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index e05ddc1eb..ded63ece2 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -16,14 +16,14 @@ */ #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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/rom.h> // DEPENDENT!!! +#include <86box/video.h> #include <86box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/nv/vid_nv3.h> /* These are the object classes AS RECOGNISED BY THE GRAPHICS HARDWARE. */ /* The drivers implement a COMPLETELY DIFFERENT SET OF CLASSES. */ diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index e336168c6..dd6c49fed 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> #include <86box/pci.h> -#include <86Box/rom.h> -#include <86Box/video.h> -#include <86Box/nv/vid_nv.h> -#include <86Box/nv/vid_nv3.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index d190c838e..949ce53be 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -17,15 +17,15 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> -#include <86Box/io.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> nv3_t* nv3; diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index de3bc2eec..6e8e2b9a5 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -24,14 +24,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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 diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index d32a7dfd7..e8beb6df7 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -17,15 +17,15 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> const device_config_t nv3_config[] = { diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index e9d8723ad..c9fd0f3aa 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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... diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 3aa1d497b..d5d17fe2d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -19,13 +19,13 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> /* Nvidia DMA Engine */ \ 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 index c62911fb2..6d8cadbf0 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -20,14 +20,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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() { diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index b2eae7c55..435225bc2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> // Functions only used in this translation unit uint32_t nv3_pfb_config0_read(); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index ad4d4469f..4a62c8cf5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -6,9 +6,9 @@ * * This file is part of the 86Box distribution. * - * NV3 pfifo (FIFO for graphics object submission) - * - * + * NV3 PFIFO (FIFO for graphics object submission) + * PIO object submission + * Gray code conversion routines * * Authors: Connor Hyde, I need a better email address ;^) * @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> // // ****** PFIFO register list START ****** diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index b1743c04d..85ad21cff 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> // Initialise the PGRAPH subsystem. diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index f7f91e87d..f67eb707b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index 392bcfd63..e555c9752 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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}, diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 95cc2afe2..6ff090716 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -22,14 +22,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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() { diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 16655779b..8dcba1b54 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -20,14 +20,14 @@ #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> +#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> #include <86box/nv/classes/vid_nv3_classes.h> // Functions only used in this translation unit diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 47db7b3b5..9a21c2e4f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> uint32_t nv3_ramfc_read(uint32_t address) { diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index b471f3a87..e56dd5c3a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -19,14 +19,14 @@ #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> +#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> /* This implements the hash that all the objects are stored within. It is used to get the offset within RAMHT of a graphics object. diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 3ba6b9d7a..2104868c6 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> uint32_t nv3_ramro_read(uint32_t address) { diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 366f2510f..aa5942ad8 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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[] = { diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index 6709c92a2..db6e78a59 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#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[] = { diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index c26df9b40..5732ec144 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -19,14 +19,14 @@ #include #include #include -#include <86Box/86box.h> -#include <86Box/device.h> -#include <86Box/mem.h> +#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> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> // PIO Method Submission diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index af17bd3a7..420d209b4 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -21,11 +21,11 @@ #include #include #include -#include <86Box/86box.h> +#include <86box/86box.h> #ifndef RELEASE_BUILD -#include <86Box/device.h> +#include <86box/device.h> #endif -#include <86Box/log.h> +#include <86box/log.h> // Common logging From f8c2c604d8f639ef82eb675e634ea23d860c9217 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 14 Feb 2025 00:38:42 +0000 Subject: [PATCH 094/274] Start working on the notifier engine and fix thecontext selection for cache1 object submission. --- .../nv3 driver init status_2025-02-10.txt | 8 +++---- .../86box/nv/classes/vid_nv3_classes.h | 24 +++++++++++++++---- src/include/86box/nv/vid_nv3.h | 21 +++++++++++++++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 14 ++++++----- src/video/nv/nv3/subsystems/nv3_user.c | 7 +++++- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt index c76e79743..24137f334 100644 --- a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt +++ b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt @@ -66,10 +66,10 @@ DrvEnableSurface RmLoadState SUCCESS 01:53 10/02/2025 NV3EnableCursor SUCCESS 01:54 10/02/2025 NV3WaitUntilFinished SUCCESS Passing 02:23 10/02/2025 - EngDeviceIoControl IOCTL 0x230408 - EngDeviceIoControl IOCTL 0x232024 - NvAllocHardware - bCreateStdPatches(?) + EngDeviceIoControl IOCTL 0x230408 SUCCESS 02:26 10/02/2025 + EngDeviceIoControl IOCTL 0x232024 SUCCESS 02:26 10/02/2025 + NvAllocHardware SUCCESS 02:29 10/02/2025 + bCreateStdPatches(?) FAILURE 02:31 10/02/2025 CHECK - NV4 vDestroyStdPatches(?) NV3_WaitForOneVerticalRefresh diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index aa23d29dc..019b04ea4 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1038,17 +1038,33 @@ typedef struct nv3_object_class_017 nv3_d3d5_alpha_control_t alpha_control; uint8_t reserved3[0xCE4]; - nv3_d3d5_coordinate_t coordinate_points[128]; // The points wer are rendering. + nv3_d3d5_coordinate_t coordinate_points[128]; // The points we are rendering. /* No placeholder needed, it really is that long. */ } nv3_d3d5_accelerated_triangle_with_zeta_buffer_t; -/* 0x19, 0x1A, 0x1B don't exist */ + +// Color and Zeta Buffer algorithm +typedef struct nv3_zeta_buffer_s +{ + nv3_color_argb_32_t color; + uint32_t zeta; // 16 bits z, 8 bits stenciul +} nv3_zeta_buffer_t; typedef struct nv3_object_class_018 -{ - +{ + nv3_class_ctx_switch_method_t set_notify_ctx_dma; + uint8_t reserved[0x100]; + uint32_t set_notify; + uint8_t reserved2[0x1FC]; + nv3_d3d5_control_out_t control_out; + nv3_d3d5_alpha_control_t alpha_control; + uint8_t reserved3[0x4F0]; + nv3_position_16_t point; + nv3_zeta_buffer_t zeta[8]; } nv3_point_with_zeta_buffer_t; +/* 0x19, 0x1A, 0x1B don't exist */ + /* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? They are making it look like a bitfield but it's hex? diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 72d6d5a3d..98e60ee72 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 5 February 2025 (STILL WORKING ON IT!!!) + * Last updated: 13 February 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -869,6 +869,25 @@ typedef struct nv3_pfb_s uint32_t rtl; // Part of the memory timings } nv3_pfb_t; +// +// DMA & Notifier Engine +// + +// Not a notification status, because it's a 16-bit enum +// C23 fixes this +#define NV3_NOTIFICATION_STATUS_DONE_OK 0x0 +#define NV3_NOTIFICATION_STATUS_IN_PROGRESS 0xFF +#define NV3_NOTIFICATION_STATUS_ERROR 0x100 + +// Core notification structure +typedef struct nv3_notification_s +{ + uint64_t nanoseconds; + uint32_t info32; + uint16_t info16; + uint16_t status; +} nv3_notification_t; + #define NV3_RMA_NUM_REGS 4 // Access the GPU from real-mode typedef struct nv3_pbus_rma_s diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 4a62c8cf5..6e7a4c453 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -504,10 +504,9 @@ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) // shift right until we have our normla number again while (mask) { - // NT4 drivers v1.29 - mask >>= 1; + // NT4 drivers v1.29 do this the other way around?? val ^= mask; - + mask >>= 1; } return val; @@ -588,7 +587,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) // Up to 128 per envytools? uint32_t channel = (addr >> NV3_OBJECT_SUBMIT_CHANNEL) & 0x7F; - uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & 0x07; + uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & (NV3_DMA_CHANNELS - 1); // first make sure there is even any cache available if (!nv3->pfifo.cache1_settings.access_enabled) @@ -661,6 +660,9 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address); + nv_log("Submitted object [PIO]: Channel %d, Subchannel %d, Method ID 0x%04x (Put Address is now %d)\n", + channel, subchannel, method_offset, nv3->pfifo.cache1_settings.put_address); + // Now we're done. Phew! } @@ -689,7 +691,7 @@ void nv3_pfifo_cache1_pull() return; // interrupt was fired, and we went to ramro } - uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? + uint32_t current_context = nv3->pfifo.cache0_settings.context[current_subchannel]; // only 1 entry for CACHE0 so basically ignore the other context entries? // Tell the CPU if we found a software method if (current_context & 0x800000) @@ -711,7 +713,7 @@ void nv3_pfifo_cache1_pull() nv3->pfifo.cache0_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; #ifndef RELEASE_BUILD - nv_log("***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE1 PULLED ****** Contextual information below\n"); + nv_log("***** OBJECT PULLED, SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - ****** Contextual information below\n"); nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); #endif diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index 5732ec144..4361abcd0 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -37,7 +37,12 @@ uint32_t nv3_user_read(uint32_t address) //todo: print out the subchannel uint8_t method_offset = (address & 0x1FFC); - nv_log("User Submission Area PIO Subchannel method_offset=0x%04x\n (Trying to read...)", method_offset); +#ifndef RELEASE_BUILD + uint8_t channel = (address - NV3_USER_START) / 0x10000; + uint8_t subchannel = ((address - NV3_USER_START)) / 0x2000 % NV3_DMA_SUBCHANNELS_PER_CHANNEL; + + nv_log("User Submission Area PIO Channel %d.%d method_offset=0x%04x\n", channel, subchannel, method_offset); +#endif // 0x10 is free CACHE1 object // TODO: THERE ARE OTHER STUFF! From 0cf469f75b7ff18dc0bccbf988c4221ac1f009cc Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 14 Feb 2025 01:38:06 +0000 Subject: [PATCH 095/274] Fix put addresses --- src/include/86box/nv/vid_nv3.h | 20 ++++++-- .../nv/nv3/classes/nv3_class_shared_methods.c | 5 ++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 49 ++++++++++--------- src/video/nv/nv3/subsystems/nv3_pgraph.c | 11 ++++- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 5 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 98e60ee72..7b037c41c 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -618,6 +618,17 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CLASS1C_MEM2IMAGE_START 0x5C0000 // class 55, 56, 62, 63? #define NV3_PGRAPH_CLASS1C_MEM2IMAGE_END 0x5C1FFF +/* + OBJECT METHODS +*/ + +// Global stuff +#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 +#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x100 // Set object ctx for dma...see nv3_dma_context_t structure +#define NV3_SET_NOTIFY 0x104 + +#define NV3_W95TXT_COLORA 0x3FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. + #define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers #define NV3_PGRAPH_REAL_END 0x5C1FFF @@ -724,9 +735,6 @@ extern const device_config_t nv3_config[]; // not done -// Master Control - - // CRTC/CIO (0x3b0-0x3df) #define NV3_CRTC_DATA_OUT 0x3C0 @@ -810,6 +818,8 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F + + /* STRUCTURES FOR THE GPU START HERE OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H @@ -1358,9 +1368,9 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); / // RAMIN functions uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel); -bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel_id, uint32_t subchannel_id); +bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel_id, uint8_t subchannel_id); #ifndef RELEASE_BUILD -void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); +void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); #endif uint32_t nv3_ramfc_read(uint32_t address); diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index dd6c49fed..39f7e2b81 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -32,4 +32,9 @@ void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj) { +} + +void nv3_notify() +{ + } \ 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 index 6e7a4c453..a6252f3b7 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -500,13 +500,13 @@ Back to sanity uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) { uint32_t mask = val >> 1; - + // shift right until we have our normla number again while (mask) { - // NT4 drivers v1.29 do this the other way around?? - val ^= mask; + // Algorithm from NT4 drivers, version 1.29 mask >>= 1; + val ^= mask; } return val; @@ -530,11 +530,11 @@ void nv3_pfifo_cache0_pull() return; // There is only one entry for cache0 - uint16_t current_channel = nv3->pfifo.cache0_settings.channel; - uint32_t current_subchannel = nv3->pfifo.cache0_entry.subchannel; + uint8_t current_channel = nv3->pfifo.cache0_settings.channel; + uint8_t current_subchannel = nv3->pfifo.cache0_entry.subchannel; uint32_t current_name = nv3->pfifo.cache0_entry.data; - uint32_t current_method = nv3->pfifo.cache0_entry.method; - + uint16_t current_method = nv3->pfifo.cache0_entry.method; + // i.e. there is no method in cache0, so we have to find the object. if (!current_method) { @@ -543,6 +543,7 @@ void nv3_pfifo_cache0_pull() } uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? + uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; // Tell the CPU if we found a software method if (current_context & 0x800000) @@ -594,6 +595,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) { oh_shit = true; oh_shit_reason = nv3_runout_reason_no_cache_available; + new_address |= (nv3_runout_reason_no_cache_available << NV3_PFIFO_RUNOUT_RAMIN_ERR); + } // Check if runout is full @@ -627,7 +630,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) { // Cache reassignment required if (!nv3->pfifo.cache_reassignment - || (nv3->pfifo.cache0_settings.get_address != nv3->pfifo.cache0_settings.get_address)) + || (nv3->pfifo.cache1_settings.get_address != nv3->pfifo.cache1_settings.get_address)) { oh_shit = true; oh_shit_reason = nv3_runout_reason_no_cache_available; @@ -654,13 +657,13 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address) + 1; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# - next_put_address &= NV3_PFIFO_CACHE1_SIZE_REV_C; + next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); else - next_put_address &= NV3_PFIFO_CACHE1_SIZE_REV_AB; + next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); - nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address); + nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; - nv_log("Submitted object [PIO]: Channel %d, Subchannel %d, Method ID 0x%04x (Put Address is now %d)\n", + nv_log("Submitted object [PIO]: Channel %d.%d, Method ID 0x%04x (Put Address is now %d)\n", channel, subchannel, method_offset, nv3->pfifo.cache1_settings.put_address); // Now we're done. Phew! @@ -679,25 +682,27 @@ void nv3_pfifo_cache1_pull() uint32_t get_address = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably - uint16_t current_channel = nv3->pfifo.cache1_settings.channel; - uint32_t current_subchannel = nv3->pfifo.cache1_entries[get_address].subchannel; + uint8_t current_channel = nv3->pfifo.cache1_settings.channel; + uint8_t current_subchannel = nv3->pfifo.cache1_entries[get_address].subchannel; uint32_t current_name = nv3->pfifo.cache1_entries[get_address].data; - uint32_t current_method = nv3->pfifo.cache1_entries[get_address].method; + uint16_t current_method = nv3->pfifo.cache1_entries[get_address].method; - // i.e. there is no method in cache0, so we have to find the object. + // i.e. there is no method in cache1, so we have to find the object. if (!current_method) { if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) return; // interrupt was fired, and we went to ramro } - uint32_t current_context = nv3->pfifo.cache0_settings.context[current_subchannel]; // only 1 entry for CACHE0 so basically ignore the other context entries? + uint32_t current_context = nv3->pfifo.cache1_settings.context[current_subchannel]; // get the current subchannel + + uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; // Tell the CPU if we found a software method if (current_context & 0x800000) { - nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); } @@ -705,12 +710,12 @@ void nv3_pfifo_cache1_pull() uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_address) + 1; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# - next_get_address &= NV3_PFIFO_CACHE1_SIZE_REV_C; + next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); else - next_get_address &= NV3_PFIFO_CACHE1_SIZE_REV_AB; + next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); // Is this needed? - nv3->pfifo.cache0_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; + nv3->pfifo.cache1_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; #ifndef RELEASE_BUILD nv_log("***** OBJECT PULLED, SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - ****** Contextual information below\n"); diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 85ad21cff..212980c8a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -468,7 +468,16 @@ void nv3_pgraph_vblank_start(svga_t* svga) nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); } -void nv3_pgraph_submit() +/* Arbitrates graphics object submission to the right object types */ +void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context) { + // class id can be derived from the context but we debug log it before we get here + switch (method) + { + // This method is how we figure out which methods exist. + case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: + nv_log("Hi, I'm an NV []"); + 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 index 8dcba1b54..18538cd91 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -347,7 +347,7 @@ bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) } // THIS IS THE MOST IMPORTANT FUNCTION! -bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint32_t channel, uint32_t subchannel) +bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, uint8_t subchannel) { // TODO: WRITE IT!!! // Set the number of entries to search based on the ramht size (2*(size+1)) From 8cf57fdc14a779f0defc3d16faa944463bb9f13d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 21 Feb 2025 22:57:04 +0000 Subject: [PATCH 096/274] hash lookup fixes --- .../nv3 driver init status_2025-02-10.txt | 26 ++++++---------- src/include/86box/nv/vid_nv3.h | 2 ++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 12 +++---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 4 +-- src/video/nv/nv3/subsystems/nv3_pramin.c | 31 ++++++++++++++----- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 4 +-- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt index 24137f334..b5643a2ea 100644 --- a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt +++ b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt @@ -46,7 +46,7 @@ DrvEnablePDEV SUCCESS 23:28 09/02/2025 EngDeviceIoControl IOCTL 0x232044 (CHECK mini) SUCCESS (eax=0) DrvCompletePDEV SUCCESS 23:52 09/02/2025 DrvEnableSurface - bEnableHardware call + bEnableHardware call SUCCESS 22:36 13/02/2025 EngCreateSemaphore call #1 csCrtc SUCCESS 00:55 10/02/2025 EngCreateSemaphore call #2 csFifo SUCCESS 00:57 10/02/2025 EngDeviceIoControl IOCTL 0x230460 SUCCESS 00:57 10/02/2025 @@ -54,8 +54,8 @@ DrvEnableSurface NvAllocRoot SUCCESS 01:03 10/02/2025 NvAllocDevice SUCCESS 01:04 10/02/2025 NV3/NV4 architecture check SUCCESS 01:14 10/02/2025 - bAssertModeHardware call (bEnable=1) FAILURE - HANG/LOOP - EngDeviceIoControl IOCTL 0x23040C] FAILURE 01:27 10/02/2025 + bAssertModeHardware call (bEnable=1) SUCCESS Passing starting with build at 02:23 10/02/2025 + EngDeviceIoControl IOCTL 0x23040C] SUCCESS Passing starting with build at 02:23 10/02/2025 nv3_mini NVStartIO ioctlcode=0x23040C NVSetMode NV3SetMode SUCCESS 01:53 10/02/2025 @@ -65,19 +65,19 @@ DrvEnableSurface UpdateArbitrationSettings SUCCESS 01:52 10/02/2025 RmLoadState SUCCESS 01:53 10/02/2025 NV3EnableCursor SUCCESS 01:54 10/02/2025 - NV3WaitUntilFinished SUCCESS Passing 02:23 10/02/2025 + NV3WaitUntilFinished SUCCESS 02:23 10/02/2025 EngDeviceIoControl IOCTL 0x230408 SUCCESS 02:26 10/02/2025 EngDeviceIoControl IOCTL 0x232024 SUCCESS 02:26 10/02/2025 NvAllocHardware SUCCESS 02:29 10/02/2025 - bCreateStdPatches(?) FAILURE 02:31 10/02/2025 - CHECK - NV4 - vDestroyStdPatches(?) - NV3_WaitForOneVerticalRefresh - EngDeviceIoControl IOCTL 0x230410 + bCreateStdPatches(?) SUCCESS (EAX=1!!!) 22:24 13/02/2025 + CHECK - NV4 N/A + vDestroyStdPatches(?) N/A + NV3_WaitForOneVerticalRefresh SUCCESS + EngDeviceIoControl IOCTL 0x230410 SUCCESS SET UP CORRECT FUNCTION POINTERS - + Indirect call (call dword [edi]) to NV3_WaitWhileGraphicsEngineBusy HANG 22:14 16/02/2025 _heap_init call bEnableOffscreenHeap call bEnablePointer call @@ -87,10 +87,4 @@ DrvEnableSurface EngCreateBitmap call EngAssociateBitmap call - - - - DrvDisableSurface: ONLY IN THE CASE OF FAILURE - - diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 7b037c41c..58ca3b623 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1446,6 +1446,8 @@ void nv3_pgraph_init(); uint32_t nv3_pgraph_read(uint32_t address); void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); +void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context); + // NV3 PFIFO void nv3_pfifo_init(); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index a6252f3b7..ca5694af3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -680,14 +680,14 @@ void nv3_pfifo_cache1_pull() if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) return; - uint32_t get_address = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably + uint32_t get_index = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably uint8_t current_channel = nv3->pfifo.cache1_settings.channel; - uint8_t current_subchannel = nv3->pfifo.cache1_entries[get_address].subchannel; - uint32_t current_name = nv3->pfifo.cache1_entries[get_address].data; - uint16_t current_method = nv3->pfifo.cache1_entries[get_address].method; + uint8_t current_subchannel = nv3->pfifo.cache1_entries[get_index].subchannel; + uint32_t current_name = nv3->pfifo.cache1_entries[get_index].data; + uint16_t current_method = nv3->pfifo.cache1_entries[get_index].method; - // i.e. there is no method in cache1, so we have to find the object. + // NV_ROOT if (!current_method) { if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) @@ -707,7 +707,7 @@ void nv3_pfifo_cache1_pull() } // start by incrementing - uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_address) + 1; + uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_index) + 1; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 212980c8a..1c5750596 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -449,7 +449,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // Addresses should be aligned to 4 bytes. uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - nv_log("PGRAPH Context Cache Write (Entry=%04x Value=%04x)\n", entry, value); + nv_log("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); nv3->pgraph.context_cache[entry] = value; } } @@ -477,7 +477,7 @@ void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t s { // This method is how we figure out which methods exist. case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("Hi, I'm an NV []"); + nv_log("Hi, I'm an NVidia object :)\n"); 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 index 18538cd91..e8452d870 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -352,13 +352,32 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // TODO: WRITE IT!!! // Set the number of entries to search based on the ramht size (2*(size+1)) // Not a switch statement in case newer gpus have larger ramins - uint32_t bucket_entries = 2 * (((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03) + 1); + + uint32_t bucket_entries = 2; + + switch (nv3->pfifo.ramht_config) + { + case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: + // stays as is + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: + bucket_entries = 4; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: + bucket_entries = 8; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: + bucket_entries = 16; + break; + + } // Calculate the address in the hashtable uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; - uint32_t ramht_cur_address = ramht_base; + uint32_t ramht_cur_address = ramht_base + (nv3_ramht_hash(name, channel)) * bucket_entries * 8; - nv_log("Beginning search for graphics object at RAMHT base=0x%04x, Cache%d, channel=%d, subchannel=%d)", name, cache_num, channel, subchannel); + nv_log("Beginning search for graphics object at RAMHT base=0x%04x, name=0x%08x, Cache%d, channel=%d.%d)\n", + ramht_cur_address, name, cache_num, channel, subchannel); bool found_object = false; @@ -385,19 +404,17 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!found_object) { + nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; + if (!cache_num) { - nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; - //It turns itself off on failure, the drivers turn it back on nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; } else { - nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; - //It turns itself off on failure, the drivers turn it back on nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index e56dd5c3a..00ef8e046 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -35,11 +35,11 @@ It is used to get the offset within RAMHT of a graphics object. uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) { // convert the name to an array of bytes - uint8_t* hash_bytes = (uint8_t*)name; + uint8_t* hash_bytes = (uint8_t*)&name; // is this the right endianness? uint32_t hash = (hash_bytes[0] ^ hash_bytes[1] ^ hash_bytes[2] ^ hash_bytes[3] ^ (uint8_t)channel); - nv_log("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, name, channel); + nv_log("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, (hash/8), name, channel); return hash; } From 71bdf8d081dd07629d3968b4a9aeaf487deb7385 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 26 Feb 2025 23:55:49 +0000 Subject: [PATCH 097/274] even more fixes to hash lookup --- src/include/86box/nv/vid_nv3.h | 4 +++- src/video/nv/nv3/subsystems/nv3_pbus.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 10 ++++++++- src/video/nv/nv3/subsystems/nv3_pramin.c | 22 ++++++++++--------- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 7 +++--- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 58ca3b623..334c8f615 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 13 February 2025 (STILL WORKING ON IT!!!) + * Last updated: 26 February 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -41,6 +41,8 @@ extern const device_config_t nv3_config[]; #define NV3_DMA_CHANNELS 8 #define NV3_DMA_SUBCHANNELS_PER_CHANNEL 8 +#define NV3_DMA_CHANNELS_TOTAL 0x7F // This is also used somewhere despite there only being 8*8 = 64 channels + #define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1 // The amount by which we have to ration out the memory clock because it's not fast enough... // Multiply by this value to get the real clock speed. #define NV3_LAST_VALID_GRAPHICS_OBJECT_ID 0x1F diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index c9fd0f3aa..2594e0739 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -127,8 +127,8 @@ void nv3_pbus_write(uint32_t address, uint32_t value) uint8_t nv3_pbus_rma_read(uint16_t addr) { addr &= 0xFF; - uint32_t real_final_address; - uint8_t ret; + uint32_t real_final_address = 0x0; + uint8_t ret = 0x0; switch (addr) { diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index ca5694af3..55b961c96 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -290,6 +290,11 @@ uint32_t nv3_pfifo_read(uint32_t address) return ret; } +void nv3_pfifo_trigger_dma_if_required() +{ + +} + void nv3_pfifo_write(uint32_t address, uint32_t value) { // before doing anything, check the subsystem enablement @@ -481,6 +486,9 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x", ctx_entry_id, value); } + + /* Trigger DMA for notifications if we need to */ + nv3_pfifo_trigger_dma_if_required(); } /* @@ -690,7 +698,7 @@ void nv3_pfifo_cache1_pull() // NV_ROOT if (!current_method) { - if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) + if (!nv3_ramin_find_object(current_name, 1, current_channel, current_subchannel)) return; // interrupt was fired, and we went to ramro } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index e8452d870..25f6b36e1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -114,7 +114,7 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { val = vram_32bit[addr]; - nv_log("Read dword from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); } return val; @@ -134,7 +134,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) // 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - uint32_t val32 = 0x00; + uint32_t val32 = (uint32_t)val; if (!nv3_ramin_arbitrate_write(addr, val32)) { @@ -160,7 +160,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 1; // what - uint32_t val32 = 0x00; + uint32_t val32 = (uint32_t)val; if (!nv3_ramin_arbitrate_write(addr, val32)) { @@ -186,9 +186,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what - uint32_t val32 = 0x00; - - if (!nv3_ramin_arbitrate_write(addr, val32)) + if (!nv3_ramin_arbitrate_write(addr, val)) { vram_32bit[addr] = val; nv_log("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); @@ -354,8 +352,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // Not a switch statement in case newer gpus have larger ramins uint32_t bucket_entries = 2; + uint8_t ramht_size = (nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03; - switch (nv3->pfifo.ramht_config) + switch (ramht_size) { case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: // stays as is @@ -374,7 +373,10 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // Calculate the address in the hashtable uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; - uint32_t ramht_cur_address = ramht_base + (nv3_ramht_hash(name, channel)) * bucket_entries * 8; + + // This is certainly wrong. But the objects seem to be written to 4600? So I just multiply it by 80 to multiply the final address by 10. + // Why does this work? + uint32_t ramht_cur_address = ramht_base + (nv3_ramht_hash(name, channel) * bucket_entries * 8); nv_log("Beginning search for graphics object at RAMHT base=0x%04x, name=0x%08x, Cache%d, channel=%d.%d)\n", ramht_cur_address, name, cache_num, channel, subchannel); @@ -382,8 +384,8 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u bool found_object = false; // set up some variables - uint32_t found_obj_name; - nv3_ramin_context_t obj_context_struct; + uint32_t found_obj_name = 0x00; + nv3_ramin_context_t obj_context_struct = {0}; for (uint32_t bucket_entry = 0; bucket_entry < bucket_entries; bucket_entry++) { diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index 00ef8e046..e09f80e83 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -32,13 +32,14 @@ It is used to get the offset within RAMHT of a graphics object. */ + uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) { - // convert the name to an array of bytes - uint8_t* hash_bytes = (uint8_t*)&name; + // the official nvidia hash algorithm, tweaked for readability + uint32_t hash = ((name ^ (name >> 8) ^ (name >> 16) ^ (name >> 24)) & 0xFF) ^ (channel & NV3_DMA_CHANNELS_TOTAL); + // is this the right endianness? - uint32_t hash = (hash_bytes[0] ^ hash_bytes[1] ^ hash_bytes[2] ^ hash_bytes[3] ^ (uint8_t)channel); nv_log("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, (hash/8), name, channel); return hash; } From 15ebc48383aa8160d12c47417d96dc0968c0fd6e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 3 Mar 2025 01:27:50 +0000 Subject: [PATCH 098/274] Raise the correct cache error on DEBUG_0 if lookup fails --- src/video/nv/nv3/subsystems/nv3_pramin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 25f6b36e1..ece982f0e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -406,16 +406,16 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!found_object) { - nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; - if (!cache_num) { + nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; } else { + nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; From c51127af08fddda10d44cf6f9fa0d5dd2f432365 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 5 Mar 2025 01:43:18 +0000 Subject: [PATCH 099/274] Fix context dirty state, start working on DMA and notifiers, fix target node. --- src/include/86box/nv/vid_nv3.h | 28 ++++--- src/video/nv/nv3/subsystems/nv3_pfifo.c | 98 +++++++++++++++--------- src/video/nv/nv3/subsystems/nv3_pramin.c | 12 +-- 3 files changed, 84 insertions(+), 54 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 334c8f615..34356c5ee 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -256,7 +256,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_RUNOUT_GET 0x2420 #define NV3_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 -#define NV3_PFIFO_RUNOUT_RAMIN_ERR 28 +#define NV3_PFIFO_RUNOUT_RAMIN_ERR 28 // bit to or with #define NV3_PFIFO_CACHE0_SIZE 1 // This is for software-injected notified only! #define NV3_PFIFO_CACHE1_SIZE_REV_AB 32 @@ -264,7 +264,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 -#define NV3_PFIFO_CACHE0_PUSH_ACCESS 0x3000 +#define NV3_PFIFO_CACHE0_DMA_PUSH0 0x3000 #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 @@ -275,8 +275,8 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE 4 #define NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD 8 -#define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY 0x3050 -#define NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY_BOOL 4 // 1=dirty 0=clean +#define NV3_PFIFO_CACHE0_PULLER_CTX_STATE 0x3050 +#define NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 #define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit // Current channel context - cache1 @@ -285,7 +285,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_METHOD 0x3100 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 -#define NV3_PFIFO_CACHE1_PUSH_ACCESS 0x3200 +#define NV3_PFIFO_CACHE1_DMA_PUSH0 0x3200 #define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 @@ -298,17 +298,21 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 #define NV3_PFIFO_CACHE1_DMA_CONFIG_2 0x3228 #define NV3_PFIFO_CACHE1_DMA_CONFIG_3 0x322C +#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE 0 // The type of bus we are sending over +#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_PCI 0x02 // The type of bus we are sending over +#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_AGP 0x03 // The type of bus we are sending over + // Why does a gpu need its own translation lookaside buffer and pagetable format. Are they crazy #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULLER_CONTROL 0x3240 +#define NV3_PFIFO_CACHE1_PULL0 0x3240 //todo: merge stuff -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD 8 // 0=software, 1=hardware -#define NV3_PFIFO_CACHE1_PULLER_STATE1 0x3250 -#define NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY 4 +#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware +#define NV3_PFIFO_CACHE1_PULLER_CTX_STATE 0x3250 +#define NV3_PFIFO_CACHE1_PULLER_CTX_STATE_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 #define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 @@ -920,7 +924,7 @@ typedef struct nv3_pbus_s typedef struct nv3_pfifo_cache_s { - bool access_enabled; // Can we even access this cache? + bool dma_push0; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. uint8_t channel; // The DMA channel ID of this cache. diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 55b961c96..00e11895d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -21,6 +21,7 @@ #include #include <86box/86box.h> #include <86box/device.h> +#include <86box/dma.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/rom.h> // DEPENDENT!!! @@ -43,27 +44,27 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, { NV3_PFIFO_CACHE_REASSIGNMENT, "PFIFO - Allow Cache Channel Reassignment", NULL, NULL }, { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller Control", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller Control"}, - { NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULLER_CONTROL, "PFIFO - Cache1 Puller State0", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULLER_STATE1, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE0_PUSH_ACCESS, "PFIFO - Cache0 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE1_PUSH_ACCESS, "PFIFO - Cache1 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller Control"}, + { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, + { NV3_PFIFO_CACHE0_DMA_PUSH0, "PFIFO - Cache0 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_DMA_PUSH0, "PFIFO - Cache1 Access", NULL, NULL, }, { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 DMA Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 DMA Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, - { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, - { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get MUST TRIGGER DMA NOW TO OBTAIN ENTRY", NULL, NULL }, - { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, - { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put MUST TRIGGER DMA NOW TO INSERT ENTRY", NULL, NULL }, + { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get", NULL, NULL }, + { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get", NULL, NULL }, + { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put", NULL, NULL }, + { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put", NULL, NULL }, //Cache1 exclusive stuff { NV3_PFIFO_CACHE1_DMA_CONFIG_0, "PFIFO - Cache1 DMA Config0"}, { NV3_PFIFO_CACHE1_DMA_CONFIG_1, "PFIFO - Cache1 DMA Config1"}, { NV3_PFIFO_CACHE1_DMA_CONFIG_2, "PFIFO - Cache1 DMA Config2"}, { NV3_PFIFO_CACHE1_DMA_CONFIG_3, "PFIFO - Cache1 DMA Config3"}, - { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status"}, + { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status - PROBABLY TRIGGERING DMA"}, { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA Translation Lookaside Buffer - Pagetable Base"}, { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA Status"}, { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA Status"}, @@ -147,20 +148,20 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE0_PULLER_CONTROL: ret = nv3->pfifo.cache0_settings.puller_control; break; - case NV3_PFIFO_CACHE1_PULLER_CONTROL: + case NV3_PFIFO_CACHE1_PULL0: ret = nv3->pfifo.cache1_settings.puller_control; break; - case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: - ret = nv3->pfifo.cache0_settings.context_is_dirty; + case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: + ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; break; - case NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY: - ret = nv3->pfifo.cache1_settings.context_is_dirty; + case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: + ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; break; - case NV3_PFIFO_CACHE0_PUSH_ACCESS: - ret = nv3->pfifo.cache0_settings.access_enabled; + case NV3_PFIFO_CACHE0_DMA_PUSH0: + ret = nv3->pfifo.cache0_settings.dma_push0; break; - case NV3_PFIFO_CACHE1_PUSH_ACCESS: - ret = nv3->pfifo.cache1_settings.access_enabled; + case NV3_PFIFO_CACHE1_DMA_PUSH0: + ret = nv3->pfifo.cache1_settings.dma_push0; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: ret = nv3->pfifo.cache0_settings.channel; @@ -219,7 +220,10 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.cache1_settings.dma_address; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_3: - ret = nv3->pfifo.cache1_settings.dma_target_node; + if (nv3->nvbase.bus_generation == nv_bus_pci) + return NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_PCI; + else + return NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_AGP; break; case NV3_PFIFO_CACHE1_DMA_STATUS: ret = nv3->pfifo.cache1_settings.dma_status; @@ -292,7 +296,32 @@ uint32_t nv3_pfifo_read(uint32_t address) void nv3_pfifo_trigger_dma_if_required() { + // Not a thing for cache0 + + bool cache1_dma = false; + /* Check that DMA is enabled */ + if (nv3->pfifo.cache1_settings.dma_state + && nv3->pfifo.cache1_settings.dma_enabled) + { + uint32_t bytes_to_send = nv3->pfifo.cache1_settings.dma_length; + uint32_t where_to_send = nv3->pfifo.cache1_settings.dma_address; + uint32_t target_node = nv3->pfifo.cache1_settings.dma_target_node; //2=pci, 3=agp. What does this even do + + /* Pagetable information */ + uint32_t tlb_pt_base = nv3->pfifo.cache1_settings.dma_tlb_pt_base; + uint32_t tlb_pt_entry = nv3->pfifo.cache1_settings.dma_tlb_pte; + uint32_t tlb_pt_tag = nv3->pfifo.cache1_settings.dma_tlb_tag; // 0xFFFFFFFF usually? + + /* PUSH - System to GPU (?) */ + if (nv3->pfifo.cache1_settings.dma_push0) + { + + } + + /* PULL - GPU to System */ + nv_log("Initiating NV to System DMA - Probably we are trying to notify"); + } } void nv3_pfifo_write(uint32_t address, uint32_t value) @@ -396,20 +425,20 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CACHE0_PULLER_CONTROL: nv3->pfifo.cache0_settings.puller_control = value; // 8bits meaningful break; - case NV3_PFIFO_CACHE1_PULLER_CONTROL: + case NV3_PFIFO_CACHE1_PULL0: nv3->pfifo.cache1_settings.puller_control = value; // 8bits meaningful break; - case NV3_PFIFO_CACHE0_PULLER_CTX_IS_DIRTY: - nv3->pfifo.cache0_settings.context_is_dirty = value; + case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: + nv3->pfifo.cache0_settings.context_is_dirty = (value >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; - case NV3_PFIFO_CACHE1_PULLER_CTX_IS_DIRTY: - nv3->pfifo.cache1_settings.context_is_dirty = value; + case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: + nv3->pfifo.cache1_settings.context_is_dirty = (value >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; - case NV3_PFIFO_CACHE0_PUSH_ACCESS: - nv3->pfifo.cache0_settings.access_enabled = value; + case NV3_PFIFO_CACHE0_DMA_PUSH0: + nv3->pfifo.cache0_settings.dma_push0 = value; break; - case NV3_PFIFO_CACHE1_PUSH_ACCESS: - nv3->pfifo.cache1_settings.access_enabled = value; + case NV3_PFIFO_CACHE1_DMA_PUSH0: + nv3->pfifo.cache1_settings.dma_push0 = value; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: nv3->pfifo.cache0_settings.channel = value; @@ -435,9 +464,6 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) case NV3_PFIFO_CACHE1_DMA_CONFIG_2: nv3->pfifo.cache1_settings.dma_address = value; break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_3: - nv3->pfifo.cache1_settings.dma_target_node = value; - break; case NV3_PFIFO_CACHE1_DMA_STATUS: nv3->pfifo.cache1_settings.dma_status = value; break; @@ -484,7 +510,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; nv3->pfifo.cache1_settings.context[ctx_entry_id] = value; - nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x", ctx_entry_id, value); + nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, value); } /* Trigger DMA for notifications if we need to */ @@ -599,7 +625,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & (NV3_DMA_CHANNELS - 1); // first make sure there is even any cache available - if (!nv3->pfifo.cache1_settings.access_enabled) + if (!nv3->pfifo.cache1_settings.dma_push0) { oh_shit = true; oh_shit_reason = nv3_runout_reason_no_cache_available; @@ -681,7 +707,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) void nv3_pfifo_cache1_pull() { // Do nothing if PFIFO CACHE1 is disabled - if (!nv3->pfifo.cache1_settings.puller_control & (1 >> NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED)) + if (!nv3->pfifo.cache1_settings.puller_control & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) return; // Do nothing if there is nothing in cache1 to pull diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index ece982f0e..b04d261ee 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -416,9 +416,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u else { nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; - nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; + nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; } nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); @@ -454,7 +454,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!cache_num) nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; else - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_HASH_FAILURE; + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; // Caches store all the subchannels for our current dma channel and basically get stale every context switch // Also we have to check that a osftware object didn't end up in here... @@ -476,8 +476,8 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u } else { - nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; } // It's an error but it isn't lol @@ -490,7 +490,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!cache_num) nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; else - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; } // Ok we found it. Lol From a3e41c611e5f4194e48856650263351362dc83d1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 10 Mar 2025 17:58:17 +0000 Subject: [PATCH 100/274] fix parameter names --- doc/nvidia_notes/NV3 DMA Engine.txt | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 49 ++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt index 50d34cfa9..282868503 100644 --- a/doc/nvidia_notes/NV3 DMA Engine.txt +++ b/doc/nvidia_notes/NV3 DMA Engine.txt @@ -23,7 +23,7 @@ CACHE1_DMA3 - Bus address space (Area BAR0 mapped to? Or bar1?) TO START: To set up DMA for for Cache1 Puller: CACHE1_PULL0 -> 1, changes to 0 when done -To set up DMA Cache1 Push: CACHE1_PULL0 -> 1, changes to 0 when done +To set up DMA Cache1 Push: CACHE1_PUsh0 -> 1, changes to 0 when done Set CACHES to 1 GO: Set DMA0 to 1 diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 00e11895d..e78c766b4 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -610,7 +610,7 @@ void nv3_pfifo_context_switch(uint32_t new_channel) // NV_USER writes go here! // Pushes graphics objects into cache1 -void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) +void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) { bool oh_shit = false; // RAMRO needed nv3_ramin_ramro_reason oh_shit_reason = 0x00; // It's all good for now @@ -648,7 +648,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); } - // 0x0 is used for the context + // 0x0 is used for creating the object. if (method_offset > 0 && method_offset < 0x100) { // Reserved NVIDIA Objects @@ -685,7 +685,46 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) uint32_t current_put_address = nv3->pfifo.cache1_settings.put_address >> 2; nv3->pfifo.cache1_entries[current_put_address].subchannel = subchannel; nv3->pfifo.cache1_entries[current_put_address].method = method_offset; - nv3->pfifo.cache1_entries[current_put_address].data = val; + nv3->pfifo.cache1_entries[current_put_address].data = object_name; + + /* + // I think we have to do this on PIO submission. Maybe not? + uint32_t hash = nv3_ramht_hash(object_name, channel); + uint32_t bucket_entries = 2; + uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; + + uint8_t ramht_size = (nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03; + + switch (ramht_size) + { + case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: + // stays as is + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: + bucket_entries = 4; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: + bucket_entries = 8; + break; + case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: + bucket_entries = 16; + break; + + } + + uint32_t ramin_address = ramht_base + hash * bucket_entries * 8; + + if (method_offset == 0) + { + nv3_ramin_write32(ramin_address, object_name, nv3); + nv3_ramin_write32(ramin_address + 0x04, nv3->pfifo.cache1_settings.context[subchannel], nv3); + } + else + { + // MAYBE + nv3_ramin_write32(ramin_address + method_offset, object_name, nv3); + } +*/ // now we have to recalculate the cache1 put address uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address) + 1; @@ -697,8 +736,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val) nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; - nv_log("Submitted object [PIO]: Channel %d.%d, Method ID 0x%04x (Put Address is now %d)\n", - channel, subchannel, method_offset, nv3->pfifo.cache1_settings.put_address); + nv_log("Submitted object [PIO]: Channel %d.%d, Object Name 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", + channel, subchannel, object_name, method_offset, nv3->pfifo.cache1_settings.put_address); // Now we're done. Phew! } From 0568028730d853f761f7b1c7499dbd4908da98ad Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 10 Mar 2025 21:19:26 +0000 Subject: [PATCH 101/274] get rid of stupid bad code that had no understanding of dma --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 39 ------------------------- 1 file changed, 39 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index e78c766b4..1ffb1023c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -687,45 +687,6 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) nv3->pfifo.cache1_entries[current_put_address].method = method_offset; nv3->pfifo.cache1_entries[current_put_address].data = object_name; - /* - // I think we have to do this on PIO submission. Maybe not? - uint32_t hash = nv3_ramht_hash(object_name, channel); - uint32_t bucket_entries = 2; - uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; - - uint8_t ramht_size = (nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03; - - switch (ramht_size) - { - case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - // stays as is - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - bucket_entries = 4; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - bucket_entries = 8; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - bucket_entries = 16; - break; - - } - - uint32_t ramin_address = ramht_base + hash * bucket_entries * 8; - - if (method_offset == 0) - { - nv3_ramin_write32(ramin_address, object_name, nv3); - nv3_ramin_write32(ramin_address + 0x04, nv3->pfifo.cache1_settings.context[subchannel], nv3); - } - else - { - // MAYBE - nv3_ramin_write32(ramin_address + method_offset, object_name, nv3); - } -*/ - // now we have to recalculate the cache1 put address uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address) + 1; From 363b2c2f4d9fa240cb09b49f386e2a2fc8c6ad88 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 01:12:41 +0000 Subject: [PATCH 102/274] I don't even know why using a lookup table fixes the object submission to be honest but the objects its submitting alos look completely screwed up --- doc/nvidia_notes/NV3 DMA Engine.txt | 16 +++++++++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 38 ++++++++++++++++++------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt index 282868503..6cae9d131 100644 --- a/doc/nvidia_notes/NV3 DMA Engine.txt +++ b/doc/nvidia_notes/NV3 DMA Engine.txt @@ -27,3 +27,19 @@ To set up DMA Cache1 Push: CACHE1_PUsh0 -> 1, changes to 0 when done Set CACHES to 1 GO: Set DMA0 to 1 + +***** Implementation in Driver ****** + +You can dma to "localvidmem:", "sysvidmem:" or "sysmem:", this is represented by a driver + +CAUSE OF FAILURE: +the pfifo is never free because it never processes the submitted objects +which means that the FIFO is never free +which means that the drivers spin forever waiting for the fifo to be free + +DMA_OBJECT STRUCTURE IN RESOURCE MANAGER: +0x328: Valid +0x34c: base address? +0x374: actually do the transfer + + diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1ffb1023c..0037d3e01 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -517,15 +517,33 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv3_pfifo_trigger_dma_if_required(); } + /* https://en.wikipedia.org/wiki/Gray_code WHY?????? IT'S NOT A TELEGRAPH IT'S A GPU????? Convert from a normal number to a total insanity number which is only used in PFIFO CACHE1 for ungodly and totally unknowable reasons + +I decided to use a lookup table to save everyone's time, also the numbers generated from the function +that existed here before didn't make any sense */ + +#define NV3_GRAY_TABLE_NUM_ENTRIES 64 + +uint8_t nv3_pfifo_cache1_gray_code_table[NV3_GRAY_TABLE_NUM_ENTRIES] = { + 0b000000, 0b000001, 0b000011, 0b000010, 0b000110, 0b000111, 0b000101, 0b000100, + 0b001100, 0b001101, 0b001111, 0b001110, 0b001010, 0b001011, 0b001001, 0b001000, + 0b011000, 0b011001, 0b011011, 0b011010, 0b011110, 0b011111, 0b011101, 0b011100, + 0b010100, 0b010101, 0b010111, 0b010110, 0b010010, 0b010011, 0b010001, 0b010000, + 0b110000, 0b110001, 0b110011, 0b110010, 0b110110, 0b110111, 0b110101, 0b110100, + 0b111100, 0b111101, 0b111111, 0b111110, 0b111010, 0b111011, 0b111001, 0b111000, + 0b101000, 0b101001, 0b101011, 0b101010, 0b101110, 0b101111, 0b101101, 0b101100, + 0b100100, 0b100101, 0b100111, 0b100110, 0b100010, 0b100011, 0b100001, 0b100000 +}; + uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val) { - return (val) ^ (val >> 1); + return nv3_pfifo_cache1_gray_code_table[val]; } /* @@ -533,17 +551,14 @@ Back to sanity */ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) { - uint32_t mask = val >> 1; - - // shift right until we have our normla number again - while (mask) + /* Is this a good idea? */ + for (uint32_t i = 0; i < NV3_GRAY_TABLE_NUM_ENTRIES; i++) { - // Algorithm from NT4 drivers, version 1.29 - mask >>= 1; - val ^= mask; + if (nv3_pfifo_cache1_gray_code_table[i] == val) + return i; } - return val; + return 0x00; } // Submits graphics objects INTO cache0 @@ -688,7 +703,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) nv3->pfifo.cache1_entries[current_put_address].data = object_name; // now we have to recalculate the cache1 put address - uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address) + 1; + uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address); + next_put_address++; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); @@ -754,7 +770,7 @@ void nv3_pfifo_cache1_pull() #ifndef RELEASE_BUILD nv_log("***** OBJECT PULLED, SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - ****** Contextual information below\n"); - nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); + nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)¤t_context); #endif //Todo: finish it From 4fdb522792218009198555f4d6538f6d30d4ff14 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 02:02:33 +0000 Subject: [PATCH 103/274] Implement put/get registers. Also, bit numbering is hard. --- src/include/86box/nv/vid_nv3.h | 13 ++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 104 ++++++++++++++--------- src/video/nv/nv3/subsystems/nv3_pramin.c | 14 +-- 3 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 34356c5ee..56eae4dfa 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 26 February 2025 (STILL WORKING ON IT!!!) + * Last updated: 11 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -977,7 +977,7 @@ typedef struct nv3_pfifo_s uint32_t runout_get; // 8:3 if RAMRO=512b, otherwise 12:3 // Cache stuff - uint32_t cache_reassignment; // Enable automatic reassignment into CACHE0? + uint32_t cache_reassignment; // Allow context switching nv3_pfifo_cache_t cache0_settings; nv3_pfifo_cache_t cache1_settings; @@ -1213,13 +1213,12 @@ typedef struct nv3_ramin_context_s struct { - bool reserved : 1; - uint8_t channel : 7; - bool is_rendering : 1; - uint8_t class_id : 7; uint16_t ramin_offset; + uint8_t class_id : 7; + bool is_rendering : 1; + uint8_t channel : 7; + bool reserved : 1; }; - }; } nv3_ramin_context_t; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 0037d3e01..273e0a2e3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -201,8 +201,17 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = ((nv3->pfifo.cache1_settings.method_subchannel << 13) & 0x07) | ((nv3->pfifo.cache1_settings.method_address << 2) & 0x7FF); break; + case NV3_PFIFO_CACHE0_PUT: + ret = nv3->pfifo.cache0_settings.put_address; + break; case NV3_PFIFO_CACHE0_GET: - //wa + ret = nv3->pfifo.cache0_settings.get_address; + break; + case NV3_PFIFO_CACHE1_PUT: + ret = nv3->pfifo.cache1_settings.put_address; + break; + case NV3_PFIFO_CACHE1_GET: + ret = nv3->pfifo.cache1_settings.get_address; break; // Reassignment case NV3_PFIFO_CACHE_REASSIGNMENT: @@ -324,7 +333,7 @@ void nv3_pfifo_trigger_dma_if_required() } } -void nv3_pfifo_write(uint32_t address, uint32_t value) +void nv3_pfifo_write(uint32_t address, uint32_t val) { // before doing anything, check the subsystem enablement @@ -337,14 +346,14 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); - nv_log("PFIFO Write 0x%08x -> 0x%08x", value, address); + nv_log("PFIFO Write 0x%08x -> 0x%08x", val, address); // if the register actually exists if (reg) { // on-read function if (reg->on_write) - reg->on_write(value); + reg->on_write(val); else { switch (reg->address) @@ -356,7 +365,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) // Bit 12 - DMA Pusher // Bit 16 - DMA Page Table Entry (pagefault?) case NV3_PFIFO_INTR: - nv3->pfifo.interrupt_status &= ~value; + nv3->pfifo.interrupt_status &= ~val; nv3_pmc_clear_interrupts(); // update the internal cache error state @@ -364,21 +373,21 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) nv3->pfifo.debug_0 &= ~NV3_PFIFO_INTR_CACHE_ERROR; break; case NV3_PFIFO_INTR_EN: - nv3->pfifo.interrupt_enable = value & 0x00011111; + nv3->pfifo.interrupt_enable = val & 0x00011111; nv3_pmc_handle_interrupts(true); break; case NV3_PFIFO_DELAY_0: - nv3->pfifo.dma_delay_retry = value; + nv3->pfifo.dma_delay_retry = val; break; case NV3_PFIFO_CONFIG_0: - nv3->pfifo.config_0 = value; + nv3->pfifo.config_0 = val; break; case NV3_PFIFO_CONFIG_RAMHT: - nv3->pfifo.ramht_config = value; + nv3->pfifo.ramht_config = val; // This code sucks a bit fix it later #ifdef ENABLE_NV_LOG - uint32_t new_size_ramht = ((value >> 16) & 0x03); + uint32_t new_size_ramht = ((val >> 16) & 0x03); if (new_size_ramht == 0) new_size_ramht = 0x1000; @@ -395,15 +404,15 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) #endif break; case NV3_PFIFO_CONFIG_RAMFC: - nv3->pfifo.ramfc_config = value; + nv3->pfifo.ramfc_config = val; nv_log("RAMFC Reconfiguration\n" "Base Address in RAMIN: %d\n", ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9); break; case NV3_PFIFO_CONFIG_RAMRO: - nv3->pfifo.ramro_config = value; + nv3->pfifo.ramro_config = val; - uint32_t new_size_ramro = ((value >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); + uint32_t new_size_ramro = ((val >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (new_size_ramro == 0) new_size_ramro = 0x200; @@ -415,86 +424,99 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) "Size: 0x%08x bytes\n", ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9, new_size_ramro); break; case NV3_PFIFO_DEBUG_0: - nv3->pfifo.debug_0 = value; + nv3->pfifo.debug_0 = val; break; // Reassignment case NV3_PFIFO_CACHE_REASSIGNMENT: - nv3->pfifo.cache_reassignment = value & 0x01; //1bit meaningful + nv3->pfifo.cache_reassignment = val & 0x01; //1bit meaningful break; // Control case NV3_PFIFO_CACHE0_PULLER_CONTROL: - nv3->pfifo.cache0_settings.puller_control = value; // 8bits meaningful + nv3->pfifo.cache0_settings.puller_control = val; // 8bits meaningful break; case NV3_PFIFO_CACHE1_PULL0: - nv3->pfifo.cache1_settings.puller_control = value; // 8bits meaningful + nv3->pfifo.cache1_settings.puller_control = val; // 8bits meaningful break; case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: - nv3->pfifo.cache0_settings.context_is_dirty = (value >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; + nv3->pfifo.cache0_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: - nv3->pfifo.cache1_settings.context_is_dirty = (value >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; + nv3->pfifo.cache1_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; case NV3_PFIFO_CACHE0_DMA_PUSH0: - nv3->pfifo.cache0_settings.dma_push0 = value; + nv3->pfifo.cache0_settings.dma_push0 = val; break; case NV3_PFIFO_CACHE1_DMA_PUSH0: - nv3->pfifo.cache1_settings.dma_push0 = value; + nv3->pfifo.cache1_settings.dma_push0 = val; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: - nv3->pfifo.cache0_settings.channel = value; + nv3->pfifo.cache0_settings.channel = val; break; case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: - nv3->pfifo.cache1_settings.channel = value; + nv3->pfifo.cache1_settings.channel = val; break; // CACHE0_STATUS and CACHE1_STATUS are not writable case NV3_PFIFO_CACHE0_METHOD: - nv3->pfifo.cache0_settings.method_subchannel = (value >> 13) & 0x07; - nv3->pfifo.cache0_settings.method_address = (value >> 2) & 0x7FF; + nv3->pfifo.cache0_settings.method_subchannel = (val >> 13) & 0x07; + nv3->pfifo.cache0_settings.method_address = (val >> 2) & 0x7FF; break; case NV3_PFIFO_CACHE1_METHOD: - nv3->pfifo.cache1_settings.method_subchannel = (value >> 13) & 0x07; - nv3->pfifo.cache1_settings.method_address = (value >> 2) & 0x7FF; + nv3->pfifo.cache1_settings.method_subchannel = (val >> 13) & 0x07; + nv3->pfifo.cache1_settings.method_address = (val >> 2) & 0x7FF; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_0: - nv3->pfifo.cache1_settings.dma_state = value; + nv3->pfifo.cache1_settings.dma_state = val; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_1: - nv3->pfifo.cache1_settings.dma_length = value; + nv3->pfifo.cache1_settings.dma_length = val; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_2: - nv3->pfifo.cache1_settings.dma_address = value; + nv3->pfifo.cache1_settings.dma_address = val; break; case NV3_PFIFO_CACHE1_DMA_STATUS: - nv3->pfifo.cache1_settings.dma_status = value; + nv3->pfifo.cache1_settings.dma_status = val; break; case NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE: - nv3->pfifo.cache1_settings.dma_tlb_pt_base = value; + nv3->pfifo.cache1_settings.dma_tlb_pt_base = val; break; case NV3_PFIFO_CACHE1_DMA_TLB_PTE: - nv3->pfifo.cache1_settings.dma_tlb_pte = value; + nv3->pfifo.cache1_settings.dma_tlb_pte = val; break; case NV3_PFIFO_CACHE1_DMA_TLB_TAG: - nv3->pfifo.cache1_settings.dma_tlb_tag = value; + nv3->pfifo.cache1_settings.dma_tlb_tag = val; + break; + /* Put and Get addresses */ + case NV3_PFIFO_CACHE0_PUT: + nv3->pfifo.cache0_settings.put_address = val; + break; + case NV3_PFIFO_CACHE0_GET: + nv3->pfifo.cache0_settings.get_address = val; + break; + case NV3_PFIFO_CACHE1_PUT: + nv3->pfifo.cache1_settings.put_address = val; + break; + case NV3_PFIFO_CACHE1_GET: + nv3->pfifo.cache1_settings.get_address = val; break; case NV3_PFIFO_RUNOUT_GET: uint32_t size_get = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_get == 0) //512b - nv3->pfifo.runout_get = ((value & 0x3F) << 3); + nv3->pfifo.runout_get = ((val & 0x3F) << 3); else - nv3->pfifo.runout_get = ((value & 0x3FF) << 3); + nv3->pfifo.runout_get = ((val & 0x3FF) << 3); break; case NV3_PFIFO_RUNOUT_PUT: uint32_t size_put = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_put == 0) //512b - nv3->pfifo.runout_put = ((value & 0x3F) << 3); + nv3->pfifo.runout_put = ((val & 0x3F) << 3); else - nv3->pfifo.runout_put = ((value & 0x3FF) << 3); + nv3->pfifo.runout_put = ((val & 0x3FF) << 3); break; /* Cache1 is handled below */ case NV3_PFIFO_CACHE0_CTX: - nv3->pfifo.cache0_settings.context[0] = value; + nv3->pfifo.cache0_settings.context[0] = val; break; } } @@ -508,9 +530,9 @@ void nv3_pfifo_write(uint32_t address, uint32_t value) else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) { uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; - nv3->pfifo.cache1_settings.context[ctx_entry_id] = value; + nv3->pfifo.cache1_settings.context[ctx_entry_id] = val; - nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, value); + nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, val); } /* Trigger DMA for notifications if we need to */ diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index b04d261ee..33799c39b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -502,15 +502,15 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // Prints out some informaiton about the object void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) { - nv_log("Found object:"); - nv_log("Name: 0x%04x", name); + nv_log("Found object:\n"); + nv_log("Name: 0x%04x\n", name); - nv_log("Context:"); - nv_log("DMA Channel %d (0-7 valid)", context.channel); - nv_log("Class ID: as repreesnted in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)", context.class_id, + nv_log("Context:\n"); + nv_log("DMA Channel %d (0-7 valid)\n", context.channel); + nv_log("Class ID: as repreesnted in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)\n", context.class_id, context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); - nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)", context.is_rendering); - nv_log("PRAMIN Offset 0x%08x", context.ramin_offset << 4); + nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); + nv_log("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); } #endif \ No newline at end of file From 467c7124d369f1d5b4c2daad1ec1020931eba460 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 02:30:18 +0000 Subject: [PATCH 104/274] Direct CACHE0/CACHE1 Injection --- src/include/86box/nv/vid_nv3.h | 7 ++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 69 ++++++++++++++++++------ src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 56eae4dfa..d18aa8754 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -282,7 +282,8 @@ extern const device_config_t nv3_config[]; // Current channel context - cache1 #define NV3_PFIFO_CACHE0_CTX 0x3080 -#define NV3_PFIFO_CACHE0_METHOD 0x3100 +#define NV3_PFIFO_CACHE0_METHOD_START 0x3100 +#define NV3_PFIFO_CACHE0_METHOD_END 0x3200 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 #define NV3_PFIFO_CACHE1_DMA_PUSH0 0x3200 @@ -320,10 +321,12 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_CTX_START 0x3280 #define NV3_PFIFO_CACHE1_CTX_END 0x32F0 -#define NV3_PFIFO_CACHE1_METHOD 0x3300 +#define NV3_PFIFO_CACHE1_METHOD_START 0x3300 +#define NV3_PFIFO_CACHE1_METHOD_END 0x3400 #define NV3_PFIFO_CACHE1_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 // 15:13 + #define NV3_PFIFO_END 0x3FFF #define NV3_PRM_START 0x4000 // Real-Mode Device Support Subsystem #define NV3_PRM_INTR 0x4100 diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 273e0a2e3..1743750ba 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -192,14 +192,6 @@ uint32_t nv3_pfifo_read(uint32_t address) if (nv3->pfifo.runout_put != nv3->pfifo.runout_get) ret |= 1 << NV3_PFIFO_CACHE1_STATUS_RANOUT; - break; - case NV3_PFIFO_CACHE0_METHOD: - ret = ((nv3->pfifo.cache0_settings.method_subchannel << 13) & 0x07) - | ((nv3->pfifo.cache0_settings.method_address << 2) & 0x7FF); - break; - case NV3_PFIFO_CACHE1_METHOD: - ret = ((nv3->pfifo.cache1_settings.method_subchannel << 13) & 0x07) - | ((nv3->pfifo.cache1_settings.method_address << 2) & 0x7FF); break; case NV3_PFIFO_CACHE0_PUT: ret = nv3->pfifo.cache0_settings.put_address; @@ -295,6 +287,30 @@ uint32_t nv3_pfifo_read(uint32_t address) nv_log("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x", ctx_entry_id, ret); } + /* Direct cache read stuff */ + else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) + { + if (address & 4) + return nv3->pfifo.cache0_entry.data; + else + return nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + } + else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) + { + // Not sure if REV C changes this. It should... + uint32_t slot = 0; + + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + slot = (address >> 3) & 0x3F; + else + slot = (address >> 3) & 0x1F; + + // See if we want the object name or the channel/subchannel information. + if (address & 4) + return nv3->pfifo.cache1_entries[slot].data; + else + return nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + } else { nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); @@ -456,14 +472,6 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache1_settings.channel = val; break; // CACHE0_STATUS and CACHE1_STATUS are not writable - case NV3_PFIFO_CACHE0_METHOD: - nv3->pfifo.cache0_settings.method_subchannel = (val >> 13) & 0x07; - nv3->pfifo.cache0_settings.method_address = (val >> 2) & 0x7FF; - break; - case NV3_PFIFO_CACHE1_METHOD: - nv3->pfifo.cache1_settings.method_subchannel = (val >> 13) & 0x07; - nv3->pfifo.cache1_settings.method_address = (val >> 2) & 0x7FF; - break; case NV3_PFIFO_CACHE1_DMA_CONFIG_0: nv3->pfifo.cache1_settings.dma_state = val; break; @@ -526,6 +534,33 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) else nv_log("\n"); } + else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) + { + if (address & 4) + nv3->pfifo.cache0_entry.data = val; + else + nv3->pfifo.cache0_entry.method = (val & 0x1FFC); + nv3->pfifo.cache0_entry.subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; + } + else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) + { + // Not sure if REV C changes this. It should... + uint32_t slot = 0; + + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + slot = (address >> 3) & 0x3F; + else + slot = (address >> 3) & 0x1F; + + uint32_t real_entry = nv3_pfifo_cache1_normal2gray(slot); + + // See if we want the object name or the channel/subchannel information. + if (address & 4) + nv3->pfifo.cache1_entries[real_entry].data = val; + else + nv3->pfifo.cache1_entries[real_entry].method = (val & 0x1FFC); + nv3->pfifo.cache1_entries[real_entry].subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; + } /* Handle some special memory areas */ else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) { @@ -630,7 +665,7 @@ void nv3_pfifo_cache0_pull() #ifndef RELEASE_BUILD nv_log("***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE0 PULLED ****** Contextual information below\n"); - nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)current_context); + nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)¤t_context); #endif } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 33799c39b..e1a2a6442 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -54,7 +54,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) addr &= (nv3->nvbase.svga.vram_max - 1); uint32_t raw_addr = addr; // saved after and - addr ^= (nv3->nvbase.svga.vram_max- 0x10); + addr ^= (nv3->nvbase.svga.vram_max - 0x10); uint32_t val = 0x00; From 145831eb35ec847e49cb9f64a09ba0056c041c29 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 17:21:36 +0000 Subject: [PATCH 105/274] Update cache0 put address on object creation, too --- src/include/86box/nv/vid_nv.h | 26 +++++------ src/include/86box/nv/vid_nv3.h | 18 ++++---- src/video/nv/nv3/subsystems/nv3_pfifo.c | 58 ++++++++++++++---------- src/video/nv/nv3/subsystems/nv3_pgraph.c | 18 +++++++- src/video/nv/nv3/subsystems/nv3_pramin.c | 24 +++++----- 5 files changed, 85 insertions(+), 59 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 2927b91c6..c58d3b0ba 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -21,7 +21,7 @@ * - People who prevented me from giving up (various) * * Authors: Connor Hyde / starfrost - * + * * Copyright 2024-2025 Connor Hyde */ #ifdef EMU_DEVICE_H // what @@ -55,19 +55,19 @@ void nv_log(const char *fmt, ...); #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_NV1_A0 0x0000 // 1994 +#define CHIP_REVISION_NV1_B0 0x0010 // 1995 +#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 - +#define NV_ARCHITECTURE_NV1 1 // NV1/STG2000 +#define NV_ARCHITECTURE_NV2 2 // Nvidia 'Mutara V08' +#define NV_ARCHITECTURE_NV3 3 // Riva 128 +#define NV_ARCHITECTURE_NV4 4 // Riva TNT and later typedef enum nv_bus_generation_e { @@ -119,7 +119,7 @@ typedef struct nv_base_s void* ddc; // Display Data Channel for EDID } nv_base_t; -#define NV_REG_LIST_END 0xD15EA5E +#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 @@ -131,11 +131,11 @@ typedef struct nv_base_s // 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 + 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 + 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); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index d18aa8754..d9d0a6ad3 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -271,10 +271,10 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty #define NV3_PFIFO_CACHE0_STATUS_FULL 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE0_PULLER_CONTROL 0x3040 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED 0 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD 8 +#define NV3_PFIFO_CACHE0_DMA_PULL0 0x3040 +#define NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD 8 #define NV3_PFIFO_CACHE0_PULLER_CTX_STATE 0x3050 #define NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 @@ -307,11 +307,11 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULL0 0x3240 +#define NV3_PFIFO_CACHE1_DMA_PULL0 0x3240 //todo: merge stuff -#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware +#define NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE 0x3250 #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 @@ -932,7 +932,7 @@ typedef struct nv3_pfifo_cache_s uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. uint8_t channel; // The DMA channel ID of this cache. uint32_t status; - uint32_t puller_control; + uint32_t dma_pull0; uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; // Only one of these exists for cache0 /* cache1 only diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 1743750ba..9857afbd7 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -43,19 +43,20 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, { NV3_PFIFO_CACHE_REASSIGNMENT, "PFIFO - Allow Cache Channel Reassignment", NULL, NULL }, - { NV3_PFIFO_CACHE0_PULLER_CONTROL, "PFIFO - Cache0 Puller Control", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller Control"}, + { NV3_PFIFO_CACHE0_DMA_PULL0, "PFIFO - Cache0 Puller Control", NULL, NULL}, + { NV3_PFIFO_CACHE1_DMA_PULL0, "PFIFO - Cache1 Puller Control"}, { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, + { NV3_PFIFO_CACHE1_DMA_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, { NV3_PFIFO_CACHE0_DMA_PUSH0, "PFIFO - Cache0 Access", NULL, NULL, }, { NV3_PFIFO_CACHE1_DMA_PUSH0, "PFIFO - Cache1 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 DMA Channel ID", NULL, NULL, }, - { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 DMA Channel ID", NULL, NULL, }, + { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 Push Channel ID", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get", NULL, NULL }, + { NV3_PFIFO_CACHE0_CTX, "PFIFO - Cache0 Context", NULL, NULL }, { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get", NULL, NULL }, { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put", NULL, NULL }, { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put", NULL, NULL }, @@ -145,11 +146,11 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CONFIG_RAMRO: ret = nv3->pfifo.ramro_config; break; - case NV3_PFIFO_CACHE0_PULLER_CONTROL: - ret = nv3->pfifo.cache0_settings.puller_control; + case NV3_PFIFO_CACHE0_DMA_PULL0: + ret = nv3->pfifo.cache0_settings.dma_pull0; break; - case NV3_PFIFO_CACHE1_PULL0: - ret = nv3->pfifo.cache1_settings.puller_control; + case NV3_PFIFO_CACHE1_DMA_PULL0: + ret = nv3->pfifo.cache1_settings.dma_pull0; break; case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; @@ -285,11 +286,13 @@ uint32_t nv3_pfifo_read(uint32_t address) uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; ret = nv3->pfifo.cache1_settings.context[ctx_entry_id]; - nv_log("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x", ctx_entry_id, ret); + nv_log("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x\n", ctx_entry_id, ret); } /* Direct cache read stuff */ else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { + nv_log("PFIFO Cache0 Read\n"); + if (address & 4) return nv3->pfifo.cache0_entry.data; else @@ -305,6 +308,8 @@ uint32_t nv3_pfifo_read(uint32_t address) else slot = (address >> 3) & 0x1F; + nv_log("PFIFO Cache1 Read slot=%d\n", slot); + // See if we want the object name or the channel/subchannel information. if (address & 4) return nv3->pfifo.cache1_entries[slot].data; @@ -341,11 +346,15 @@ void nv3_pfifo_trigger_dma_if_required() /* PUSH - System to GPU (?) */ if (nv3->pfifo.cache1_settings.dma_push0) { - + /* PULL - GPU to System */ + nv_log("Initiating System to NV DMA - Probably we are trying to notify\n"); + } + else if (nv3->pfifo.cache1_settings.dma_pull0) + { + /* PULL - GPU to System */ + nv_log("Initiating NV to System DMA - Probably we are trying to notify\n"); } - /* PULL - GPU to System */ - nv_log("Initiating NV to System DMA - Probably we are trying to notify"); } } @@ -447,11 +456,11 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache_reassignment = val & 0x01; //1bit meaningful break; // Control - case NV3_PFIFO_CACHE0_PULLER_CONTROL: - nv3->pfifo.cache0_settings.puller_control = val; // 8bits meaningful + case NV3_PFIFO_CACHE0_DMA_PULL0: + nv3->pfifo.cache0_settings.dma_pull0 = val; // 8bits meaningful break; - case NV3_PFIFO_CACHE1_PULL0: - nv3->pfifo.cache1_settings.puller_control = val; // 8bits meaningful + case NV3_PFIFO_CACHE1_DMA_PULL0: + nv3->pfifo.cache1_settings.dma_pull0 = val; // 8bits meaningful break; case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: nv3->pfifo.cache0_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; @@ -628,7 +637,7 @@ void nv3_pfifo_cache0_push() void nv3_pfifo_cache0_pull() { // Do nothing if PFIFO CACHE0 is disabled - if (!nv3->pfifo.cache0_settings.puller_control & (1 >> NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED)) + if (!nv3->pfifo.cache0_settings.dma_pull0 & (1 >> NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED)) return; // Do nothing if there is nothing in cache0 to pull @@ -644,6 +653,9 @@ void nv3_pfifo_cache0_pull() // i.e. there is no method in cache0, so we have to find the object. if (!current_method) { + // flip the get address over + nv3->pfifo.cache0_settings.get_address ^= 0x04; + if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) return; // interrupt was fired, and we went to ramro } @@ -654,8 +666,8 @@ void nv3_pfifo_cache0_pull() // Tell the CPU if we found a software method if (current_context & 0x800000) { - nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); } @@ -780,7 +792,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) void nv3_pfifo_cache1_pull() { // Do nothing if PFIFO CACHE1 is disabled - if (!nv3->pfifo.cache1_settings.puller_control & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) + if (!nv3->pfifo.cache1_settings.dma_pull0 & (1 >> NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED)) return; // Do nothing if there is nothing in cache1 to pull @@ -808,8 +820,8 @@ void nv3_pfifo_cache1_pull() // Tell the CPU if we found a software method if (current_context & 0x800000) { - nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); } diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 1c5750596..50089c410 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -468,16 +468,30 @@ void nv3_pgraph_vblank_start(svga_t* svga) nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); } +void nv3_pgraph_arbitrate_method(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context) +{ + switch (class_id) + { + + } +} + /* Arbitrates graphics object submission to the right object types */ void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context) { // class id can be derived from the context but we debug log it before we get here - + // Do we need to read grobj here? + switch (method) { // This method is how we figure out which methods exist. case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("Hi, I'm an NVidia object :)\n"); + nv_log("I'm an Nvidia Object! name=0x%08x channel=%d.%d class=0x%02x (%s) method=0x%04x, context=0x%08x\n", + name, channel, subchannel, class_id, nv3_class_names[class_id], method, context); + break; + default: + // Object Method orchestration + nv3_pgraph_arbitrate_method(name, method, channel, subchannel, class_id, context); 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 index e1a2a6442..55de2616b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -409,16 +409,16 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!cache_num) { nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; - nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; + nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; } else { nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; - nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; + nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; + nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED; } nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); @@ -452,9 +452,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // By definition we can't have a cache error by here so take it off if (!cache_num) - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_HASH_FAILURE; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE; else - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; + nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE; // Caches store all the subchannels for our current dma channel and basically get stale every context switch // Also we have to check that a osftware object didn't end up in here... @@ -471,13 +471,13 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // handle it as an error if (!cache_num) { - nv3->pfifo.cache0_settings.puller_control |= NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_ENABLED; + nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; } else { - nv3->pfifo.cache1_settings.puller_control |= NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; + nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED; } // It's an error but it isn't lol @@ -488,9 +488,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u { // obviously turn off the "is software" if it's not if (!cache_num) - nv3->pfifo.cache0_settings.puller_control &= ~NV3_PFIFO_CACHE0_PULLER_CONTROL_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; else - nv3->pfifo.cache1_settings.puller_control &= ~NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD; } // Ok we found it. Lol From ca6a7c4f37d1a0dce73d80324ea841ff3f26f517 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 17:56:25 +0000 Subject: [PATCH 106/274] Workaround for 86box dynarec design allowing the riva to overwrite old graphics objects. --- src/video/nv/nv3/nv3_core.c | 13 ++++++++++++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 8 ++++++-- src/video/nv/nv3/subsystems/nv3_user.c | 4 ++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 949ce53be..0876f7c89 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -127,7 +127,14 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) } - return nv3_mmio_arbitrate_read(addr); + ret = nv3_mmio_arbitrate_read(addr); + + // This may get around the riva shredding its own cache + //nv3_pfifo_cache0_pull(); + //nv3_pfifo_cache1_pull(); + + return ret; + } // Write 8-bit MMIO @@ -206,6 +213,10 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) } nv3_mmio_arbitrate_write(addr, val); + + // This may get around the riva shredding its own cache + //nv3_pfifo_cache0_pull(); + //nv3_pfifo_cache1_pull(); } // PCI stuff diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 9857afbd7..a9ae7dc61 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -267,7 +267,7 @@ uint32_t nv3_pfifo_read(uint32_t address) break; - /* Cache1 is handled below */ + /* Cache1 is handled below - cache0 only has one entry */ case NV3_PFIFO_CACHE0_CTX: ret = nv3->pfifo.cache0_settings.context[0]; break; @@ -293,7 +293,7 @@ uint32_t nv3_pfifo_read(uint32_t address) { nv_log("PFIFO Cache0 Read\n"); - if (address & 4) + if (address & 4) return nv3->pfifo.cache0_entry.data; else return nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); @@ -545,8 +545,12 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) } else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { + // 3104 always written after 3100 if (address & 4) + { nv3->pfifo.cache0_entry.data = val; + nv3_pfifo_cache0_pull(); // immediately pull out + } else nv3->pfifo.cache0_entry.method = (val & 0x1FFC); nv3->pfifo.cache0_entry.subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index 4361abcd0..a6c9a588c 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -66,4 +66,8 @@ uint32_t nv3_user_read(uint32_t address) void nv3_user_write(uint32_t address, uint32_t value) { nv3_pfifo_cache1_push(address, value); + + // This isn't ideal, but otherwise, the dynarec causes the GPU to write so many objects into CACHE1, it starts overwriting the old objects + // This basically makes the fifo not a fifo, but oh well + nv3_pfifo_cache1_pull(); } \ No newline at end of file From ce207fef8520f9b490af44584399c1cc831427d2 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 18:06:57 +0000 Subject: [PATCH 107/274] remove some oboslete code --- src/video/nv/nv3/nv3_core.c | 4 ---- src/video/nv/nv3/nv3_core_arbiter.c | 6 +----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 0876f7c89..c56551b14 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -213,10 +213,6 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) } nv3_mmio_arbitrate_write(addr, val); - - // This may get around the riva shredding its own cache - //nv3_pfifo_cache0_pull(); - //nv3_pfifo_cache1_pull(); } // PCI stuff diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 6e8e2b9a5..17cca15df 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -103,9 +103,6 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) ret = nv3_vram_read(address); else if (address >= NV3_USER_START && address <= NV3_USER_END) ret = nv3_user_read(address); - // RAMIN is handled by a separate memory mapping in PCI BAR1 - //else if (address >= NV3_RAMIN_START && address <= NV3_RAMIN_END) - //ret = nv3_ramin_arbitrate_read(address); // RAMHT, RAMFC, RAMRO etc dettermined by nv3_ramin_* function else { nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); @@ -167,8 +164,7 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) nv3_vram_write(address, value); else if (address >= NV3_USER_START && address <= NV3_USER_END) nv3_user_write(address, value); - else if (address >= NV3_RAMIN_START && address <= NV3_RAMIN_END) - nv3_ramin_arbitrate_write(address, value); // RAMHT, RAMFC, RAMRO etc is determined by the nv3_ramin_* functions + //RAMIN is its own thing else { nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); From 1e32c119c56e0310bd358162f6ad0f1bd7138c57 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 19:00:08 +0000 Subject: [PATCH 108/274] Make the pullre registers work. This makes the grobj submitted exist --- src/include/86box/nv/vid_nv3.h | 24 +++--- src/video/nv/nv3/subsystems/nv3_pfifo.c | 98 ++++++++++++++++-------- src/video/nv/nv3/subsystems/nv3_pramin.c | 24 +++--- 3 files changed, 89 insertions(+), 57 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index d9d0a6ad3..852c368b5 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -264,17 +264,17 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 -#define NV3_PFIFO_CACHE0_DMA_PUSH0 0x3000 +#define NV3_PFIFO_CACHE0_PUSH0 0x3000 #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 #define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty #define NV3_PFIFO_CACHE0_STATUS_FULL 8 #define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE0_DMA_PULL0 0x3040 -#define NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD 8 +#define NV3_PFIFO_CACHE0_PULL0 0x3040 +#define NV3_PFIFO_CACHE0_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD 8 #define NV3_PFIFO_CACHE0_PULLER_CTX_STATE 0x3050 #define NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY 4 // 1=dirty 0=clean #define NV3_PFIFO_CACHE0_GET 0x3070 @@ -286,7 +286,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_METHOD_END 0x3200 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 -#define NV3_PFIFO_CACHE1_DMA_PUSH0 0x3200 +#define NV3_PFIFO_CACHE1_PUSH0 0x3200 #define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 @@ -307,11 +307,11 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_DMA_PULL0 0x3240 +#define NV3_PFIFO_CACHE1_PULL0 0x3240 //todo: merge stuff -#define NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware +#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE 0x3250 #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 @@ -927,12 +927,12 @@ typedef struct nv3_pbus_s typedef struct nv3_pfifo_cache_s { - bool dma_push0; // Can we even access this cache? + bool push0; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. uint8_t channel; // The DMA channel ID of this cache. uint32_t status; - uint32_t dma_pull0; + uint32_t pull0; uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; // Only one of these exists for cache0 /* cache1 only diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index a9ae7dc61..c3deac24e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -43,13 +43,13 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, { NV3_PFIFO_CACHE_REASSIGNMENT, "PFIFO - Allow Cache Channel Reassignment", NULL, NULL }, - { NV3_PFIFO_CACHE0_DMA_PULL0, "PFIFO - Cache0 Puller Control", NULL, NULL}, - { NV3_PFIFO_CACHE1_DMA_PULL0, "PFIFO - Cache1 Puller Control"}, + { NV3_PFIFO_CACHE0_PULL0, "PFIFO - Cache0 Puller Control", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller Control"}, { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE1_DMA_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, + { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE0_DMA_PUSH0, "PFIFO - Cache0 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE1_DMA_PUSH0, "PFIFO - Cache1 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE0_PUSH0, "PFIFO - Cache0 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PUSH0, "PFIFO - Cache1 Access", NULL, NULL, }, { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, @@ -146,11 +146,12 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CONFIG_RAMRO: ret = nv3->pfifo.ramro_config; break; - case NV3_PFIFO_CACHE0_DMA_PULL0: - ret = nv3->pfifo.cache0_settings.dma_pull0; + /* These automatically trigger pulls when 1 is written */ + case NV3_PFIFO_CACHE0_PULL0: + ret = nv3->pfifo.cache0_settings.pull0; break; - case NV3_PFIFO_CACHE1_DMA_PULL0: - ret = nv3->pfifo.cache1_settings.dma_pull0; + case NV3_PFIFO_CACHE1_PULL0: + ret = nv3->pfifo.cache1_settings.pull0; break; case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; @@ -158,11 +159,12 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; break; - case NV3_PFIFO_CACHE0_DMA_PUSH0: - ret = nv3->pfifo.cache0_settings.dma_push0; + /* Does this automatically push? */ + case NV3_PFIFO_CACHE0_PUSH0: + ret = nv3->pfifo.cache0_settings.push0; break; - case NV3_PFIFO_CACHE1_DMA_PUSH0: - ret = nv3->pfifo.cache1_settings.dma_push0; + case NV3_PFIFO_CACHE1_PUSH0: + ret = nv3->pfifo.cache1_settings.push0; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: ret = nv3->pfifo.cache0_settings.channel; @@ -344,12 +346,12 @@ void nv3_pfifo_trigger_dma_if_required() uint32_t tlb_pt_tag = nv3->pfifo.cache1_settings.dma_tlb_tag; // 0xFFFFFFFF usually? /* PUSH - System to GPU (?) */ - if (nv3->pfifo.cache1_settings.dma_push0) + if (nv3->pfifo.cache1_settings.push0) { /* PULL - GPU to System */ nv_log("Initiating System to NV DMA - Probably we are trying to notify\n"); } - else if (nv3->pfifo.cache1_settings.dma_pull0) + else if (nv3->pfifo.cache1_settings.pull0) { /* PULL - GPU to System */ nv_log("Initiating NV to System DMA - Probably we are trying to notify\n"); @@ -455,12 +457,20 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) case NV3_PFIFO_CACHE_REASSIGNMENT: nv3->pfifo.cache_reassignment = val & 0x01; //1bit meaningful break; - // Control - case NV3_PFIFO_CACHE0_DMA_PULL0: - nv3->pfifo.cache0_settings.dma_pull0 = val; // 8bits meaningful + // Control - these can trigger pulls + case NV3_PFIFO_CACHE0_PULL0: + nv3->pfifo.cache0_settings.pull0 = val; // 8bits meaningful + + if (nv3->pfifo.cache0_settings.pull0 & (1 >> NV3_PFIFO_CACHE0_PULL0_ENABLED)) + nv3_pfifo_cache0_pull(); + break; - case NV3_PFIFO_CACHE1_DMA_PULL0: - nv3->pfifo.cache1_settings.dma_pull0 = val; // 8bits meaningful + case NV3_PFIFO_CACHE1_PULL0: + nv3->pfifo.cache1_settings.pull0 = val; // 8bits meaningful + + if (nv3->pfifo.cache1_settings.pull0 & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) + nv3_pfifo_cache1_pull(); + break; case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: nv3->pfifo.cache0_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; @@ -468,11 +478,11 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: nv3->pfifo.cache1_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; - case NV3_PFIFO_CACHE0_DMA_PUSH0: - nv3->pfifo.cache0_settings.dma_push0 = val; + case NV3_PFIFO_CACHE0_PUSH0: + nv3->pfifo.cache0_settings.push0 = val; break; - case NV3_PFIFO_CACHE1_DMA_PUSH0: - nv3->pfifo.cache1_settings.dma_push0 = val; + case NV3_PFIFO_CACHE1_PUSH0: + nv3->pfifo.cache1_settings.push0 = val; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: nv3->pfifo.cache0_settings.channel = val; @@ -545,15 +555,22 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) } else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { + nv_log("PFIFO Cache0 Write"); + // 3104 always written after 3100 if (address & 4) { + nv_log("Name = 0x%08x\n", val); nv3->pfifo.cache0_entry.data = val; nv3_pfifo_cache0_pull(); // immediately pull out } else + { nv3->pfifo.cache0_entry.method = (val & 0x1FFC); nv3->pfifo.cache0_entry.subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; + nv_log("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache0_entry.subchannel, nv3->pfifo.cache0_entry.method); + } + } else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) { @@ -567,12 +584,21 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) uint32_t real_entry = nv3_pfifo_cache1_normal2gray(slot); + nv_log("Cache1 Write Slot %d (Gray code)", real_entry); + // See if we want the object name or the channel/subchannel information. if (address & 4) + { + nv_log("Name = 0x%08x\n", val); nv3->pfifo.cache1_entries[real_entry].data = val; + } else + { nv3->pfifo.cache1_entries[real_entry].method = (val & 0x1FFC); nv3->pfifo.cache1_entries[real_entry].subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; + nv_log("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache1_entries[real_entry].subchannel, nv3->pfifo.cache1_entries[real_entry].method); + } + } /* Handle some special memory areas */ else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) @@ -641,7 +667,7 @@ void nv3_pfifo_cache0_push() void nv3_pfifo_cache0_pull() { // Do nothing if PFIFO CACHE0 is disabled - if (!nv3->pfifo.cache0_settings.dma_pull0 & (1 >> NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED)) + if (!nv3->pfifo.cache0_settings.pull0 & (1 >> NV3_PFIFO_CACHE0_PULL0_ENABLED)) return; // Do nothing if there is nothing in cache0 to pull @@ -668,10 +694,12 @@ void nv3_pfifo_cache0_pull() uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; // Tell the CPU if we found a software method - if (current_context & 0x800000) + if (!(current_context & 0x800000)) { - nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; + nv_log("The object in CACHE0 is a software object\n"); + + nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); } @@ -713,7 +741,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & (NV3_DMA_CHANNELS - 1); // first make sure there is even any cache available - if (!nv3->pfifo.cache1_settings.dma_push0) + if (!nv3->pfifo.cache1_settings.push0) { oh_shit = true; oh_shit_reason = nv3_runout_reason_no_cache_available; @@ -796,7 +824,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) void nv3_pfifo_cache1_pull() { // Do nothing if PFIFO CACHE1 is disabled - if (!nv3->pfifo.cache1_settings.dma_pull0 & (1 >> NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED)) + if (!nv3->pfifo.cache1_settings.pull0 & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) return; // Do nothing if there is nothing in cache1 to pull @@ -822,10 +850,14 @@ void nv3_pfifo_cache1_pull() uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; // Tell the CPU if we found a software method - if (current_context & 0x800000) + //bit23 unset=software + //bit23 set=hardware + if (!(current_context & 0x800000)) { - nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; + nv_log("The object in CACHE1 is a software object\n"); + + nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 55de2616b..a591f69b1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -409,16 +409,16 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u if (!cache_num) { nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; - nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE; + nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; } else { nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; - nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE; + nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED; + nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; } nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); @@ -452,9 +452,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // By definition we can't have a cache error by here so take it off if (!cache_num) - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_HASH_FAILURE; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE; else - nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_HASH_FAILURE; + nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; // Caches store all the subchannels for our current dma channel and basically get stale every context switch // Also we have to check that a osftware object didn't end up in here... @@ -471,13 +471,13 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // handle it as an error if (!cache_num) { - nv3->pfifo.cache0_settings.dma_pull0 |= NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_ENABLED; + nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; } else { - nv3->pfifo.cache1_settings.dma_pull0 |= NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_ENABLED; + nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; } // It's an error but it isn't lol @@ -488,9 +488,9 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u { // obviously turn off the "is software" if it's not if (!cache_num) - nv3->pfifo.cache0_settings.dma_pull0 &= ~NV3_PFIFO_CACHE0_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; else - nv3->pfifo.cache1_settings.dma_pull0 &= ~NV3_PFIFO_CACHE1_DMA_PULL0_SOFTWARE_METHOD; + nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; } // Ok we found it. Lol From 66fda3070d4a8c3f8a7cfd5b6e5f8fd0b58bc75d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 11 Mar 2025 22:24:51 +0000 Subject: [PATCH 109/274] Fix cache0 checks. Now it actually does a whole bunch of shit. --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index c3deac24e..b219ae535 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -175,7 +175,7 @@ uint32_t nv3_pfifo_read(uint32_t address) case NV3_PFIFO_CACHE0_STATUS: // CACHE0 has only one entry so it can only ever be empty or full - if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache1_settings.get_address) + if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache0_settings.get_address) ret |= 1 << NV3_PFIFO_CACHE0_STATUS_EMPTY; else ret |= 1 << NV3_PFIFO_CACHE0_STATUS_FULL; @@ -491,6 +491,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache1_settings.channel = val; break; // CACHE0_STATUS and CACHE1_STATUS are not writable + // DMA configuration case NV3_PFIFO_CACHE1_DMA_CONFIG_0: nv3->pfifo.cache1_settings.dma_state = val; break; @@ -701,6 +702,7 @@ void nv3_pfifo_cache0_pull() nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + return; } // Is this needed? @@ -859,6 +861,7 @@ void nv3_pfifo_cache1_pull() nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); + return; } // start by incrementing From 4d8a9d2892546ebfd7f088f3d73750cceff6e823 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 12 Mar 2025 16:35:35 +0000 Subject: [PATCH 110/274] Send pgraph writes to the right classes. Implement grobj reading. Fix class execution functions --- .../86box/nv/classes/vid_nv3_classes.h | 73 ++++++++++----- src/include/86box/nv/vid_nv3.h | 3 +- .../nv3/classes/nv3_class_001_beta_factor.c | 2 +- src/video/nv/nv3/classes/nv3_class_002_rop.c | 2 +- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 2 +- .../nv3_class_005_clipping_rectangle.c | 2 +- .../nv/nv3/classes/nv3_class_006_pattern.c | 2 +- .../nv/nv3/classes/nv3_class_007_rectangle.c | 2 +- .../nv/nv3/classes/nv3_class_008_point.c | 2 +- src/video/nv/nv3/classes/nv3_class_009_line.c | 2 +- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- .../nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- .../classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../nv3_class_00e_scaled_image_from_mem.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- .../nv/nv3/classes/nv3_class_011_image.c | 2 +- .../nv/nv3/classes/nv3_class_012_bitmap.c | 2 +- .../classes/nv3_class_014_transfer2memory.c | 2 +- .../nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 2 +- .../classes/nv3_class_018_point_zeta_buffer.c | 2 +- .../classes/nv3_class_01c_image_in_memory.c | 2 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 16 ++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 88 ++++++++++++++++++- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 4 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 4 +- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 4 +- 31 files changed, 178 insertions(+), 62 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 019b04ea4..0a5225226 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -41,6 +41,33 @@ // CLass names for debugging extern const char* nv3_class_names[]; +/* Defines valid classes. */ +typedef enum nv3_pgraph_class_e +{ + nv3_pgraph_class01_beta_factor = 0x01, + nv3_pgraph_class02_rop = 0x02, + nv3_pgraph_class03_chroma_key = 0x03, + nv3_pgraph_class04_plane_mask = 0x04, + nv3_pgraph_class05_clipping_rectangle = 0x05, + nv3_pgraph_class06_pattern = 0x06, + nv3_pgraph_class07_rectangle = 0x07, + nv3_pgraph_class08_point = 0x08, + nv3_pgraph_class09_line = 0x09, + nv3_pgraph_class0a_lin = 0x0a, + nv3_pgraph_class0b_triangle = 0x0b, + nv3_pgraph_class0c_w95txt = 0x0c, + nv3_pgraph_class0d_m2mf = 0x0d, + nv3_pgraph_class0e_scaled_image_from_memory = 0x0e, + nv3_pgraph_class10_blit = 0x10, + nv3_pgraph_class11_image = 0x11, + nv3_pgraph_class12_bitmap = 0x12, + nv3_pgraph_class14_transfer2memory = 0x14, + nv3_pgraph_class15_stretched_image_from_cpu = 0x15, + nv3_pgraph_class17_d3d5tri_zeta_buffer = 0x17, + nv3_pgraph_class18_point_zeta_buffer = 0x18, + nv3_pgraph_class1c_image_in_memory = 0x1c, +} nv3_pgraph_class; + /* Class context switch method */ typedef struct nv3_class_ctx_switch_method_s { @@ -1116,29 +1143,29 @@ typedef struct nv3_grobj_s #define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 // Class methods -void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_001_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_002_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_003_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_004_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_005_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_006_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_007_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_009_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_00c_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_00d_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_00e_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_010_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_011_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_012_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_014_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_015_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_017_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_018_method(uint32_t method_id, nv3_grobj_t grobj); -void nv3_class_01c_method(uint32_t method_id, nv3_grobj_t grobj); +void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); // This area is used for holding universal representations of the U* registers... extern struct nv3_object_class_001 nv3_beta_factor; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 852c368b5..07c30a9ac 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1086,6 +1086,7 @@ typedef struct nv3_pgraph_status_s } nv3_pgraph_status_t; + // Graphics Subsystem typedef struct nv3_pgraph_s { @@ -1454,7 +1455,7 @@ void nv3_pgraph_init(); uint32_t nv3_pgraph_read(uint32_t address); void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); -void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context); +void nv3_pgraph_submit(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context); // NV3 PFIFO diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 7a700bf6e..2dc6a4022 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -31,7 +31,7 @@ struct nv3_object_class_001 beta_factor; -void nv3_class_001_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index ac06ef010..f5a1c343e 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -31,7 +31,7 @@ struct nv3_object_class_002 nv3_rop; -void nv3_class_002_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 887e2a8bd..8b99fa808 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -31,7 +31,7 @@ struct nv3_object_class_003 nv3_chroma_key; -void nv3_class_003_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 95e5e70c4..e32eb6333 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -31,7 +31,7 @@ struct nv3_object_class_004 nv3_plane_mask; -void nv3_class_004_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 76ab1cfec..b5d4a5b80 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -30,7 +30,7 @@ struct nv3_object_class_005 nv3_clipping_rectangle; -void nv3_class_005_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 32561e977..8442bde0d 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -31,7 +31,7 @@ struct nv3_object_class_006 nv3_pattern; -void nv3_class_006_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 2b194960b..9b0908b8d 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -31,7 +31,7 @@ struct nv3_object_class_007 nv3_rectangle; -void nv3_class_007_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index eaa12f5c4..4ecfa6d87 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -31,7 +31,7 @@ struct nv3_object_class_008 nv3_point; -void nv3_class_008_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index a17cf0f90..c8104cd9c 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -31,7 +31,7 @@ struct nv3_object_class_009 nv3_line; -void nv3_class_009_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 66d3d949b..8e83c2433 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -32,7 +32,7 @@ struct nv3_object_class_00A nv3_lin; -void nv3_class_00a_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index f1d41a816..766597af1 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -31,7 +31,7 @@ struct nv3_object_class_00B nv3_triangle; -void nv3_class_00b_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index d8dbc4513..cceece8b7 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -31,7 +31,7 @@ struct nv3_object_class_00C nv3_win95_gdi_text; -void nv3_class_00c_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 4f51f187a..6e2569cd9 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -31,7 +31,7 @@ struct nv3_object_class_00D nv3_m2mf; -void nv3_class_00d_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index 165c1e9d3..e5cbb9634 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -31,7 +31,7 @@ struct nv3_object_class_00E nv3_scaled_image_from_mem; -void nv3_class_00e_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index f32309343..7c1e9626f 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -31,7 +31,7 @@ struct nv3_object_class_010 nv3_blit; -void nv3_class_010_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index e42ea8071..d7a2a7987 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -31,7 +31,7 @@ struct nv3_object_class_011 nv3_image; -void nv3_class_011_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index a0ff8d537..63a5f8e1d 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -32,7 +32,7 @@ struct nv3_object_class_012 nv3_bitmap; -void nv3_class_012_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index bc4e3f879..197235c75 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -32,7 +32,7 @@ struct nv3_object_class_014 nv3_transfer2memory; -void nv3_class_014_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 1954724d1..1ab61faed 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -31,7 +31,7 @@ struct nv3_object_class_015 nv3_stretched_image_from_cpu; -void nv3_class_015_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 7c669a1e5..2bb2bfd77 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -31,7 +31,7 @@ struct nv3_object_class_017 nv3_d3d5_tri; -void nv3_class_017_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index bbf29694f..68747c9ce 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -31,7 +31,7 @@ struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; -void nv3_class_018_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index f40924deb..0efb6c8ce 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -31,7 +31,7 @@ struct nv3_object_class_01C nv3_image_in_memory; -void nv3_class_01c_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 39f7e2b81..eb0469823 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> -void nv3_generic_method(uint32_t method_id, nv3_grobj_t grobj) +void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index b219ae535..31630dae2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -709,9 +709,13 @@ void nv3_pfifo_cache0_pull() nv3->pfifo.cache0_settings.get_address ^= 0x04; #ifndef RELEASE_BUILD - nv_log("***** SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - CACHE0 PULLED ****** Contextual information below\n"); + nv_log("***** DEBUG: CACHE0 PULLED ****** Contextual information below\n"); - nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)¤t_context); + nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; + + nv3_debug_ramin_print_context_info(current_name, context_structure); + + nv3_pgraph_submit(current_name, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); #endif } @@ -876,9 +880,11 @@ void nv3_pfifo_cache1_pull() nv3->pfifo.cache1_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; #ifndef RELEASE_BUILD - nv_log("***** OBJECT PULLED, SUBMITTING GRAPHICS COMMANDS CURRENTLY UNIMPLEMENTED - ****** Contextual information below\n"); - - nv3_debug_ramin_print_context_info(current_name, *(nv3_ramin_context_t*)¤t_context); + nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; + + nv3_debug_ramin_print_context_info(current_name, context_structure); + + nv3_pgraph_submit(current_name, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); #endif //Todo: finish it diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 50089c410..ffbaa7f01 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -468,16 +468,98 @@ void nv3_pgraph_vblank_start(svga_t* svga) nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); } -void nv3_pgraph_arbitrate_method(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context) +/* Sends off method execution to the right class */ +void nv3_pgraph_arbitrate_method(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) { + /* Obtain the grobj information from the context in ramin */ + nv3_grobj_t grobj = {0}; + + grobj.grobj_0 = nv3_ramin_read32(context.ramin_offset, nv3); + grobj.grobj_1 = nv3_ramin_read32(context.ramin_offset + 4, nv3); + grobj.grobj_2 = nv3_ramin_read32(context.ramin_offset + 8, nv3); + grobj.grobj_3 = nv3_ramin_read32(context.ramin_offset + 12, nv3); + + nv_log("**** About to execute method **** obj name=0x%08x, method=0x%04x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x", + name, method, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); + + // By this point, we already ANDed the class ID to 0x1F. + // Send the grobj, the context, the method and the name off to actually be acted upon. switch (class_id) { - + case nv3_pgraph_class01_beta_factor: + nv3_class_001_method(name, method, context, grobj); + break; + case nv3_pgraph_class02_rop: + nv3_class_002_method(name, method, context, grobj); + break; + case nv3_pgraph_class03_chroma_key: + nv3_class_003_method(name, method, context, grobj); + break; + case nv3_pgraph_class04_plane_mask: + nv3_class_004_method(name, method, context, grobj); + break; + case nv3_pgraph_class05_clipping_rectangle: + nv3_class_005_method(name, method, context, grobj); + break; + case nv3_pgraph_class06_pattern: + nv3_class_006_method(name, method, context, grobj); + break; + case nv3_pgraph_class07_rectangle: + nv3_class_007_method(name, method, context, grobj); + break; + case nv3_pgraph_class08_point: + nv3_class_008_method(name, method, context, grobj); + break; + case nv3_pgraph_class09_line: + nv3_class_009_method(name, method, context, grobj); + break; + case nv3_pgraph_class0a_lin: + nv3_class_00a_method(name, method, context, grobj); + break; + case nv3_pgraph_class0b_triangle: + nv3_class_00b_method(name, method, context, grobj); + break; + case nv3_pgraph_class0c_w95txt: + nv3_class_00c_method(name, method, context, grobj); + break; + case nv3_pgraph_class0d_m2mf: + nv3_class_00d_method(name, method, context, grobj); + break; + case nv3_pgraph_class0e_scaled_image_from_memory: + nv3_class_00e_method(name, method, context, grobj); + break; + case nv3_pgraph_class10_blit: + nv3_class_010_method(name, method, context, grobj); + break; + case nv3_pgraph_class11_image: + nv3_class_011_method(name, method, context, grobj); + break; + case nv3_pgraph_class12_bitmap: + nv3_class_012_method(name, method, context, grobj); + break; + case nv3_pgraph_class14_transfer2memory: + nv3_class_014_method(name, method, context, grobj); + break; + case nv3_pgraph_class15_stretched_image_from_cpu: + nv3_class_015_method(name, method, context, grobj); + break; + case nv3_pgraph_class17_d3d5tri_zeta_buffer: + nv3_class_017_method(name, method, context, grobj); + break; + case nv3_pgraph_class18_point_zeta_buffer: + nv3_class_018_method(name, method, context, grobj); + break; + case nv3_pgraph_class1c_image_in_memory: + nv3_class_01c_method(name, method, context, grobj); + break; + default: + fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); + return; } } /* Arbitrates graphics object submission to the right object types */ -void nv3_pgraph_submit(uint8_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, uint32_t context) +void nv3_pgraph_submit(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) { // class id can be derived from the context but we debug log it before we get here // Do we need to read grobj here? diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index a591f69b1..226f4d8ee 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -507,7 +507,7 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte nv_log("Context:\n"); nv_log("DMA Channel %d (0-7 valid)\n", context.channel); - nv_log("Class ID: as repreesnted in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)\n", context.class_id, + nv_log("Class ID: as represented in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)\n", context.class_id, context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); nv_log("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 9a21c2e4f..fa0d5899c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -30,10 +30,10 @@ uint32_t nv3_ramfc_read(uint32_t address) { - nv_log("RAMFC (Unused DMA channel context) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAMFC (Unused DMA channel context) Read (0x%04x)), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); } void nv3_ramfc_write(uint32_t address, uint32_t value) { - nv_log("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); + nv_log("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x)), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", value, address); } \ 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 index e09f80e83..e46b46618 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -47,10 +47,10 @@ uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) uint32_t nv3_ramht_read(uint32_t address) { - nv_log("RAMHT (Graphics object storage hashtable) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAMHT (Graphics object storage hashtable) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); } void nv3_ramht_write(uint32_t address, uint32_t value) { - nv_log("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), UNIMPLEMENTED\n", value, address); + nv_log("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - UNIMPLEMENTED\n", value, address); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 2104868c6..d421b28dd 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -30,12 +30,12 @@ uint32_t nv3_ramro_read(uint32_t address) { - nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), UNIMPLEMENTED - RETURNING 0x00\n", address); + nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", address); } void nv3_ramro_write(uint32_t address, uint32_t value) { - nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), UNIMPLEMENTED\n (Todo: Read the entries...)\n", value, address); + nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", value, address); } void nv3_ramro_send() From 3512b462b36d46004b0fdf009522d52f352c2d16 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 12 Mar 2025 16:53:57 +0000 Subject: [PATCH 111/274] fix grobj, rename name to param, fix compile --- .../86box/nv/classes/vid_nv3_classes.h | 25 +---------------- src/include/86box/nv/vid_nv3.h | 28 ++++++++++++++++++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 16 +++++------ src/video/nv/nv3/subsystems/nv3_pgraph.c | 23 ++++++++------- 4 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 0a5225226..632f99342 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1142,30 +1142,7 @@ typedef struct nv3_grobj_s #define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START 0x0012 #define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 -// Class methods -void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); + // This area is used for holding universal representations of the U* registers... extern struct nv3_object_class_001 nv3_beta_factor; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 07c30a9ac..3c696bf92 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1142,6 +1142,7 @@ typedef struct nv3_pgraph_s uint32_t trapped_instance; } nv3_pgraph_t; + // GPU Manufacturing Configuration (again) // In the future this will be configurable typedef struct nv3_pextdev_s @@ -1455,8 +1456,33 @@ void nv3_pgraph_init(); uint32_t nv3_pgraph_read(uint32_t address); void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); -void nv3_pgraph_submit(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context); +void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context); +// PGRAPH class methods +// this should be in "vid_nv3_classes.h", but before that can happen, some things need to be rejigged +void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); // NV3 PFIFO void nv3_pfifo_init(); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 31630dae2..a836fdd21 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -678,7 +678,7 @@ void nv3_pfifo_cache0_pull() // There is only one entry for cache0 uint8_t current_channel = nv3->pfifo.cache0_settings.channel; uint8_t current_subchannel = nv3->pfifo.cache0_entry.subchannel; - uint32_t current_name = nv3->pfifo.cache0_entry.data; + uint32_t current_param = nv3->pfifo.cache0_entry.data; uint16_t current_method = nv3->pfifo.cache0_entry.method; // i.e. there is no method in cache0, so we have to find the object. @@ -687,7 +687,7 @@ void nv3_pfifo_cache0_pull() // flip the get address over nv3->pfifo.cache0_settings.get_address ^= 0x04; - if (!nv3_ramin_find_object(current_name, 0, current_channel, current_subchannel)) + if (!nv3_ramin_find_object(current_param, 0, current_channel, current_subchannel)) return; // interrupt was fired, and we went to ramro } @@ -713,9 +713,9 @@ void nv3_pfifo_cache0_pull() nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; - nv3_debug_ramin_print_context_info(current_name, context_structure); + nv3_debug_ramin_print_context_info(current_param, context_structure); - nv3_pgraph_submit(current_name, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); + nv3_pgraph_submit(current_param, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); #endif } @@ -841,13 +841,13 @@ void nv3_pfifo_cache1_pull() uint8_t current_channel = nv3->pfifo.cache1_settings.channel; uint8_t current_subchannel = nv3->pfifo.cache1_entries[get_index].subchannel; - uint32_t current_name = nv3->pfifo.cache1_entries[get_index].data; + uint32_t current_param = nv3->pfifo.cache1_entries[get_index].data; uint16_t current_method = nv3->pfifo.cache1_entries[get_index].method; // NV_ROOT if (!current_method) { - if (!nv3_ramin_find_object(current_name, 1, current_channel, current_subchannel)) + if (!nv3_ramin_find_object(current_param, 1, current_channel, current_subchannel)) return; // interrupt was fired, and we went to ramro } @@ -882,9 +882,9 @@ void nv3_pfifo_cache1_pull() #ifndef RELEASE_BUILD nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; - nv3_debug_ramin_print_context_info(current_name, context_structure); + nv3_debug_ramin_print_context_info(current_param, context_structure); - nv3_pgraph_submit(current_name, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); + nv3_pgraph_submit(current_param, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); #endif //Todo: finish it diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index ffbaa7f01..3cae7b872 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -27,7 +27,7 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> - +#include <86box/nv/classes/vid_nv3_classes.h> // Initialise the PGRAPH subsystem. void nv3_pgraph_init() @@ -474,12 +474,15 @@ void nv3_pgraph_arbitrate_method(uint32_t name, uint16_t method, uint8_t channel /* Obtain the grobj information from the context in ramin */ nv3_grobj_t grobj = {0}; - grobj.grobj_0 = nv3_ramin_read32(context.ramin_offset, nv3); - grobj.grobj_1 = nv3_ramin_read32(context.ramin_offset + 4, nv3); - grobj.grobj_2 = nv3_ramin_read32(context.ramin_offset + 8, nv3); - grobj.grobj_3 = nv3_ramin_read32(context.ramin_offset + 12, nv3); + // we need to shift left by 4 to get the real address, something to do with the 16 byte unit of reversal + uint32_t real_ramin_base = context.ramin_offset << 4; - nv_log("**** About to execute method **** obj name=0x%08x, method=0x%04x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x", + grobj.grobj_0 = nv3_ramin_read32(real_ramin_base, nv3); + grobj.grobj_1 = nv3_ramin_read32(real_ramin_base + 4, nv3); + grobj.grobj_2 = nv3_ramin_read32(real_ramin_base + 8, nv3); + grobj.grobj_3 = nv3_ramin_read32(real_ramin_base + 12, nv3); + + nv_log("**** About to execute method **** obj name=0x%08x, method=0x%04x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", name, method, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); // By this point, we already ANDed the class ID to 0x1F. @@ -559,7 +562,7 @@ void nv3_pgraph_arbitrate_method(uint32_t name, uint16_t method, uint8_t channel } /* Arbitrates graphics object submission to the right object types */ -void nv3_pgraph_submit(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) +void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) { // class id can be derived from the context but we debug log it before we get here // Do we need to read grobj here? @@ -568,12 +571,12 @@ void nv3_pgraph_submit(uint32_t name, uint16_t method, uint8_t channel, uint8_t { // This method is how we figure out which methods exist. case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("I'm an Nvidia Object! name=0x%08x channel=%d.%d class=0x%02x (%s) method=0x%04x, context=0x%08x\n", - name, channel, subchannel, class_id, nv3_class_names[class_id], method, context); + nv_log("I'm an Nvidia Object! channel=%d.%d class=0x%02x (%s) method=0x%04x parameter=0x%08x, context=0x%08x\n", + channel, subchannel, class_id, nv3_class_names[class_id], method, param, context); break; default: // Object Method orchestration - nv3_pgraph_arbitrate_method(name, method, channel, subchannel, class_id, context); + nv3_pgraph_arbitrate_method(param, method, channel, subchannel, class_id, context); break; } } \ No newline at end of file From 18277c1bf949d73e1f6468bea855f3e64d6c6df6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 13 Mar 2025 13:01:29 +0000 Subject: [PATCH 112/274] Move some defines around and add the rendering files. Add some basic things as to what we have to do next (These won't bein the final thing) --- doc/nvidia_notes/NV128.xlsx | Bin 0 -> 9325 bytes .../86box/nv/classes/vid_nv3_classes.h | 12 +++++ src/include/86box/nv/vid_nv3.h | 10 ---- src/video/CMakeLists.txt | 2 + src/video/nv/nv3/render/nv3_render.h | 19 +++++++ src/video/nv/nv3/render/nv3_render_core.c | 32 +++++++++++ src/video/nv/nv3/subsystems/nv3_pgraph.c | 50 +++++++++--------- 7 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 doc/nvidia_notes/NV128.xlsx create mode 100644 src/video/nv/nv3/render/nv3_render.h create mode 100644 src/video/nv/nv3/render/nv3_render_core.c diff --git a/doc/nvidia_notes/NV128.xlsx b/doc/nvidia_notes/NV128.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e293a97ebdeea621058b4c580a4a72be07c06e12 GIT binary patch literal 9325 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 literal 0 HcmV?d00001 diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 632f99342..c33927090 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -68,6 +68,18 @@ typedef enum nv3_pgraph_class_e nv3_pgraph_class1c_image_in_memory = 0x1c, } nv3_pgraph_class; +/* + OBJECT METHODS +*/ + +// Global stuff +#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 +#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x100 // Set object ctx for dma...see nv3_dma_context_t structure +#define NV3_SET_NOTIFY 0x104 + +#define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x304 +#define NV3_W95TXT_COLORA 0x3FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. + /* Class context switch method */ typedef struct nv3_class_ctx_switch_method_s { diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 3c696bf92..6a316e928 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -627,16 +627,6 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CLASS1C_MEM2IMAGE_START 0x5C0000 // class 55, 56, 62, 63? #define NV3_PGRAPH_CLASS1C_MEM2IMAGE_END 0x5C1FFF -/* - OBJECT METHODS -*/ - -// Global stuff -#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 -#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x100 // Set object ctx for dma...see nv3_dma_context_t structure -#define NV3_SET_NOTIFY 0x104 - -#define NV3_W95TXT_COLORA 0x3FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. #define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers #define NV3_PGRAPH_REAL_END 0x5C1FFF diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index db6b8de2d..03d2b504a 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -124,6 +124,8 @@ add_library(vid OBJECT nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c nv/nv3/classes/nv3_class_018_point_zeta_buffer.c nv/nv3/classes/nv3_class_01c_image_in_memory.c + + nv/nv3/render/nv3_render_core.c ) diff --git a/src/video/nv/nv3/render/nv3_render.h b/src/video/nv/nv3/render/nv3_render.h new file mode 100644 index 000000000..98131680d --- /dev/null +++ b/src/video/nv/nv3/render/nv3_render.h @@ -0,0 +1,19 @@ +/* + * 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 headers for rendering + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ + + #pragma once + diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c new file mode 100644 index 000000000..a26d7ef15 --- /dev/null +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -0,0 +1,32 @@ +/* + * 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 Core rendering code (Software version) + * + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 Connor Hyde + */ + + #include + #include + #include + #include + #include <86box/86box.h> + #include <86box/device.h> + #include <86box/mem.h> + #include <86box/pci.h> + #include <86box/rom.h> + #include <86box/video.h> + #include <86box/nv/vid_nv.h> + #include <86box/nv/vid_nv3.h> + #include <86box/nv/classes/vid_nv3_classes.h> + + \ 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 index 3cae7b872..35e5c8e0d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -469,7 +469,7 @@ void nv3_pgraph_vblank_start(svga_t* svga) } /* Sends off method execution to the right class */ -void nv3_pgraph_arbitrate_method(uint32_t name, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) +void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) { /* Obtain the grobj information from the context in ramin */ nv3_grobj_t grobj = {0}; @@ -482,78 +482,78 @@ void nv3_pgraph_arbitrate_method(uint32_t name, uint16_t method, uint8_t channel grobj.grobj_2 = nv3_ramin_read32(real_ramin_base + 8, nv3); grobj.grobj_3 = nv3_ramin_read32(real_ramin_base + 12, nv3); - nv_log("**** About to execute method **** obj name=0x%08x, method=0x%04x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", - name, method, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); + nv_log("**** About to execute method **** method=0x%04x param=0x%08x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", + method, param, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); // By this point, we already ANDed the class ID to 0x1F. // Send the grobj, the context, the method and the name off to actually be acted upon. switch (class_id) { case nv3_pgraph_class01_beta_factor: - nv3_class_001_method(name, method, context, grobj); + nv3_class_001_method(param, method, context, grobj); break; case nv3_pgraph_class02_rop: - nv3_class_002_method(name, method, context, grobj); + nv3_class_002_method(param, method, context, grobj); break; case nv3_pgraph_class03_chroma_key: - nv3_class_003_method(name, method, context, grobj); + nv3_class_003_method(param, method, context, grobj); break; case nv3_pgraph_class04_plane_mask: - nv3_class_004_method(name, method, context, grobj); + nv3_class_004_method(param, method, context, grobj); break; case nv3_pgraph_class05_clipping_rectangle: - nv3_class_005_method(name, method, context, grobj); + nv3_class_005_method(param, method, context, grobj); break; case nv3_pgraph_class06_pattern: - nv3_class_006_method(name, method, context, grobj); + nv3_class_006_method(param, method, context, grobj); break; case nv3_pgraph_class07_rectangle: - nv3_class_007_method(name, method, context, grobj); + nv3_class_007_method(param, method, context, grobj); break; case nv3_pgraph_class08_point: - nv3_class_008_method(name, method, context, grobj); + nv3_class_008_method(param, method, context, grobj); break; case nv3_pgraph_class09_line: - nv3_class_009_method(name, method, context, grobj); + nv3_class_009_method(param, method, context, grobj); break; case nv3_pgraph_class0a_lin: - nv3_class_00a_method(name, method, context, grobj); + nv3_class_00a_method(param, method, context, grobj); break; case nv3_pgraph_class0b_triangle: - nv3_class_00b_method(name, method, context, grobj); + nv3_class_00b_method(param, method, context, grobj); break; case nv3_pgraph_class0c_w95txt: - nv3_class_00c_method(name, method, context, grobj); + nv3_class_00c_method(param, method, context, grobj); break; case nv3_pgraph_class0d_m2mf: - nv3_class_00d_method(name, method, context, grobj); + nv3_class_00d_method(param, method, context, grobj); break; case nv3_pgraph_class0e_scaled_image_from_memory: - nv3_class_00e_method(name, method, context, grobj); + nv3_class_00e_method(param, method, context, grobj); break; case nv3_pgraph_class10_blit: - nv3_class_010_method(name, method, context, grobj); + nv3_class_010_method(param, method, context, grobj); break; case nv3_pgraph_class11_image: - nv3_class_011_method(name, method, context, grobj); + nv3_class_011_method(param, method, context, grobj); break; case nv3_pgraph_class12_bitmap: - nv3_class_012_method(name, method, context, grobj); + nv3_class_012_method(param, method, context, grobj); break; case nv3_pgraph_class14_transfer2memory: - nv3_class_014_method(name, method, context, grobj); + nv3_class_014_method(param, method, context, grobj); break; case nv3_pgraph_class15_stretched_image_from_cpu: - nv3_class_015_method(name, method, context, grobj); + nv3_class_015_method(param, method, context, grobj); break; case nv3_pgraph_class17_d3d5tri_zeta_buffer: - nv3_class_017_method(name, method, context, grobj); + nv3_class_017_method(param, method, context, grobj); break; case nv3_pgraph_class18_point_zeta_buffer: - nv3_class_018_method(name, method, context, grobj); + nv3_class_018_method(param, method, context, grobj); break; case nv3_pgraph_class1c_image_in_memory: - nv3_class_01c_method(name, method, context, grobj); + nv3_class_01c_method(param, method, context, grobj); break; default: fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); From 0b62b3252704d726ecaa6ba07da19580d85a00db Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 13 Mar 2025 13:04:33 +0000 Subject: [PATCH 113/274] Whoops --- .../nv3_render.h => include/86box/nv/render/vid_nv3_render.h} | 0 src/video/nv/nv3/render/nv3_render_core.c | 1 + 2 files changed, 1 insertion(+) rename src/{video/nv/nv3/render/nv3_render.h => include/86box/nv/render/vid_nv3_render.h} (100%) diff --git a/src/video/nv/nv3/render/nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h similarity index 100% rename from src/video/nv/nv3/render/nv3_render.h rename to src/include/86box/nv/render/vid_nv3_render.h diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index a26d7ef15..5fd450260 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -29,4 +29,5 @@ #include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> + \ No newline at end of file From fae3d1023872fda5b8a6f9ad4a49d59726ff8238 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 13 Mar 2025 13:38:09 +0000 Subject: [PATCH 114/274] Move class globals to pgraph. --- .../86box/nv/classes/vid_nv3_classes.h | 57 ++++++++++--------- src/include/86box/nv/render/vid_nv3_render.h | 2 +- src/include/86box/nv/vid_nv3.h | 26 ++++++++- .../nv3/classes/nv3_class_001_beta_factor.c | 3 - src/video/nv/nv3/classes/nv3_class_002_rop.c | 2 - .../nv/nv3/classes/nv3_class_003_chroma_key.c | 3 - .../nv/nv3/classes/nv3_class_004_plane_mask.c | 3 - .../nv3_class_005_clipping_rectangle.c | 3 - .../nv/nv3/classes/nv3_class_006_pattern.c | 3 - .../nv/nv3/classes/nv3_class_007_rectangle.c | 3 - .../nv/nv3/classes/nv3_class_008_point.c | 3 - src/video/nv/nv3/classes/nv3_class_009_line.c | 3 - src/video/nv/nv3/classes/nv3_class_00a_lin.c | 3 - .../nv/nv3/classes/nv3_class_00b_triangle.c | 3 - .../classes/nv3_class_00c_win95_gdi_text.c | 3 - src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 3 - .../nv3_class_00e_scaled_image_from_mem.c | 3 - src/video/nv/nv3/classes/nv3_class_010_blit.c | 3 - .../nv/nv3/classes/nv3_class_011_image.c | 3 - .../nv/nv3/classes/nv3_class_012_bitmap.c | 3 - .../classes/nv3_class_014_transfer2memory.c | 3 - .../nv3_class_015_stretched_image_from_cpu.c | 3 - .../nv3_class_017_d3d5_tri_zeta_buffer.c | 3 - .../classes/nv3_class_018_point_zeta_buffer.c | 1 - .../classes/nv3_class_01c_image_in_memory.c | 3 - .../nv/nv3/classes/nv3_class_shared_methods.c | 1 - 26 files changed, 55 insertions(+), 94 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index c33927090..0e2a27b74 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -132,7 +132,9 @@ typedef struct nv3_color_16_a4r4g4b4_s uint8_t b : 4; } nv3_color_16_a4r4g4b4_t; -/* A1R5G5B5 format */ +/* A1R5G5B5 format + Can also be used for R5G5B5 +*/ typedef struct nv3_color_16_a1r5g5b5_s { uint8_t a : 1; @@ -233,6 +235,31 @@ typedef struct nv3_object_class_001 // Put the rest of it here } nv3_beta_factor_t; +/* Note: This is not used in the class, there are "special" rops that do certain things. So they need to be defined for code readability. It all gets optimised away +by the compiler anyway */ +typedef enum nv3_render_operation_type_e +{ + // Black + nv3_rop_blackness = 0x00, + // dst = !src + nv3_rop_dstinvert = 0x55, + // pattern invert + nv3_rop_patinvert = 0x5A, + // src ^ dst + nv3_rop_xor = 0x66, + // src & dst + nv3_rop_srcand = 0x88, + // dst = src (?) + nv3_rop_dstcopy = 0xAA, + // src = dst (?) + nv3_rop_srccopy = 0xCC, + // paint source + nv3_rop_srcpaint = 0xEE, + // pattern copy + nv3_rop_patcopy = 0xF0, + // White + nv3_rop_whiteness = 0xFF, +} nv3_render_operation_type; /* Object class 0x02 (real hardware) 0x14/0x43 (drivers) @@ -1152,30 +1179,4 @@ typedef struct nv3_grobj_s // PIO Subchannel info #define NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE 0x0010 #define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START 0x0012 -#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 - - - -// This area is used for holding universal representations of the U* registers... -extern struct nv3_object_class_001 nv3_beta_factor; -extern struct nv3_object_class_002 nv3_rop; -extern struct nv3_object_class_003 nv3_chroma_key; -extern struct nv3_object_class_004 nv3_plane_mask; -extern struct nv3_object_class_005 nv3_clipping_rectangle; -extern struct nv3_object_class_006 nv3_pattern; -extern struct nv3_object_class_007 nv3_rectangle; -extern struct nv3_object_class_008 nv3_point; -extern struct nv3_object_class_009 nv3_line; -extern struct nv3_object_class_00A nv3_lin; -extern struct nv3_object_class_00B nv3_triangle; -extern struct nv3_object_class_00C nv3_win95_gdi_text; -extern struct nv3_object_class_00D nv3_m2mf; -extern struct nv3_object_class_00E nv3_scaled_image_from_memory; -extern struct nv3_object_class_010 nv3_blit; -extern struct nv3_object_class_011 nv3_image; -extern struct nv3_object_class_012 nv3_bitmap; -extern struct nv3_object_class_014 nv3_transfer2memory; -extern struct nv3_object_class_015 nv3_stretched_image_from_cpu; -extern struct nv3_object_class_017 nv3_d3d5_tri; -extern struct nv3_object_class_018 nv3_point_zeta_buffer; -extern struct nv3_object_class_01C nv3_image_in_memory; \ No newline at end of file +#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 \ No newline at end of file diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 98131680d..fd6ad8abf 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -15,5 +15,5 @@ * Copyright 2024-2025 Connor Hyde */ - #pragma once +#pragma once diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 6a316e928..e2a74aba1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -23,6 +23,7 @@ #pragma once #include <86box/nv/classes/vid_nv3_classes.h> +#include <86box/nv/render/vid_nv3_render.h> // The GPU base structure extern const device_config_t nv3_config[]; @@ -818,7 +819,6 @@ extern const device_config_t nv3_config[]; #define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F - /* STRUCTURES FOR THE GPU START HERE OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H @@ -1130,6 +1130,30 @@ typedef struct nv3_pgraph_s uint32_t trapped_address; uint32_t trapped_data; uint32_t trapped_instance; + + /* This area is used for holding universal representations of the U* registers, which are actually mapped into MMIO */ + struct nv3_object_class_001 beta_factor; + struct nv3_object_class_002 rop; + struct nv3_object_class_003 chroma_key; + struct nv3_object_class_004 plane_mask; + struct nv3_object_class_005 clipping_rectangle; + struct nv3_object_class_006 pattern; + struct nv3_object_class_007 rectangle; + struct nv3_object_class_008 point; + struct nv3_object_class_009 line; + struct nv3_object_class_00A lin; + struct nv3_object_class_00B triangle; + struct nv3_object_class_00C win95_gdi_text; + struct nv3_object_class_00D m2mf; + struct nv3_object_class_00E scaled_image_from_memory; + struct nv3_object_class_010 blit; + struct nv3_object_class_011 image; + struct nv3_object_class_012 bitmap; + struct nv3_object_class_014 transfer2memory; + struct nv3_object_class_015 stretched_image_from_cpu; + struct nv3_object_class_017 d3d5_tri; + struct nv3_object_class_018 point_zeta_buffer; + struct nv3_object_class_01C image_in_memory; } nv3_pgraph_t; diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 2dc6a4022..b03a6cc21 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_001 beta_factor; void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index f5a1c343e..f4102a605 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -27,9 +27,7 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> -struct nv3_object_class_002 nv3_rop; void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 8b99fa808..e86cea995 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_003 nv3_chroma_key; void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index e32eb6333..f26dba5ef 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_004 nv3_plane_mask; void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index b5d4a5b80..456bf6a20 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -26,9 +26,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_005 nv3_clipping_rectangle; void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 8442bde0d..2eb9755b6 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_006 nv3_pattern; void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 9b0908b8d..cb4d34eaf 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_007 nv3_rectangle; void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 4ecfa6d87..3e0bba3b1 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_008 nv3_point; void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index c8104cd9c..51a637cc4 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_009 nv3_line; void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 8e83c2433..46d538095 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -28,9 +28,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_00A nv3_lin; void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 766597af1..219e36b62 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_00B nv3_triangle; void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index cceece8b7..17dcfd1a1 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_00C nv3_win95_gdi_text; void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 6e2569cd9..a0b2773d4 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_00D nv3_m2mf; void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index e5cbb9634..d0e4970c2 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_00E nv3_scaled_image_from_mem; void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 7c1e9626f..50149cd54 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_010 nv3_blit; void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index d7a2a7987..404ee3774 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_011 nv3_image; void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index 63a5f8e1d..8cba8c6c6 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -28,9 +28,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_012 nv3_bitmap; void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index 197235c75..a1112b1c9 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -28,9 +28,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_014 nv3_transfer2memory; void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 1ab61faed..34997d724 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_015 nv3_stretched_image_from_cpu; void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 2bb2bfd77..2c4b88cfa 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_017 nv3_d3d5_tri; void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 68747c9ce..2ba880306 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -27,7 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 0efb6c8ce..2f7440555 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -27,9 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -struct nv3_object_class_01C nv3_image_in_memory; void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index eb0469823..919fc5eb3 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -27,7 +27,6 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { From 12276374d5ed9c521ffe6b6cee420121fa2c469e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 13 Mar 2025 13:43:17 +0000 Subject: [PATCH 115/274] Fix compile --- src/include/86box/nv/vid_nv3.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index e2a74aba1..a1124bad8 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1132,10 +1132,10 @@ typedef struct nv3_pgraph_s uint32_t trapped_instance; /* This area is used for holding universal representations of the U* registers, which are actually mapped into MMIO */ - struct nv3_object_class_001 beta_factor; - struct nv3_object_class_002 rop; - struct nv3_object_class_003 chroma_key; - struct nv3_object_class_004 plane_mask; + struct nv3_object_class_001 beta_factor_class; + struct nv3_object_class_002 rop_class; + struct nv3_object_class_003 chroma_key_class; + struct nv3_object_class_004 plane_mask_class; struct nv3_object_class_005 clipping_rectangle; struct nv3_object_class_006 pattern; struct nv3_object_class_007 rectangle; From 1af8bf7d78843cad1931f21e8ae0584b0c7a020f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 15 Mar 2025 13:45:13 +0000 Subject: [PATCH 116/274] Implement PGRAPH invalid method functionality --- src/include/86box/nv/classes/vid_nv3_classes.h | 16 ++++++++++------ src/include/86box/nv/vid_nv3.h | 16 ++++++++++++---- .../nv/nv3/classes/nv3_class_001_beta_factor.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_002_rop.c | 9 +++++++-- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 8 +++++++- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 8 +++++++- .../classes/nv3_class_005_clipping_rectangle.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_006_pattern.c | 8 +++++++- .../nv/nv3/classes/nv3_class_007_rectangle.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_008_point.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_009_line.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 8 +++++++- .../nv/nv3/classes/nv3_class_00b_triangle.c | 8 +++++++- .../nv3/classes/nv3_class_00c_win95_gdi_text.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 8 +++++++- .../nv3_class_00e_scaled_image_from_mem.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_010_blit.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_011_image.c | 8 +++++++- src/video/nv/nv3/classes/nv3_class_012_bitmap.c | 8 +++++++- .../nv3/classes/nv3_class_014_transfer2memory.c | 8 +++++++- .../nv3_class_015_stretched_image_from_cpu.c | 8 +++++++- .../classes/nv3_class_017_d3d5_tri_zeta_buffer.c | 8 +++++++- .../classes/nv3_class_018_point_zeta_buffer.c | 8 +++++++- .../nv3/classes/nv3_class_01c_image_in_memory.c | 8 +++++++- .../nv/nv3/classes/nv3_class_shared_methods.c | 8 +++++++- src/video/nv/nv3/subsystems/nv3_pgraph.c | 11 ++++++++++- 26 files changed, 193 insertions(+), 35 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 0e2a27b74..c015d0659 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -73,12 +73,16 @@ typedef enum nv3_pgraph_class_e */ // Global stuff -#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 -#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x100 // Set object ctx for dma...see nv3_dma_context_t structure -#define NV3_SET_NOTIFY 0x104 +#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 +#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x0100 // Set object ctx for dma...see nv3_dma_context_t structure +#define NV3_SET_NOTIFY 0x0104 -#define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x304 -#define NV3_W95TXT_COLORA 0x3FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. +#define NV3_IMAGE_IN_MEMORY_COLOR_FORMAT 0x0300 +#define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x0304 +#define NV3_IMAGE_IN_MEMORY_PITCH 0x0308 +#define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET 0x030C + +#define NV3_W95TXT_COLORA 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. /* Class context switch method */ typedef struct nv3_class_ctx_switch_method_s @@ -1085,7 +1089,7 @@ typedef struct nv3_d3d5_coordinate_s float x; // X coordinate in 3d space of the triangle float y; // Y coordinate in 3d space of the triangle float z; // Z coordinate in 3d space of the triangle - float m; // "Measurement dimension" apparently, allows for more precise measurements. And curves + float m; // 1/W for projection float u; // U coordinate within texture for the (top left?) of the triangle where sampling starts. float v; // V coordinate within texture for the (top left?) of the triangle where sampling starts. } nv3_d3d5_coordinate_t; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index a1124bad8..3c99782e6 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 11 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 13 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -520,10 +520,16 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_DEBUG_3_ALPHA_CHECK 24 #define NV3_PGRAPH_INTR_0 0x400100 +#define NV3_PGRAPH_INTR_0_VBLANK 8 // Fired every frame +#define NV3_PGRAPH_INTR_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? + #define NV3_PGRAPH_INTR_1 0x400104 +#define NV3_PGRAPH_INTR_1_INVALID_METHOD 0 // Invalid method +#define NV3_PGRAPH_INTR_1_INVALID_DATA 4 // Invalid data. Not sure when this would be triggered. +#define NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY 12 // Tried to notify while a notify was pending. +#define NV3_PGRAPH_INTR_1_CTXSW_NOTIFY 16 // Notify fired for software context + #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 -#define NV3_PGRAPH_INTR_EN_0_VBLANK 8 // Fired every frame -#define NV3_PGRAPH_INTR_EN_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? //todo: add what this does #define NV3_PGRAPH_INTR_EN_1 0x400144 // Interrupt Control for PGRAPH #2 (it can receive two at onc) #define NV3_PGRAPH_CONTEXT_SWITCH 0x400180 // DMA context switcher @@ -1470,6 +1476,8 @@ void nv3_pgraph_init(); uint32_t nv3_pgraph_read(uint32_t address); void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); +void nv3_pgraph_interrupt_valid(uint32_t num); +void nv3_pgraph_interrupt_invalid(uint32_t num); void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context); // PGRAPH class methods @@ -1491,7 +1499,7 @@ void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_co void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index b03a6cc21..e21189213 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -30,5 +30,11 @@ void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index f4102a605..b771551d3 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -28,8 +28,13 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> - void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index e86cea995..73901d08d 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -30,5 +30,11 @@ void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index f26dba5ef..48a96ab96 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -30,5 +30,11 @@ void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 456bf6a20..1119147e7 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -29,5 +29,11 @@ void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 2eb9755b6..c8c81066f 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -30,5 +30,11 @@ void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index cb4d34eaf..69e66ca31 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -30,5 +30,11 @@ void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 3e0bba3b1..0cd44b8cd 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -30,5 +30,11 @@ void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index 51a637cc4..8d71f4d5e 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -30,5 +30,11 @@ void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 46d538095..141a2422c 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -31,5 +31,11 @@ void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 219e36b62..91cbaa837 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -30,5 +30,11 @@ void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 17dcfd1a1..3ba352b3c 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -30,5 +30,11 @@ void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index a0b2773d4..888ac89cf 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -30,5 +30,11 @@ void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index d0e4970c2..bf0286c20 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -30,5 +30,11 @@ void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 50149cd54..da2f16bda 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -30,5 +30,11 @@ void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 404ee3774..4499693df 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -30,5 +30,11 @@ void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index 8cba8c6c6..ca8540b75 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -31,5 +31,11 @@ void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index a1112b1c9..6a18ff1d7 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -31,5 +31,11 @@ void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 34997d724..c8560ad0f 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -30,5 +30,11 @@ void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 2c4b88cfa..2acb22a08 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -30,5 +30,11 @@ void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 2ba880306..80f10797f 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -32,5 +32,11 @@ struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 2f7440555..a74313276 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -30,5 +30,11 @@ void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 919fc5eb3..555a180e6 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -30,7 +30,13 @@ void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - + switch (method_id) + { + default: + nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + return; + } } void nv3_notify() diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 35e5c8e0d..802d98d10 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -462,10 +462,19 @@ void nv3_pgraph_interrupt_valid(uint32_t num) nv3_pmc_handle_interrupts(true); } +// Fire an INVALID pgraph interrupt +void nv3_pgraph_interrupt_invalid(uint32_t num) +{ + nv3->pgraph.interrupt_status_1 |= (1 << num); + + // Some code in pcbox hat enables the "reserved" bit HERE if it's set in intr 0. What??? + nv3_pmc_handle_interrupts(true); +} + // VBlank. Fired every single frame. void nv3_pgraph_vblank_start(svga_t* svga) { - nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_EN_0_VBLANK); + nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_VBLANK); } /* Sends off method execution to the right class */ From 6c05cecbcfcfa92868efd99ed27eb16999596277 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 17 Mar 2025 00:18:13 +0000 Subject: [PATCH 117/274] Institute video stdlib --- doc/nvidia_notes/NV3 DMA Engine.txt | 6 + src/include/86box/utils/video_stdlib.h | 20 + src/utils/CMakeLists.txt | 3 + src/utils/video/video_rop.c | 786 ++++++++++++++++++++++ src/video/nv/nv3/render/nv3_render_core.c | 81 ++- 5 files changed, 866 insertions(+), 30 deletions(-) create mode 100644 src/include/86box/utils/video_stdlib.h create mode 100644 src/utils/video/video_rop.c diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt index 6cae9d131..fe2d61508 100644 --- a/doc/nvidia_notes/NV3 DMA Engine.txt +++ b/doc/nvidia_notes/NV3 DMA Engine.txt @@ -42,4 +42,10 @@ DMA_OBJECT STRUCTURE IN RESOURCE MANAGER: 0x34c: base address? 0x374: actually do the transfer +some objects don't actually need to do dma, for example, video patchcord, it just creates a structure in the driver, and rop just allocate ssome memory to put the data for the patchcord/rop thing + +dma start=nv3_mini dc67 +call of mthdCreate for ***DRIVER*** CLASS ID: a7b44, check ptr + + diff --git a/src/include/86box/utils/video_stdlib.h b/src/include/86box/utils/video_stdlib.h new file mode 100644 index 000000000..f8e2ed5af --- /dev/null +++ b/src/include/86box/utils/video_stdlib.h @@ -0,0 +1,20 @@ +/* + * 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. + * + * Standard library for implementation of video functionality that is duplicated across multiple cards. + * + * + * + * Authors: Connor Hyde + * + * Copyright 2025 Connor Hyde + */ + + +/* ROP */ +int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src, int32_t out); \ No newline at end of file diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 2ffe41f7b..ddc423cab 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -23,4 +23,7 @@ add_library(utils OBJECT ini.c log.c random.c + + # VIDEO + video/video_rop.c ) diff --git a/src/utils/video/video_rop.c b/src/utils/video/video_rop.c new file mode 100644 index 000000000..b6350b2e9 --- /dev/null +++ b/src/utils/video/video_rop.c @@ -0,0 +1,786 @@ +#include +#include <86box/utils/video_stdlib.h> + +/* + Implements a standard GDI ternary rop for e.g. bitblit acceleration. + For further information on this function, refer to the documentation on Win32 GDI: + + https://learn.microsoft.com/en-us/windows/win32/gdi/binary-raster-operations + + This is currently used in the following graphics cards: Tseng Labs ET4000/32p, Cirrus Logic CL-GD54xx, 3dfx Voodoo Banshee/Voodoo 3, Trident TGUI9440, + S3 ViRGE, C&T 69000, ATI Mach64, and NVidia RIVA 128 +*/ +int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src, int32_t out) +{ + switch (rop) + { + case 0x00: + out = 0; + break; + case 0x01: + out = ~(dst | (pattern | src)); + break; + case 0x02: + out = dst & ~(pattern | src); + break; + case 0x03: + out = ~(pattern | src); + break; + case 0x04: + out = src & ~(dst | pattern); + break; + case 0x05: + out = ~(dst | pattern); + break; + case 0x06: + out = ~(pattern | ~(dst ^ src)); + break; + case 0x07: + out = ~(pattern | (dst & src)); + break; + case 0x08: + out = src & (dst & ~pattern); + break; + case 0x09: + out = ~(pattern | (dst ^ src)); + break; + case 0x0a: + out = dst & ~pattern; + break; + case 0x0b: + out = ~(pattern | (src & ~dst)); + break; + case 0x0c: + out = src & ~pattern; + break; + case 0x0d: + out = ~(pattern | (dst & ~src)); + break; + case 0x0e: + out = ~(pattern | ~(dst | src)); + break; + case 0x0f: + out = ~pattern; + break; + case 0x10: + out = pattern & ~(dst | src); + break; + case 0x11: + out = ~(dst | src); + break; + case 0x12: + out = ~(src | ~(dst ^ pattern)); + break; + case 0x13: + out = ~(src | (dst & pattern)); + break; + case 0x14: + out = ~(dst | ~(pattern ^ src)); + break; + case 0x15: + out = ~(dst | (pattern & src)); + break; + case 0x16: + out = pattern ^ (src ^ (dst & ~(pattern & src))); + break; + case 0x17: + out = ~(src ^ ((src ^ pattern) & (dst ^ src))); + break; + case 0x18: + out = (src ^ pattern) & (pattern ^ dst); + break; + case 0x19: + out = ~(src ^ (dst & ~(pattern & src))); + break; + case 0x1a: + out = pattern ^ (dst | (src & pattern)); + break; + case 0x1b: + out = ~(src ^ (dst & (pattern ^ src))); + break; + case 0x1c: + out = pattern ^ (src | (dst & pattern)); + break; + case 0x1d: + out = ~(dst ^ (src & (pattern ^ dst))); + break; + case 0x1e: + out = pattern ^ (dst | src); + break; + case 0x1f: + out = ~(pattern & (dst | src)); + break; + case 0x20: + out = dst & (pattern & ~src); + break; + case 0x21: + out = ~(src | (dst ^ pattern)); + break; + case 0x22: + out = dst & ~src; + break; + case 0x23: + out = ~(src | (pattern & ~dst)); + break; + case 0x24: + out = (src ^ pattern) & (dst ^ src); + break; + case 0x25: + out = ~(pattern ^ (dst & ~(src & pattern))); + break; + case 0x26: + out = src ^ (dst | (pattern & src)); + break; + case 0x27: + out = src ^ (dst | ~(pattern ^ src)); + break; + case 0x28: + out = dst & (pattern ^ src); + break; + case 0x29: + out = ~(pattern ^ (src ^ (dst | (pattern & src)))); + break; + case 0x2a: + out = dst & ~(pattern & src); + break; + case 0x2b: + out = ~(src ^ ((src ^ pattern) & (pattern ^ dst))); + break; + case 0x2c: + out = src ^ (pattern & (dst | src)); + break; + case 0x2d: + out = pattern ^ (src | ~dst); + break; + case 0x2e: + out = pattern ^ (src | (dst ^ pattern)); + break; + case 0x2f: + out = ~(pattern & (src | ~dst)); + break; + case 0x30: + out = pattern & ~src; + break; + case 0x31: + out = ~(src | (dst & ~pattern)); + break; + case 0x32: + out = src ^ (dst | (pattern | src)); + break; + case 0x33: + out = ~src; + break; + case 0x34: + out = src ^ (pattern | (dst & src)); + break; + case 0x35: + out = src ^ (pattern | ~(dst ^ src)); + break; + case 0x36: + out = src ^ (dst | pattern); + break; + case 0x37: + out = ~(src & (dst | pattern)); + break; + case 0x38: + out = pattern ^ (src & (dst | pattern)); + break; + case 0x39: + out = src ^ (pattern | ~dst); + break; + case 0x3a: + out = src ^ (pattern | (dst ^ src)); + break; + case 0x3b: + out = ~(src & (pattern | ~dst)); + break; + case 0x3c: + out = pattern ^ src; + break; + case 0x3d: + out = src ^ (pattern | ~(dst | src)); + break; + case 0x3e: + out = src ^ (pattern | (dst & ~src)); + break; + case 0x3f: + out = ~(pattern & src); + break; + case 0x40: + out = pattern & (src & ~dst); + break; + case 0x41: + out = ~(dst | (pattern ^ src)); + break; + case 0x42: + out = (src ^ dst) & (pattern ^ dst); + break; + case 0x43: + out = ~(src ^ (pattern & ~(dst & src))); + break; + case 0x44: + out = src & ~dst; + break; + case 0x45: + out = ~(dst | (pattern & ~src)); + break; + case 0x46: + out = dst ^ (src | (pattern & dst)); + break; + case 0x47: + out = ~(pattern ^ (src & (dst ^ pattern))); + break; + case 0x48: + out = src & (dst ^ pattern); + break; + case 0x49: + out = ~(pattern ^ (dst ^ (src | (pattern & dst)))); + break; + case 0x4a: + out = dst ^ (pattern & (src | dst)); + break; + case 0x4b: + out = pattern ^ (dst | ~src); + break; + case 0x4c: + out = src & ~(dst & pattern); + break; + case 0x4d: + out = ~(src ^ ((src ^ pattern) | (dst ^ src))); + break; + case 0x4e: + out = pattern ^ (dst | (src ^ pattern)); + break; + case 0x4f: + out = ~(pattern & (dst | ~src)); + break; + case 0x50: + out = pattern & ~dst; + break; + case 0x51: + out = ~(dst | (src & ~pattern)); + break; + case 0x52: + out = dst ^ (pattern | (src & dst)); + break; + case 0x53: + out = ~(src ^ (pattern & (dst ^ src))); + break; + case 0x54: + out = ~(dst | ~(pattern | src)); + break; + case 0x55: + out = ~dst; + break; + case 0x56: + out = dst ^ (pattern | src); + break; + case 0x57: + out = ~(dst & (pattern | src)); + break; + case 0x58: + out = pattern ^ (dst & (src | pattern)); + break; + case 0x59: + out = dst ^ (pattern | ~src); + break; + case 0x5a: + out = dst ^ pattern; + break; + case 0x5b: + out = dst ^ (pattern | ~(src | dst)); + break; + case 0x5c: + out = dst ^ (pattern | (src ^ dst)); + break; + case 0x5d: + out = ~(dst & (pattern | ~src)); + break; + case 0x5e: + out = dst ^ (pattern | (src & ~dst)); + break; + case 0x5f: + out = ~(dst & pattern); + break; + case 0x60: + out = pattern & (dst ^ src); + break; + case 0x61: + out = ~(dst ^ (src ^ (pattern | (dst & src)))); + break; + case 0x62: + out = dst ^ (src & (pattern | dst)); + break; + case 0x63: + out = src ^ (dst | ~pattern); + break; + case 0x64: + out = src ^ (dst & (pattern | src)); + break; + case 0x65: + out = dst ^ (src | ~pattern); + break; + case 0x66: + out = dst ^ src; + break; + case 0x67: + out = src ^ (dst | ~(pattern | src)); + break; + case 0x68: + out = ~(dst ^ (src ^ (pattern | ~(dst | src)))); + break; + case 0x69: + out = ~(pattern ^ (dst ^ src)); + break; + case 0x6a: + out = dst ^ (pattern & src); + break; + case 0x6b: + out = ~(pattern ^ (src ^ (dst & (pattern | src)))); + break; + case 0x6c: + out = src ^ (dst & pattern); + break; + case 0x6d: + out = ~(pattern ^ (dst ^ (src & (pattern | dst)))); + break; + case 0x6e: + out = src ^ (dst & (pattern | ~src)); + break; + case 0x6f: + out = ~(pattern & ~(dst ^ src)); + break; + case 0x70: + out = pattern & ~(dst & src); + break; + case 0x71: + out = ~(src ^ ((src ^ dst) & (pattern ^ dst))); + break; + case 0x72: + out = src ^ (dst | (pattern ^ src)); + break; + case 0x73: + out = ~(src & (dst | ~pattern)); + break; + case 0x74: + out = dst ^ (src | (pattern ^ dst)); + break; + case 0x75: + out = ~(dst & (src | ~pattern)); + break; + case 0x76: + out = src ^ (dst | (pattern & ~src)); + break; + case 0x77: + out = ~(dst & src); + break; + case 0x78: + out = pattern ^ (dst & src); + break; + case 0x79: + out = ~(dst ^ (src ^ (pattern & (dst | src)))); + break; + case 0x7a: + out = dst ^ (pattern & (src | ~dst)); + break; + case 0x7b: + out = ~(src & ~(dst ^ pattern)); + break; + case 0x7c: + out = src ^ (pattern & (dst | ~src)); + break; + case 0x7d: + out = ~(dst & ~(pattern ^ src)); + break; + case 0x7e: + out = (src ^ pattern) | (dst ^ src); + break; + case 0x7f: + out = ~(dst & (pattern & src)); + break; + case 0x80: + out = dst & (pattern & src); + break; + case 0x81: + out = ~((src ^ pattern) | (dst ^ src)); + break; + case 0x82: + out = dst & ~(pattern ^ src); + break; + case 0x83: + out = ~(src ^ (pattern & (dst | ~src))); + break; + case 0x84: + out = src & ~(dst ^ pattern); + break; + case 0x85: + out = ~(pattern ^ (dst & (src | ~pattern))); + break; + case 0x86: + out = dst ^ (src ^ (pattern & (dst | src))); + break; + case 0x87: + out = ~(pattern ^ (dst & src)); + break; + case 0x88: + out = dst & src; + break; + case 0x89: + out = ~(src ^ (dst | (pattern & ~src))); + break; + case 0x8a: + out = dst & (src | ~pattern); + break; + case 0x8b: + out = ~(dst ^ (src | (pattern ^ dst))); + break; + case 0x8c: + out = src & (dst | ~pattern); + break; + case 0x8d: + out = ~(src ^ (dst | (pattern ^ src))); + break; + case 0x8e: + out = src ^ ((src ^ dst) & (pattern ^ dst)); + break; + case 0x8f: + out = ~(pattern & ~(dst & src)); + break; + case 0x90: + out = pattern & ~(dst ^ src); + break; + case 0x91: + out = ~(src ^ (dst & (pattern | ~src))); + break; + case 0x92: + out = dst ^ (pattern ^ (src & (dst | pattern))); + break; + case 0x93: + out = ~(src ^ (pattern & dst)); + break; + case 0x94: + out = pattern ^ (src ^ (dst & (pattern | src))); + break; + case 0x95: + out = ~(dst ^ (pattern & src)); + break; + case 0x96: + out = dst ^ (pattern ^ src); + break; + case 0x97: + out = pattern ^ (src ^ (dst | ~(pattern | src))); + break; + case 0x98: + out = ~(src ^ (dst | ~(pattern | src))); + break; + case 0x99: + out = ~(dst ^ src); + break; + case 0x9a: + out = dst ^ (pattern & ~src); + break; + case 0x9b: + out = ~(src ^ (dst & (pattern | src))); + break; + case 0x9c: + out = src ^ (pattern & ~dst); + break; + case 0x9d: + out = ~(dst ^ (src & (pattern | dst))); + break; + case 0x9e: + out = dst ^ (src ^ (pattern | (dst & src))); + break; + case 0x9f: + out = ~(pattern & (dst ^ src)); + break; + case 0xa0: + out = dst & pattern; + break; + case 0xa1: + out = ~(pattern ^ (dst | (src & ~pattern))); + break; + case 0xa2: + out = dst & (pattern | ~src); + break; + case 0xa3: + out = ~(dst ^ (pattern | (src ^ dst))); + break; + case 0xa4: + out = ~(pattern ^ (dst | ~(src | pattern))); + break; + case 0xa5: + out = ~(pattern ^ dst); + break; + case 0xa6: + out = dst ^ (src & ~pattern); + break; + case 0xa7: + out = ~(pattern ^ (dst & (src | pattern))); + break; + case 0xa8: + out = dst & (pattern | src); + break; + case 0xa9: + out = ~(dst ^ (pattern | src)); + break; + case 0xaa: + out = dst; + break; + case 0xab: + out = dst | ~(pattern | src); + break; + case 0xac: + out = src ^ (pattern & (dst ^ src)); + break; + case 0xad: + out = ~(dst ^ (pattern | (src & dst))); + break; + case 0xae: + out = dst | (src & ~pattern); + break; + case 0xaf: + out = dst | ~pattern; + break; + case 0xb0: + out = pattern & (dst | ~src); + break; + case 0xb1: + out = ~(pattern ^ (dst | (src ^ pattern))); + break; + case 0xb2: + out = src ^ ((src ^ pattern) | (dst ^ src)); + break; + case 0xb3: + out = ~(src & ~(dst & pattern)); + break; + case 0xb4: + out = pattern ^ (src & ~dst); + break; + case 0xb5: + out = ~(dst ^ (pattern & (src | dst))); + break; + case 0xb6: + out = dst ^ (pattern ^ (src | (dst & pattern))); + break; + case 0xb7: + out = ~(src & (dst ^ pattern)); + break; + case 0xb8: + out = pattern ^ (src & (dst ^ pattern)); + break; + case 0xb9: + out = ~(dst ^ (src | (pattern & dst))); + break; + case 0xba: + out = dst | (pattern & ~src); + break; + case 0xbb: + out = dst | ~src; + break; + case 0xbc: + out = src ^ (pattern & ~(dst & src)); + break; + case 0xbd: + out = ~((src ^ dst) & (pattern ^ dst)); + break; + case 0xbe: + out = dst | (pattern ^ src); + break; + case 0xbf: + out = dst | ~(pattern & src); + break; + case 0xc0: + out = pattern & src; + break; + case 0xc1: + out = ~(src ^ (pattern | (dst & ~src))); + break; + case 0xc2: + out = ~(src ^ (pattern | ~(dst | src))); + break; + case 0xc3: + out = ~(pattern ^ src); + break; + case 0xc4: + out = src & (pattern | ~dst); + break; + case 0xc5: + out = ~(src ^ (pattern | (dst ^ src))); + break; + case 0xc6: + out = src ^ (dst & ~pattern); + break; + case 0xc7: + out = ~(pattern ^ (src & (dst | pattern))); + break; + case 0xc8: + out = src & (dst | pattern); + break; + case 0xc9: + out = ~(src ^ (pattern | dst)); + break; + case 0xca: + out = dst ^ (pattern & (src ^ dst)); + break; + case 0xcb: + out = ~(src ^ (pattern | (dst & src))); + break; + case 0xcc: + out = src; + break; + case 0xcd: + out = src | ~(dst | pattern); + break; + case 0xce: + out = src | (dst & ~pattern); + break; + case 0xcf: + out = src | ~pattern; + break; + case 0xd0: + out = pattern & (src | ~dst); + break; + case 0xd1: + out = ~(pattern ^ (src | (dst ^ pattern))); + break; + case 0xd2: + out = pattern ^ (dst & ~src); + break; + case 0xd3: + out = ~(src ^ (pattern & (dst | src))); + break; + case 0xd4: + out = src ^ ((src ^ pattern) & (pattern ^ dst)); + break; + case 0xd5: + out = ~(dst & ~(pattern & src)); + break; + case 0xd6: + out = pattern ^ (src ^ (dst | (pattern & src))); + break; + case 0xd7: + out = ~(dst & (pattern ^ src)); + break; + case 0xd8: + out = pattern ^ (dst & (src ^ pattern)); + break; + case 0xd9: + out = ~(src ^ (dst | (pattern & src))); + break; + case 0xda: + out = dst ^ (pattern & ~(src & dst)); + break; + case 0xdb: + out = ~((src ^ pattern) & (dst ^ src)); + break; + case 0xdc: + out = src | (pattern & ~dst); + break; + case 0xdd: + out = src | ~dst; + break; + case 0xde: + out = src | (dst ^ pattern); + break; + case 0xdf: + out = src | ~(dst & pattern); + break; + case 0xe0: + out = pattern & (dst | src); + break; + case 0xe1: + out = ~(pattern ^ (dst | src)); + break; + case 0xe2: + out = dst ^ (src & (pattern ^ dst)); + break; + case 0xe3: + out = ~(pattern ^ (src | (dst & pattern))); + break; + case 0xe4: + out = src ^ (dst & (pattern ^ src)); + break; + case 0xe5: + out = ~(pattern ^ (dst | (src & pattern))); + break; + case 0xe6: + out = src ^ (dst & ~(pattern & src)); + break; + case 0xe7: + out = ~((src ^ pattern) & (pattern ^ dst)); + break; + case 0xe8: + out = src ^ ((src ^ pattern) & (dst ^ src)); + break; + case 0xe9: + out = ~(dst ^ (src ^ (pattern & ~(dst & src)))); + break; + case 0xea: + out = dst | (pattern & src); + break; + case 0xeb: + out = dst | ~(pattern ^ src); + break; + case 0xec: + out = src | (dst & pattern); + break; + case 0xed: + out = src | ~(dst ^ pattern); + break; + case 0xee: + out = dst | src; + break; + case 0xef: + out = src | (dst | ~pattern); + break; + case 0xf0: + out = pattern; + break; + case 0xf1: + out = pattern | ~(dst | src); + break; + case 0xf2: + out = pattern | (dst & ~src); + break; + case 0xf3: + out = pattern | ~src; + break; + case 0xf4: + out = pattern | (src & ~dst); + break; + case 0xf5: + out = pattern | ~dst; + break; + case 0xf6: + out = pattern | (dst ^ src); + break; + case 0xf7: + out = pattern | ~(dst & src); + break; + case 0xf8: + out = pattern | (dst & src); + break; + case 0xf9: + out = pattern | ~(dst ^ src); + break; + case 0xfa: + out = dst | pattern; + break; + case 0xfb: + out = dst | (pattern | ~src); + break; + case 0xfc: + out = pattern | src; + break; + case 0xfd: + out = pattern | (src | ~dst); + break; + case 0xfe: + out = dst | (pattern | src); + break; + case 0xff: + out = ~0; + break; + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 5fd450260..d0d30c66d 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -1,33 +1,54 @@ /* - * 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 Core rendering code (Software version) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ +* 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 Core rendering code (Software version) +* +* +* +* Authors: Connor Hyde, I need a better email address ;^) +* +* Copyright 2024-2025 Connor Hyde +*/ - #include - #include - #include - #include - #include <86box/86box.h> - #include <86box/device.h> - #include <86box/mem.h> - #include <86box/pci.h> - #include <86box/rom.h> - #include <86box/video.h> - #include <86box/nv/vid_nv.h> - #include <86box/nv/vid_nv3.h> - #include <86box/nv/classes/vid_nv3_classes.h> +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> +#include <86box/nv/classes/vid_nv3_classes.h> - - \ No newline at end of file + +/* Render Core: Performs a ROP */ +void nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, uint32_t pen, nv3_render_operation_type rop) +{ + switch (rop) + { + case nv3_rop_blackness: + return 0; + case nv3_rop_srcand: + + case nv3_rop_srccopy: + return src; + case nv3_rop_dstcopy: + return dst; // do nothing + case nv3_rop_dstinvert: + return !dst; + case nv3_rop_xor: + return src ^ dst; + case nv3_rop_whiteness: + return 1; + + } +} \ No newline at end of file From 9a863caa23794b081f4e6831ab9f4fbafd366a13 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 17 Mar 2025 00:37:45 +0000 Subject: [PATCH 118/274] Fix rop core. Implement some basic image in memory methods. --- .../86box/nv/classes/vid_nv3_classes.h | 5 +++-- src/include/86box/nv/vid_nv3.h | 7 +++--- src/utils/video/video_rop.c | 10 ++++++--- .../classes/nv3_class_01c_image_in_memory.c | 14 +++++++++++- src/video/nv/nv3/render/nv3_render_core.c | 21 ++---------------- src/video/nv/nv3/subsystems/nv3_pgraph.c | 22 ++++++++++++++++++- 6 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index c015d0659..39c03b295 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -81,6 +81,7 @@ typedef enum nv3_pgraph_class_e #define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x0304 #define NV3_IMAGE_IN_MEMORY_PITCH 0x0308 #define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET 0x030C +#define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END 22 #define NV3_W95TXT_COLORA 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. @@ -118,14 +119,14 @@ typedef struct nv3_color_argb_32_s } nv3_color_argb_32_t; /* 30-bit colour format for internal PGRAPH use */ -typedef struct nv3_color_x3a10g10b10_s +typedef struct nv3_color_x2a10g10b10_s { uint8_t reserved : 1; bool alpha_if_chroma_key_otherwise_reserved2 : 1; // 1-bit ALPHA if chroma key, OTHERWISE USELESS and IGNORE uint16_t r : 10; uint16_t g : 10; uint16_t b : 10; -} nv3_color_x3a10g10b10_t; +} nv3_color_x2a10g10b10_t; /* 16-bit A4R4G4B4 colour format */ typedef struct nv3_color_16_a4r4g4b4_s diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 3c99782e6..fb080f1cd 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1114,17 +1114,18 @@ typedef struct nv3_pgraph_s nv3_position_16_bigy_t dst_canvas_min; nv3_position_16_bigy_t dst_canvas_max; // Pattern stuff - nv3_color_x3a10g10b10_t pattern_color_0_0; + nv3_color_x2a10g10b10_t pattern_color_0_0; uint32_t pattern_color_0_1; // only 7:0 relevant - nv3_color_x3a10g10b10_t pattern_color_1_0; + nv3_color_x2a10g10b10_t pattern_color_1_0; uint32_t pattern_color_1_1; // only 7:0 relevant uint32_t pattern_bitmap_high; // high part of pattern bitmap for blit uint32_t pattern_bitmap_low; uint32_t pattern_shape; // may need to be an enum - 0=8x8, 1=64x1, 2=1x64 uint32_t plane_mask; // only 7:0 relevant - nv3_color_x3a10g10b10_t chroma_key; // color key + nv3_color_x2a10g10b10_t chroma_key; // color key uint32_t beta_factor; nv3_pgraph_dma_settings_t dma_settings; + uint8_t rop; // Current GDI Ternary Render Operation nv3_pgraph_clip_misc_settings_t clip_misc_settings; nv3_notifier_t notifier; nv3_position_16_bigy_t clip0_min; diff --git a/src/utils/video/video_rop.c b/src/utils/video/video_rop.c index b6350b2e9..8bfdc9627 100644 --- a/src/utils/video/video_rop.c +++ b/src/utils/video/video_rop.c @@ -10,8 +10,10 @@ This is currently used in the following graphics cards: Tseng Labs ET4000/32p, Cirrus Logic CL-GD54xx, 3dfx Voodoo Banshee/Voodoo 3, Trident TGUI9440, S3 ViRGE, C&T 69000, ATI Mach64, and NVidia RIVA 128 */ -int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src, int32_t out) -{ +int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src) +{ + uint32_t out = 0x00; + switch (rop) { case 0x00: @@ -782,5 +784,7 @@ int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t case 0xff: out = ~0; break; - } + } + + return out; } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index a74313276..b42583f7a 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -28,10 +28,22 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + case NV3_IMAGE_IN_MEMORY_COLOR_FORMAT: + break; + /* Pitch - length between scanlines */ + case NV3_IMAGE_IN_MEMORY_PITCH: + nv3->pgraph.image_in_memory.pitch = param & 0x1FF0; + nv_log("Image in Memory PITCH=0x%04x", nv3->pgraph.image_in_memory.pitch); + break; + /* Byte offset in GPU VRAM of top left pixel (22:0) */ + case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: + nv3->pgraph.image_in_memory.linear_address = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); + nv_log("Image in Memory TOP_LEFT_OFFSET=0x%08x", nv3->pgraph.image_in_memory.linear_address); + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index d0d30c66d..426cf2c9d 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -28,27 +28,10 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> #include <86box/nv/classes/vid_nv3_classes.h> - +#include <86box/utils/video_stdlib.h> /* Render Core: Performs a ROP */ void nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, uint32_t pen, nv3_render_operation_type rop) { - switch (rop) - { - case nv3_rop_blackness: - return 0; - case nv3_rop_srcand: - - case nv3_rop_srccopy: - return src; - case nv3_rop_dstcopy: - return dst; // do nothing - case nv3_rop_dstinvert: - return !dst; - case nv3_rop_xor: - return src ^ dst; - case nv3_rop_whiteness: - return 1; - - } + return video_rop_gdi_ternary(rop, dst, pattern, src); } \ 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 index 802d98d10..510d8c1d3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -70,7 +70,7 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_PATTERN_BITMAP_HIGH, "PGRAPH Pattern Bitmap (High 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_BITMAP_LOW, "PGRAPH Pattern Bitmap (Low 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_SHAPE, "PGRAPH Pattern Shape (1:0 - 0=8x8, 1=64x1, 2=1x64)", NULL, NULL}, - { NV3_PGRAPH_ROP3, "PGRAPH Render Operation ROP3 (2^3 bits = 256 possible operations)", NULL, NULL}, + { NV3_PGRAPH_ROP3, "PGRAPH GDI Ternary Render Operation ROP3 (2^3 bits = 256 possible operations)", NULL, NULL}, { NV3_PGRAPH_PLANE_MASK, "PGRAPH Current Plane Mask (7:0)", NULL, NULL}, { NV3_PGRAPH_CHROMA_KEY, "PGRAPH Chroma Key (17:0) (Bit 30 = Alpha, 29:20 = Red, 19:10 = Green, 9:0 = Blue)", NULL, NULL}, { NV3_PGRAPH_BETA, "PGRAPH Beta factor", NULL, NULL }, @@ -207,6 +207,16 @@ uint32_t nv3_pgraph_read(uint32_t address) case NV3_PGRAPH_BETA: ret = nv3->pgraph.beta_factor; break; + // Todo: Massive table of ROP IDs or at least known ones? + case NV3_PGRAPH_ROP3: + ret = nv3->pgraph.rop; + break; + case NV3_PGRAPH_CHROMA_KEY: + ret = *(uint32_t*)&nv3->pgraph.chroma_key; + break; + case NV3_PGRAPH_PLANE_MASK: + ret = nv3->pgraph.plane_mask; + break; // DMA case NV3_PGRAPH_DMA: ret = *(uint32_t*)&nv3->pgraph.dma_settings; @@ -400,6 +410,16 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) case NV3_PGRAPH_BETA: nv3->pgraph.beta_factor = value; break; + // Todo: Massive table of ROP IDs or at least known ones? + case NV3_PGRAPH_ROP3: + nv3->pgraph.rop = value & 0xFF; + break; + case NV3_PGRAPH_CHROMA_KEY: + nv3->pgraph.chroma_key = *(nv3_color_x2a10g10b10_t*)value; + break; + case NV3_PGRAPH_PLANE_MASK: + nv3->pgraph.plane_mask = value; + break; // DMA case NV3_PGRAPH_DMA: *(uint32_t*)&nv3->pgraph.dma_settings = value; From 84f82351618f22bf06b89ddc41e28f7c7e4ab1ef Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 17 Mar 2025 01:15:02 +0000 Subject: [PATCH 119/274] Implement bpixel, bformat, bpitch. Actually apply these to class 0x1C mthd 0300,0308,030c. Temporary ROP code too. --- .../B = BUFFER. REMEMBER THESE!!!.txt | 1 + doc/nvidia_notes/NV128.xlsx | Bin 9325 -> 9550 bytes doc/nvidia_notes/NV3 DMA Engine.txt | 3 ++ .../86box/nv/classes/vid_nv3_classes.h | 8 +-- src/include/86box/nv/vid_nv3.h | 50 ++++++++++++++++-- src/include/86box/utils/video_stdlib.h | 2 +- .../classes/nv3_class_01c_image_in_memory.c | 39 ++++++++++++-- src/video/nv/nv3/render/nv3_render_core.c | 3 +- 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt diff --git a/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt b/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt new file mode 100644 index 000000000..e8f183240 --- /dev/null +++ b/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt @@ -0,0 +1 @@ +B = BUFFER. REMEMBER THESE!!! \ No newline at end of file diff --git a/doc/nvidia_notes/NV128.xlsx b/doc/nvidia_notes/NV128.xlsx index e293a97ebdeea621058b4c580a4a72be07c06e12..1cb14fdcc0d22153a3862cf106641e349aea72c8 100644 GIT binary patch delta 3677 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 I{~zLi03YhQQ~&?~ delta 3466 zcmZ9Pc{J1y_s7SKZDg55WGvB)eJ!D^*^;g7*(YRQMidesTa;}wV~};iD6)o;ee7h7 z7-LN+Ym~Cf@B4Iq=X{^@+&}Ig_q_kQ@B2FU-XZ->y~+>t(8giS5*i2yM8%EKBY}W3 z1gtosm^AvmWbHT7bUDJT6zu9VR#*cW!iBx)f;_W?yBMwH1L?>s9|R@_y{puaX&tpUa;wHB*KEugXG0^h!Z|vl*DDM zjm9J0ts!an7gxte@O85ie*Mc;w~2Et08SyB~0a`%I{Vys=H8J`6|e3 zx{+`pwn8w1y zg`+Y5K9qB03)xE4BX@)dTqicvT(>5=XG%(QzHbF_22?$3H}pRLw3lm8RYs}VBgWP9 zhkj8YaQis=j)!kk5cx!^%jYOeG@{s$UM|bTIOPasz5hJeG5x(k(P2k3olwqw zB5Wv(9=K0OGS0 z!e!r;gW${URep>0T$AA(jgOW>%T<57eOKWgblZ`XJ&>bl{8VS{aMbXC?)3CkuLc}h#&YkLqD5Hs{GufAZc0%DJ#g?iTa&Eo-9Ss(|+Fn&A2#P;z?UEETu>Wt=Ck-;=6szhUIljEC00Dw0SzF;)^K*W7jq{m!aYM z$nRAfhQu>=abMt;K0(2!*LJlid8^^|&(=rG`Pf1i{W)0ZPGm=BZOttEZOx>V4`D}g zG&;X%U%VbG1OoZZA?Q*(Sng0 z#x{GMkZpnSJHD^XfUl1CEM1$sv0R$(6i!D7L?KJxfBY;iC!7WPt}wD!T{}TF}D97;ZZ+>;i0k2Y8ZhqjU70gNMVUWH1O-=hn5 z!Bu{-aR?5KuJUsaj^v+5l&;xd=!xl3nQ;8ST>*mtjPpg6;mhZWw2sJ>gct56VB@F2-?=noKtHCYw9>1>w3zltbT z2y#}xU=|r@eDiU{3d%exex#Z>g#6c#LspFe?{e4q$sb`U84)qU-Vq%`5dT7P%rYZ?W+6h>^GD#UhP!VE9;i*ZTsa{7$!za zW{;rswRO6bpDUA1B13rBn7qUXJm{Z1KNP{zKKy>=-ERp}RGv|{Y(Sus(R;S-MK{`u zW&Cn9%Nm8Pk*P%bRa?VaZFog+g*^4(hP={e63tj}aUcy#b1S{&kD=7=>@<4pxOXk- zRM%p=(Y%{XyuBEeg2%?#((Q>H(iP&&hPHgh0GT|LA`%Nvfd<$jxVz)V!&_st5AVlO zd?f$C$~ySTu^I=!N#z?cwtbnL-N<shtKv-+2f#iX^YX%{^uh}y6X{;p- zN&;@R&oW9Z@XsIoVgun)ARGXt(Uz1+0|I@e1%WvJ9|re(5AM5|1wQcbb^FUiOsdK4 zc3D`@ywNdhy4q@VAe|0knwchjpTHf}lD>&bvR9r?Cgjbrb~5yix>%1kI#!&_+slN{ zSg@1aOGm^7OS#T!;MXQwN_#JhbDq^Lz?D&@}+~$NCc{Z5YBU-a*B3-u9$U zHFd^M=a!p<4BKSIE4RwSJ-wovDv|kb5t}zZzOb|t1=stg8uUh-bx+;*L?8O;IZKRA z5)-~*pz9d2hv+Y*BIzWL^=rYev9@PSItiLqYwfw<1l`r{9GfdukVQX2E-4x6uihrqr*rBk@I0A)H2|?(<@)pE2ru^Rn~4&m>@>Z zgoiGpH5HF!ql*<7t>VJ>NV}mst(?F8_83TZhsq%x&m5O+JRw83+0;?H=W!m(ti7F# z(zc$15`wL4-Qiw@Beb;rklT)&l{eYM;Y6W2g^ zhRg1LWkTF=Qe12%`C81mI#b0@KtsXYJsqm@EKPC;Iuc%)YG&Zi_G?^Q`zFHeoN3s~ zV-Py_RV;nXRV5~&O{17C;L)XQ0TIU-Y4)a^UdQpdSH{XKn8%Pwf%# diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt index fe2d61508..380e0281e 100644 --- a/doc/nvidia_notes/NV3 DMA Engine.txt +++ b/doc/nvidia_notes/NV3 DMA Engine.txt @@ -49,3 +49,6 @@ call of mthdCreate for ***DRIVER*** CLASS ID: a7b44, check ptr + + + diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 39c03b295..e1458671e 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -1145,17 +1145,17 @@ typedef enum nv3_object_class_01C_pixel_format_e { // Y8P4 // 12-bits (Y8 - Planar YUV 8 bits (Y value only), 4 bits of indexed colour too? - nv3_m2mt_pixel_format_le_y8_p4 = 0x1010000, + nv3_image_in_memory_pixel_format_le_y8_p4 = 0x1010000, // Y16P2 // 16-bits (Y16) - Planar YUV 16 bits (Y value only), 2 bits of indexed colour too? - nv3_m2mt_pixel_format_le_y16_p2 = 0x1010101, + nv3_image_in_memory_pixel_format_le_y16_p2 = 0x1010101, /* 1 unused bit, 555 15-bit format, p2(?) */ - nv3_m2mt_pixel_format_x1r5g5b5_p2 = 0x1000000, + nv3_image_in_memory_pixel_format_x1r5g5b5_p2 = 0x1000000, // X8G8B8R8, 24-bit colour (or 24-bit colour with alpha) - nv3_m2mt_pixel_format_x8g8b8r8 = 0x1, + nv3_image_in_memory_pixel_format_x8g8b8r8 = 0x1, } nv3_object_class_01C_pixel_format; typedef struct nv3_object_class_01C diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index fb080f1cd..49bc8c64e 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 13 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 17 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -454,6 +454,8 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_START 0x400000 // Scene graph for 2d/3d rendering...the most important part // PGRAPH Core +#define NV3_PGRAPH_MAX_BUFFERS 4 + // For these debug registers, 0=Disabled, 1=Enabled // Debug 0: General @@ -532,7 +534,25 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 //todo: add what this does #define NV3_PGRAPH_INTR_EN_1 0x400144 // Interrupt Control for PGRAPH #2 (it can receive two at onc) -#define NV3_PGRAPH_CONTEXT_SWITCH 0x400180 // DMA context switcher +#define NV3_PGRAPH_CONTEXT_SWITCH 0x400180 // Holds the current PGRAPH context, switched by context switching + +/* Contextual information for pgraph */ +#define NV3_PGRAPH_CONTEXT_SWITCH_COLOR_FORMAT 2 // Holds the current color format used for drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_ALPHA 3 // Holds a boolean if alpha transparency is currently enabled in drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_MONO_FORMAT 8 // Holds the current color format used for monochome drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_DAC_BYPASS 9 // Holds if PRAMDAC should be bypassed, and an external DAC drawn. +#define NV3_PGRAPH_CONTEXT_SWITCH_Z_WRITE 12 // Holds if we should write back to the zbuffer. +#define NV3_PGRAPH_CONTEXT_SWITCH_CHROMA_KEY 13 // Holds the current chroma mask used for drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_PLANE_MASK 14 // Holds the current plane mask used for drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_USER_CLIP 15 // Holds the user-specified clipping information used for drawing operations. +#define NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER 16 // Holds the buffer ID used for drawing operation (i.e. which bpixel/bpitch/boffset index to use) +#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED 20 // Holds a boolean indicating if buffer 0 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED 21 // Holds a boolean indicating if buffer 1 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED 22 // Holds a boolean indicating if buffer 2 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED 23 // Holds a boolean indicating if buffer 3 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CONTEXT_SWITCH_PATCH_CONFIG 24 // Something to do with an operation to do during a patchcord? +#define NV3_PGRAPH_CONTEXT_SWITCH_VOLATILE 31 // HUH + #define NV3_PGRAPH_CONTEXT_CONTROL 0x400190 // DMA context control #define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename #define NV3_PGRAPH_CONTEXT_CACHE(i) 0x4001A0+(i*4) // Context Cache @@ -1082,6 +1102,23 @@ typedef struct nv3_pgraph_status_s } nv3_pgraph_status_t; +/* All of this B* stuff is registers at 400630..40065c and 4006a8 in reality, easier to implement it like this + BPixel = Internal Binary Representation of the pixel within the architecture +*/ +#define NV3_BPIXEL_FORMAT 0 +#define NV3_BPIXEL_FORMAT_IS_VALID 2 + +typedef enum nv3_pgraph_bpixel_format_e +{ + // Y16 + bpixel_fmt_y16 = 0, + // 8-bit colour + bpixel_fmt_8bit = 1, + // 16-bit colour + bpixel_fmt_16bit = 2, + // 32-bit colour (BGRA/ARGB) + bpixel_fmt_32bit = 3, +} nv3_pgraph_bpixel_format; // Graphics Subsystem typedef struct nv3_pgraph_s @@ -1126,14 +1163,19 @@ typedef struct nv3_pgraph_s uint32_t beta_factor; nv3_pgraph_dma_settings_t dma_settings; uint8_t rop; // Current GDI Ternary Render Operation + // SURFACE STUFF - PGRAPH CAN OPERATE ON 4 SURFACES/BUFFERS AT A TIME + uint32_t boffset[NV3_PGRAPH_MAX_BUFFERS]; // 22-bit linear VRAM offset for the start of a surface. + uint16_t bpitch[NV3_PGRAPH_MAX_BUFFERS]; // 12-bit linear VRAM offset for the pitch of a surfac.e + uint32_t bpixel[NV3_PGRAPH_MAX_BUFFERS]; // Pixel format for each possible surfaces. + // CLIP nv3_pgraph_clip_misc_settings_t clip_misc_settings; nv3_notifier_t notifier; nv3_position_16_bigy_t clip0_min; nv3_position_16_bigy_t clip0_max; nv3_position_16_bigy_t clip1_min; nv3_position_16_bigy_t clip1_max; - uint32_t fifo_access; - nv3_pgraph_status_t status; + uint32_t fifo_access; // Determines if PGRAPH can access PFIFO. + nv3_pgraph_status_t status; // Current status of the 3D engine. uint32_t trapped_address; uint32_t trapped_data; uint32_t trapped_instance; diff --git a/src/include/86box/utils/video_stdlib.h b/src/include/86box/utils/video_stdlib.h index f8e2ed5af..3605631f1 100644 --- a/src/include/86box/utils/video_stdlib.h +++ b/src/include/86box/utils/video_stdlib.h @@ -17,4 +17,4 @@ /* ROP */ -int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src, int32_t out); \ No newline at end of file +int32_t video_rop_gdi_ternary(int32_t rop, int32_t dst, int32_t pattern, int32_t src); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index b42583f7a..547ffc441 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -30,19 +30,52 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { + /* We need this for a lot of methods, so may as well store it here. */ + uint32_t src_buffer_id = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + switch (method_id) { + /* Color format of the image */ case NV3_IMAGE_IN_MEMORY_COLOR_FORMAT: + // convert to how the bpixel registers represent surface + uint32_t real_format = 1; + + /* TODO: THIS CODE MIGHT BE NONSENSE + Convert between different internal representations of the pixel format, because Nvidia says: I WANT TO MAKE YOUR LIFE PAIN. + */ + switch (param) + { + case nv3_image_in_memory_pixel_format_x8g8b8r8: + real_format = 3; //32bit + // no change + break; + case nv3_image_in_memory_pixel_format_x1r5g5b5_p2: + real_format = 2; + break; + case nv3_image_in_memory_pixel_format_le_y16_p2: + real_format = 0; + break; + } + + /* Set the format */ + + nv3->pgraph.bpixel[src_buffer_id] = ((real_format & 0x03) | NV3_BPIXEL_FORMAT_IS_VALID); + nv_log("Image in Memory BUF%d COLOR_FORMAT=0x%04x", src_buffer_id, param); + break; /* Pitch - length between scanlines */ case NV3_IMAGE_IN_MEMORY_PITCH: + nv3->pgraph.image_in_memory.pitch = param & 0x1FF0; - nv_log("Image in Memory PITCH=0x%04x", nv3->pgraph.image_in_memory.pitch); + nv3->pgraph.bpitch[src_buffer_id] = param & 0x1FF0; // 12:0 + + nv_log("Image in Memory BUFL%d PITCH=0x%04x", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); break; /* Byte offset in GPU VRAM of top left pixel (22:0) */ case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: - nv3->pgraph.image_in_memory.linear_address = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); - nv_log("Image in Memory TOP_LEFT_OFFSET=0x%08x", nv3->pgraph.image_in_memory.linear_address); + nv3->pgraph.boffset[src_buffer_id] = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); + + nv_log("Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x", src_buffer_id, nv3->pgraph.image_in_memory.linear_address); break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 426cf2c9d..8b5771f36 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -27,11 +27,10 @@ #include <86box/video.h> #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> #include <86box/utils/video_stdlib.h> /* Render Core: Performs a ROP */ -void nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, uint32_t pen, nv3_render_operation_type rop) +uint32_t nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, nv3_render_operation_type rop) { return video_rop_gdi_ternary(rop, dst, pattern, src); } \ No newline at end of file From 08983a9da3722689224a948d9d771e193853a5d9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 17 Mar 2025 01:18:15 +0000 Subject: [PATCH 120/274] start actual notification code --- src/include/86box/nv/vid_nv3.h | 3 +++ src/video/nv/nv3/classes/nv3_class_shared_methods.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 49bc8c64e..957714799 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1549,6 +1549,9 @@ void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_co void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); void nv3_class_01c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); +// Notification Engine +void nv3_notify_if_needed(nv3_grobj_t grobj); + // NV3 PFIFO void nv3_pfifo_init(); uint32_t nv3_pfifo_read(uint32_t address); diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 555a180e6..663214cec 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -39,7 +39,7 @@ void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t c } } -void nv3_notify() +void nv3_notify_if_needed(nv3_grobj_t grobj) { } \ 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 index 510d8c1d3..117a88733 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -588,6 +588,8 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); return; } + + nv3_notify_if_needed(grobj); } /* Arbitrates graphics object submission to the right object types */ From a5b864c59c9fb2083c60c8757d67d0d47679ef12 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 20 Mar 2025 00:01:59 +0000 Subject: [PATCH 121/274] pull out the notifier object. --- doc/nvidia_notes/NV128.xlsx | Bin 9550 -> 9615 bytes src/include/86box/nv/vid_nv3.h | 23 +++++- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 30 +++---- src/video/nv/nv3/classes/nv3_class_names.c | 2 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 76 +++++++++++++++++- src/video/nv/nv3/nv3_core_arbiter.c | 3 +- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 7 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 5 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 6 +- 10 files changed, 124 insertions(+), 30 deletions(-) diff --git a/doc/nvidia_notes/NV128.xlsx b/doc/nvidia_notes/NV128.xlsx index 1cb14fdcc0d22153a3862cf106641e349aea72c8..919297d56816e5278deb74887f1c321198297f08 100644 GIT binary patch delta 2823 zcmYjTcQhM{7f$S5T00SYR4FA^6&0lwvu10n-NY)-=#a!}jh2eWY>iT+iWntUY0tZeyvvq~LsO4a7DP|C~7oq0^U5Xf_HHr4F3L&LZoT-B^Ld^VWOH;yEW`+{sSJmk-?KOJ zr@|_f&``5)tT}nG-oi)a=x1p;y&3uh*dmwtzi`Euv%Sa1rRT91#xa$*|u!Jg1 zNm#*d=Mz*A_<^L-WnMSyxv7gU#aWu7IT-PftK)3cmTyZs0PIs{!GRVV8 zCo+5Z1|1#mBFsi}u*hKyLe{}`e)yC+P1U|Uu$lGO-ftV_qGA>^S=#tzR>Oz{Pk%`x z%MAFI6I`0qsM`X2K%<@a{ZpoZ@xs1 zqPOgGZIi9VYdAI;%A;f>CG+k^4Sc;}=BLcvm&fV8iS?lMkrVq|b?JP`#B=i!LLF-5 zVUa$jsOTF<){_jjh?(-;4khj*1fj6n>~-wQV8ZgGMQIpqa3ZbC)ofikwH|7o#n5?S zWdRh5s>xh&#H@OEK00IRKiY}upe;oCH|r)HAP@*)rA0Gz61Hp z^Mu$tm#mn5yJU7FY{leARzBJt8MSxmofSqXgu_niCg{*q!YG{ZMwLS`4$9O}4yyaeTSBvgS(38!jB@M@V<2xrM?&LbmOsPP@C|R41pQY z>!l27!**MC$XNAi_hHPw27+)?=&h886nz_^M8<)%Cv-&&kOpA2Kb0e%1 zpCl@^N8}Ah>Bc7FV+hz&(R+nC@%@j&$!)Nn-@Ohu$m-AQ&dbNUZ7oIFPNs*Bb1sMU zYxovJ>(qgGZ9HRy-l`XO^g=B-f;dHgoitU6M^fr^bcW0vy-xx+z6LZY=aBJk+Iv2T zeE`T94XF(&Ir%zUjMYf^*0%kP-uRPbfU@@-^K0YWZ>~Npoh_q2cptaVX|m|1pRiEv zPds06XTR$QCtvir`Bvm-1Hgo$F6B9zUSBs99?D4C6X1(c1GK52w>b|v*zfbB-G#ft z=NMvt7S+y7cSP74F%@79740>zC{M%|;7|f|I{{hbP3RWfMTR~~>@a;xuA4+TY5g8I zex3&b`LQG!O{$wb{cv3%rNj!B+>47D2m-A>Hscf%cBN@N?@zZ?mi} zYLosXZ;LN`$o>PO{zdcke~+Kw=n&S>7RyK;0j^`5X;1Fr7EAF3F+%}xTZW- zYQB#A+*o}Dnw_Sw=V~{OHlffz)0l*Gl;ZrI{cw&u4#IObQ-op|0wx!wNNJ} z1_6pOt;(I3-VSkRB5_~Wh~xOwZ+O@$R}5X24`%t8+5S@I)X1t0#!h^|r3Pk)n0*7j zDx~$+VK$YIm{*mEtkO1#4MRC7KE>h9#)20I-1moq&MvZWjYLRf^~PT&PZdG1*m<>U zf=a@y7pD!5(>o4MH=^yTnXX>NXbu$mW0RFxYv{<-|Hv%xy2yJTktfcVM8+eiRn2 zDf_oc?M7~@))N;l?JzIuQoSC_zkHtQtgpyxM~zmf-?=^V0zYRD96(ht>0Vu%L5FI4 zc{6V!yN(6x1o9numU|G-eQr$iF+!Ltc}c;Sy_w-4v~X8MaF=gW+@?euEGJh*vuB(5ODxy~fXLHI|`3A;y2;#RB9d|Us5vnXgG#%Fp? zS5)MuE;xGGzbzJw2%1~TG@rgjb$(X$<7WObLwECi=G`hwXoGvZ_2QEA&K+ajKIlu- z-)_JXduGO|Ax%OFh_d*gE|@gOdSdda!95>m#Tm?QoqURqm0G;t=?$}TH#0u?$P z(sWVszU2_nbBHMC4)GPbA5SQE5|e4*U@dMDE}!;WBi?wZqg40gY`5|oDHGKjjRR_G zb{_UMyqCn?6T;;uyRg~i?L)&msqr(ia$kL zGn~8w)wa?^WEm!tZT_IIEL7EsJzH3&(mGXL{@UX`^2Ze#OhRl)ySE&w13 z_;1MZU=4W0vB$E?Oe6f*5q=@8p`0?%78@id1^WLvvH}2t|7`-i!j{P?{=GjeSxy+( Vid~U2WV$GYRgjlqJCON{`aj&>S?T}) delta 2785 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 I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ +* 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: Methods for class 0x17 (Direct3D 5.0 accelerated triangle with zeta buffer) +* +* +* +* Authors: Connor Hyde, I need a better email address ;^) +* +* Copyright 2024-2025 Connor Hyde +*/ #include #include diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c index ded63ece2..64e35d9b3 100644 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ b/src/video/nv/nv3/classes/nv3_class_names.c @@ -64,4 +64,4 @@ const char* nv3_class_names[] = "NV3 INVALID class 0x1D", "NV3 INVALID class 0x1E", "NV3 INVALID class 0x1F", -}; +}; \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 663214cec..abeb0571b 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -21,6 +21,7 @@ #include #include <86box/86box.h> #include <86box/device.h> +#include <86box/dma.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/rom.h> @@ -39,7 +40,78 @@ void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t c } } -void nv3_notify_if_needed(nv3_grobj_t grobj) + +/* Sees if any notification is required after an object method is executed. If so, executes it... */ +void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { + if (nv3->pgraph.notify_pending) + { + nv_log("Called nv3_notify_if_needed with nv3->pgraph.notify_pending already set. name=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); + nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); + nv3_debug_ramin_print_context_info(name, context); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + + // disable + nv3->pgraph.notify_pending = false; + return; + } + + // set up the NvNotification structure + nv3_notification_t notify = {0}; + notify.nanoseconds = nv3->ptimer.time; + notify.status = NV3_NOTIFICATION_STATUS_DONE_OK; // it should be fine to just signal that it's ok -} \ No newline at end of file + // this is completely speculative and i have no idea + notify.info32 = grobj.grobj_0; + + // notify object base=grobj_1 >> 12 + uint32_t notify_obj_base = grobj.grobj_1 >> 12; + + uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); + uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); + uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); + + /* extract some important information*/ + uint32_t info_adjust = notify_obj_info & 0xFFF; + bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; + uint8_t info_notification_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; + + /* paging information */ + bool page_is_present = notify_obj_page & 0x01; + bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); + uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF; + + // This code is temporary and will probably be moved somewhere else + // Print torns of debug info + #ifdef DEBUG + nv_log("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); + + nv_log("Notification Information:\n"); + nv_log("Adjust Value: 0x%08x\n", info_adjust); + (info_pt_present) ? nv_log("Pagetable Present: True\n") : nv_log("Pagetable Present: False\n"); + + switch (info_notification_target) + { + case NV3_NOTIFICATION_TARGET_NVM: + nv_log("Notification Target: VRAM\n"); + break; + case NV3_NOTIFICATION_TARGET_CART: + nv_log("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); + break; + case NV3_NOTIFICATION_TARGET_PCI: + (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log("Notification Target: PCI Bus\n") : nv_log("Notification Target: PCI Bus (On AGP card???)\n"); + break; + case NV3_NOTIFICATION_TARGET_AGP: + (nv3->nvbase.bus_generation == nv_bus_agp_1x + || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log("Notification Target: AGP Bus\n") : nv_log("Notification Target: AGP Bus (On PCI card???)\n"); + break; + } + + nv_log("Limit: 0x%08x", notify_obj_limit); + (page_is_present) ? nv_log("Page is present\n") : nv_log("Page is not present\n"); + (page_is_readwrite) ? nv_log("Page is read-write\n") : nv_log("Page is read-only\n"); + nv_log("Pageframe Address: 0x%08x", frame_value); + #endif + + // set up the dma transfer. we need to translate to a physical address. +} diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 17cca15df..e5e7ced6b 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -194,5 +194,4 @@ 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) {}; - +void nv3_vram_write(uint32_t address, uint32_t value) {}; \ 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 index d5d17fe2d..4ce686335 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -28,4 +28,9 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -/* Nvidia DMA Engine */ \ No newline at end of file +/* Nvidia DMA Engine */ + +void nv3_dma_translate_address() +{ + +} \ 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 index 435225bc2..92a643502 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -168,13 +168,12 @@ void nv3_pfb_config0_write(uint32_t val) uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; // i don't think 16:9 is supported - uint32_t new_pfb_vtotal = new_pfb_htotal * (4.0/3.0); - + uint32_t new_pfb_vtotal = new_pfb_htotal * (4.0/3.0); uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; + nv_log("Framebuffer Config Change\n"); nv_log("Horizontal Size=%d pixels\n", new_pfb_htotal); nv_log("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); - if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) nv_log("Bit Depth=8bpp\n"); diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 117a88733..5851d9301 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -589,7 +589,7 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe return; } - nv3_notify_if_needed(grobj); + nv3_notify_if_needed(param, method, context, grobj); } /* Arbitrates graphics object submission to the right object types */ diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 226f4d8ee..f85ae3f8e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -498,10 +498,11 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u } -#ifndef RELEASE_BUILD + // Prints out some informaiton about the object void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) { + #ifndef RELEASE_BUILD nv_log("Found object:\n"); nv_log("Name: 0x%04x\n", name); @@ -511,6 +512,5 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); nv_log("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); + #endif } - -#endif \ No newline at end of file From ac63ad436fdc3646a7c1328575383fb25fdf5bae Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 20 Mar 2025 00:40:29 +0000 Subject: [PATCH 122/274] Implement generic method execution. Implement unknown register write code --- src/include/86box/nv/vid_nv3.h | 20 ++- src/sound/snd_emu8k.c | 2 +- src/sound/snd_sb.c | 4 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 55 ++++-- src/video/nv/nv3/subsystems/nv3_pbus.c | 4 + src/video/nv/nv3/subsystems/nv3_pextdev.c | 4 + src/video/nv/nv3/subsystems/nv3_pfb.c | 4 + src/video/nv/nv3/subsystems/nv3_pfifo.c | 6 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 159 ++++++++++-------- src/video/nv/nv3/subsystems/nv3_pmc.c | 4 + src/video/nv/nv3/subsystems/nv3_pme.c | 5 + src/video/nv/nv3/subsystems/nv3_pramdac.c | 5 + src/video/nv/nv3/subsystems/nv3_ptimer.c | 4 + src/video/nv/nv3/subsystems/nv3_pvideo.c | 5 +- src/video/vid_voodoo.c | 2 +- 15 files changed, 181 insertions(+), 102 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 9c1a5628e..3756a47ed 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 17 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 20 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -521,9 +521,11 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_DEBUG_3_FORMAT_CHECK 22 #define NV3_PGRAPH_DEBUG_3_ALPHA_CHECK 24 +// Interrupt stuff #define NV3_PGRAPH_INTR_0 0x400100 #define NV3_PGRAPH_INTR_0_VBLANK 8 // Fired every frame #define NV3_PGRAPH_INTR_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? +#define NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY 28 // Fired on software notification #define NV3_PGRAPH_INTR_1 0x400104 #define NV3_PGRAPH_INTR_1_INVALID_METHOD 0 // Invalid method @@ -578,7 +580,14 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CHROMA_KEY 0x40062C #define NV3_PGRAPH_BETA 0x400640 // Beta factor (30:23 fractional, 22:0 before fraction) #define NV3_PGRAPH_DMA 0x400680 -#define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH + +// Current notification object for pgraph +#define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH +#define NV3_PGRAPH_NOTIFY_INSTANCE 0 +#define NV3_PGRAPH_NOTIFY_REQUEST_PENDING 16 +#define NV3_PGRAPH_NOTIFY_REQUEST_TYPE 20 +#define NV3_PGRAPH_NOTIFY_REQUEST_TYPE_HARDWARE 0x0 // anything else is software + #define NV3_PGRAPH_CLIP0_MIN 0x400690 // Clip for Blitting 0 Min #define NV3_PGRAPH_CLIP0_MAX 0x400694 // Clip for Blitting 0 Max #define NV3_PGRAPH_CLIP1_MIN 0x400698 // Clip for Blitting 1 Min @@ -882,11 +891,6 @@ typedef struct nv3_pci_config_s uint8_t int_line; } nv3_pci_config_t; -/* Notifier Engine */ -typedef struct nv3_notifier_s -{ - /* TODO */ -} nv3_notifier_t; // add enums for eac // Chip configuration @@ -1187,7 +1191,7 @@ typedef struct nv3_pgraph_s uint32_t bpixel[NV3_PGRAPH_MAX_BUFFERS]; // Pixel format for each possible surfaces. // CLIP nv3_pgraph_clip_misc_settings_t clip_misc_settings; - nv3_notifier_t notifier; + uint32_t notifier; bool notify_pending; // Determines if a notification is pending. nv3_position_16_bigy_t clip0_min; nv3_position_16_bigy_t clip0_max; diff --git a/src/sound/snd_emu8k.c b/src/sound/snd_emu8k.c index 822abeeaa..c32c16be1 100644 --- a/src/sound/snd_emu8k.c +++ b/src/sound/snd_emu8k.c @@ -1525,7 +1525,7 @@ emu8k_outw(uint16_t addr, uint16_t val, void *priv) default: break; } - emu8k_log("EMU8K WRITE: Unknown register write: %04X-%02X(%d/%d): %04X \n", addr, (emu8k->cur_reg) << 5 | emu8k->cur_voice, + emu8k_log("EMU8K WRITE: : Unknown register write: %04X-%02X(%d/%d): %04X \n", addr, (emu8k->cur_reg) << 5 | emu8k->cur_voice, emu8k->cur_reg, emu8k->cur_voice, val); } diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index e89946486..160e79881 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -798,7 +798,7 @@ sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv) break; default: - sb_log("sb_ct1335: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("sb_ct1335: : Unknown register write: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -899,7 +899,7 @@ sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *priv) break; default: - sb_log("sb_ct1345: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("sb_ct1345: : Unknown register write: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index abeb0571b..fcfe1e7c4 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -29,10 +29,31 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + // set up the current notification request/object] + // check for double notifiers. + case NV3_SET_NOTIFY: + if (nv3->pgraph.notify_pending) + { + nv_log("Executed method NV3_SET_NOTIFY with nv3->pgraph.notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); + nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); + nv3_debug_ramin_print_context_info(param, context); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + + // disable + nv3->pgraph.notify_pending = false; + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + /* may need to disable fifo in this state */ + return; + } + + // set a notify as pending. + nv3->pgraph.notifier = param; + nv3->pgraph.notify_pending = true; + break; default: nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); @@ -44,18 +65,19 @@ void nv3_generic_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t c /* Sees if any notification is required after an object method is executed. If so, executes it... */ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { - if (nv3->pgraph.notify_pending) - { - nv_log("Called nv3_notify_if_needed with nv3->pgraph.notify_pending already set. name=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); - nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); - nv3_debug_ramin_print_context_info(name, context); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - - // disable - nv3->pgraph.notify_pending = false; + if (!nv3->pgraph.notify_pending) return; - } + uint32_t current_notification_object = nv3->pgraph.notifier; + + // check for a software method (0 = hardware, 1 = software) + if (((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07) != 0) + { + nv_log("Software Notification, firing interrupt"); + nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY); + return; + } + // set up the NvNotification structure nv3_notification_t notify = {0}; notify.nanoseconds = nv3->ptimer.time; @@ -79,7 +101,8 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t /* paging information */ bool page_is_present = notify_obj_page & 0x01; bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); - uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF; + //uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF; + uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF000; // This code is temporary and will probably be moved somewhere else // Print torns of debug info @@ -99,18 +122,18 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t nv_log("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); break; case NV3_NOTIFICATION_TARGET_PCI: - (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log("Notification Target: PCI Bus\n") : nv_log("Notification Target: PCI Bus (On AGP card???)\n"); + (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log("Notification Target: PCI Bus\n") : nv_log("Notification Target: PCI Bus (On AGP card?)\n"); break; case NV3_NOTIFICATION_TARGET_AGP: (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log("Notification Target: AGP Bus\n") : nv_log("Notification Target: AGP Bus (On PCI card???)\n"); + || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log("Notification Target: AGP Bus\n") : nv_log("Notification Target: AGP Bus (On PCI card?)\n"); break; } - nv_log("Limit: 0x%08x", notify_obj_limit); + nv_log("Limit: 0x%08x\n", notify_obj_limit); (page_is_present) ? nv_log("Page is present\n") : nv_log("Page is not present\n"); (page_is_readwrite) ? nv_log("Page is read-write\n") : nv_log("Page is read-only\n"); - nv_log("Pageframe Address: 0x%08x", frame_value); + nv_log("Pageframe Address: 0x%08x\n", frame_value); #endif // set up the dma transfer. we need to translate to a physical address. diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 2594e0739..ca631edfc 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -122,6 +122,10 @@ void nv3_pbus_write(uint32_t address, uint32_t value) } } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } uint8_t nv3_pbus_rma_read(uint16_t addr) diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 6d8cadbf0..c26266a00 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -150,4 +150,8 @@ void nv3_pextdev_write(uint32_t address, uint32_t value) if (reg->on_write) reg->on_write(value); } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } \ 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 index 92a643502..f7d80a41a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -152,6 +152,10 @@ void nv3_pfb_write(uint32_t address, uint32_t value) } } } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } uint32_t nv3_pfb_config0_read() diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index a836fdd21..073599577 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -599,7 +599,6 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache1_entries[real_entry].subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; nv_log("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache1_entries[real_entry].subchannel, nv3->pfifo.cache1_entries[real_entry].method); } - } /* Handle some special memory areas */ else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) @@ -609,6 +608,10 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, val); } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } /* Trigger DMA for notifications if we need to */ nv3_pfifo_trigger_dma_if_required(); @@ -620,6 +623,7 @@ https://en.wikipedia.org/wiki/Gray_code WHY?????? IT'S NOT A TELEGRAPH IT'S A GPU????? Convert from a normal number to a total insanity number which is only used in PFIFO CACHE1 for ungodly and totally unknowable reasons +(Possibly it just makes it easier to implement in logic) I decided to use a lookup table to save everyone's time, also the numbers generated from the function that existed here before didn't make any sense diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 5851d9301..93fe8c32f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -472,6 +472,10 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) nv_log("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); nv3->pgraph.context_cache[entry] = value; } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } } @@ -506,6 +510,7 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe // we need to shift left by 4 to get the real address, something to do with the 16 byte unit of reversal uint32_t real_ramin_base = context.ramin_offset << 4; + // readin our grobj grobj.grobj_0 = nv3_ramin_read32(real_ramin_base, nv3); grobj.grobj_1 = nv3_ramin_read32(real_ramin_base + 4, nv3); grobj.grobj_2 = nv3_ramin_read32(real_ramin_base + 8, nv3); @@ -514,80 +519,90 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe nv_log("**** About to execute method **** method=0x%04x param=0x%08x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", method, param, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); - // By this point, we already ANDed the class ID to 0x1F. - // Send the grobj, the context, the method and the name off to actually be acted upon. - switch (class_id) + /* Methods below 0x104 are shared across all classids, so call generic_method for that*/ + if (method <= NV3_SET_NOTIFY) { - case nv3_pgraph_class01_beta_factor: - nv3_class_001_method(param, method, context, grobj); - break; - case nv3_pgraph_class02_rop: - nv3_class_002_method(param, method, context, grobj); - break; - case nv3_pgraph_class03_chroma_key: - nv3_class_003_method(param, method, context, grobj); - break; - case nv3_pgraph_class04_plane_mask: - nv3_class_004_method(param, method, context, grobj); - break; - case nv3_pgraph_class05_clipping_rectangle: - nv3_class_005_method(param, method, context, grobj); - break; - case nv3_pgraph_class06_pattern: - nv3_class_006_method(param, method, context, grobj); - break; - case nv3_pgraph_class07_rectangle: - nv3_class_007_method(param, method, context, grobj); - break; - case nv3_pgraph_class08_point: - nv3_class_008_method(param, method, context, grobj); - break; - case nv3_pgraph_class09_line: - nv3_class_009_method(param, method, context, grobj); - break; - case nv3_pgraph_class0a_lin: - nv3_class_00a_method(param, method, context, grobj); - break; - case nv3_pgraph_class0b_triangle: - nv3_class_00b_method(param, method, context, grobj); - break; - case nv3_pgraph_class0c_w95txt: - nv3_class_00c_method(param, method, context, grobj); - break; - case nv3_pgraph_class0d_m2mf: - nv3_class_00d_method(param, method, context, grobj); - break; - case nv3_pgraph_class0e_scaled_image_from_memory: - nv3_class_00e_method(param, method, context, grobj); - break; - case nv3_pgraph_class10_blit: - nv3_class_010_method(param, method, context, grobj); - break; - case nv3_pgraph_class11_image: - nv3_class_011_method(param, method, context, grobj); - break; - case nv3_pgraph_class12_bitmap: - nv3_class_012_method(param, method, context, grobj); - break; - case nv3_pgraph_class14_transfer2memory: - nv3_class_014_method(param, method, context, grobj); - break; - case nv3_pgraph_class15_stretched_image_from_cpu: - nv3_class_015_method(param, method, context, grobj); - break; - case nv3_pgraph_class17_d3d5tri_zeta_buffer: - nv3_class_017_method(param, method, context, grobj); - break; - case nv3_pgraph_class18_point_zeta_buffer: - nv3_class_018_method(param, method, context, grobj); - break; - case nv3_pgraph_class1c_image_in_memory: - nv3_class_01c_method(param, method, context, grobj); - break; - default: - fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); - return; + nv3_generic_method(param, method, context, grobj); } + else + { + // By this point, we already ANDed the class ID to 0x1F. + // Send the grobj, the context, the method and the name off to actually be acted upon. + switch (class_id) + { + case nv3_pgraph_class01_beta_factor: + nv3_class_001_method(param, method, context, grobj); + break; + case nv3_pgraph_class02_rop: + nv3_class_002_method(param, method, context, grobj); + break; + case nv3_pgraph_class03_chroma_key: + nv3_class_003_method(param, method, context, grobj); + break; + case nv3_pgraph_class04_plane_mask: + nv3_class_004_method(param, method, context, grobj); + break; + case nv3_pgraph_class05_clipping_rectangle: + nv3_class_005_method(param, method, context, grobj); + break; + case nv3_pgraph_class06_pattern: + nv3_class_006_method(param, method, context, grobj); + break; + case nv3_pgraph_class07_rectangle: + nv3_class_007_method(param, method, context, grobj); + break; + case nv3_pgraph_class08_point: + nv3_class_008_method(param, method, context, grobj); + break; + case nv3_pgraph_class09_line: + nv3_class_009_method(param, method, context, grobj); + break; + case nv3_pgraph_class0a_lin: + nv3_class_00a_method(param, method, context, grobj); + break; + case nv3_pgraph_class0b_triangle: + nv3_class_00b_method(param, method, context, grobj); + break; + case nv3_pgraph_class0c_w95txt: + nv3_class_00c_method(param, method, context, grobj); + break; + case nv3_pgraph_class0d_m2mf: + nv3_class_00d_method(param, method, context, grobj); + break; + case nv3_pgraph_class0e_scaled_image_from_memory: + nv3_class_00e_method(param, method, context, grobj); + break; + case nv3_pgraph_class10_blit: + nv3_class_010_method(param, method, context, grobj); + break; + case nv3_pgraph_class11_image: + nv3_class_011_method(param, method, context, grobj); + break; + case nv3_pgraph_class12_bitmap: + nv3_class_012_method(param, method, context, grobj); + break; + case nv3_pgraph_class14_transfer2memory: + nv3_class_014_method(param, method, context, grobj); + break; + case nv3_pgraph_class15_stretched_image_from_cpu: + nv3_class_015_method(param, method, context, grobj); + break; + case nv3_pgraph_class17_d3d5tri_zeta_buffer: + nv3_class_017_method(param, method, context, grobj); + break; + case nv3_pgraph_class18_point_zeta_buffer: + nv3_class_018_method(param, method, context, grobj); + break; + case nv3_pgraph_class1c_image_in_memory: + nv3_class_01c_method(param, method, context, grobj); + break; + default: + fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); + return; + } + } + + nv3_notify_if_needed(param, method, context, grobj); } diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index f67eb707b..24f0ad44a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -269,4 +269,8 @@ void nv3_pmc_write(uint32_t address, uint32_t value) nv_log("\n"); } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } \ 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 index e555c9752..d7f6f0cf9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -128,4 +128,9 @@ void nv3_pme_write(uint32_t address, uint32_t value) } } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } + } \ 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 index 6ff090716..c9430018f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -377,4 +377,9 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) } } } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } + } \ 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 index aa5942ad8..b5c278387 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -222,4 +222,8 @@ void nv3_ptimer_write(uint32_t address, uint32_t value) } } } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } \ 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 index db6e78a59..6f72da719 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -152,5 +152,8 @@ void nv3_pvideo_write(uint32_t address, uint32_t value) } } } - + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } } \ No newline at end of file diff --git a/src/video/vid_voodoo.c b/src/video/vid_voodoo.c index e45f112ed..8f6e8bf26 100644 --- a/src/video/vid_voodoo.c +++ b/src/video/vid_voodoo.c @@ -676,7 +676,7 @@ voodoo_writel(uint32_t addr, uint32_t val, void *priv) default: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) { - voodoo_log("Unknown register write in CMDFIFO mode %08x %08x\n", addr, val); + voodoo_log(": Unknown register write in CMDFIFO mode %08x %08x\n", addr, val); } else { voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val); } From fea521d3ac56be29c0d77a9d234bfd47aa652a69 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 20 Mar 2025 01:39:35 +0000 Subject: [PATCH 123/274] At least partially working notifiers that seem to do something --- .../nv/nv3/classes/nv3_class_shared_methods.c | 46 +++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 7 ++- src/video/nv/nv3/subsystems/nv3_pramin.c | 6 +-- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index fcfe1e7c4..2141f238b 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -69,13 +69,14 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t return; uint32_t current_notification_object = nv3->pgraph.notifier; + uint32_t notification_type = ((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07); // check for a software method (0 = hardware, 1 = software) - if (((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07) != 0) + if (notification_type != 0) { nv_log("Software Notification, firing interrupt"); nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY); - return; + //return; } // set up the NvNotification structure @@ -101,8 +102,7 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t /* paging information */ bool page_is_present = notify_obj_page & 0x01; bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); - //uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF; - uint32_t frame_value = (notify_obj_page >> NV3_NOTIFICATION_PAGE_FRAME_ADDRESS) & 0xFFFFF000; + uint32_t frame_base = notify_obj_page & 0xFFFFF000; // This code is temporary and will probably be moved somewhere else // Print torns of debug info @@ -133,8 +133,44 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t nv_log("Limit: 0x%08x\n", notify_obj_limit); (page_is_present) ? nv_log("Page is present\n") : nv_log("Page is not present\n"); (page_is_readwrite) ? nv_log("Page is read-write\n") : nv_log("Page is read-only\n"); - nv_log("Pageframe Address: 0x%08x\n", frame_value); + nv_log("Pageframe Address: 0x%08x\n", frame_base); #endif // set up the dma transfer. we need to translate to a physical address. + + uint32_t final_address = 0; + + /* Simple case: hardware notification, we can just take the pte since it's based on the type */ + if (notification_type == 0) + { + final_address = frame_base + info_adjust; + } + else + { + // for software we have to calculate the pte index + uint32_t pte_num = ((notification_type << 4) + info_adjust) >> 12; + + /* ramin entries are sorted - 1 object for each pte entry...*/ + final_address = nv3_ramin_read32(notify_obj_base + (0x10 * pte_num) + 8, nv3); + final_address += (info_adjust & 0xFFF); + } + + /* send the notification off */ + nv_log("About to send the notification to 0x%08x (Check target)", final_address); + switch (info_notification_target) + { + case NV3_NOTIFICATION_TARGET_NVM: + svga_writel_linear(final_address, (notify.nanoseconds & 0xFFFFFFFF), nv3); + svga_writel_linear(final_address + 4, (notify.nanoseconds >> 32), nv3); + svga_writel_linear(final_address + 8, notify.info32, nv3); + svga_writel_linear(final_address + 0x0C, (notify.info16 | notify.status), nv3); + break; + case NV3_NOTIFICATION_TARGET_PCI: + case NV3_NOTIFICATION_TARGET_AGP: + dma_bm_write(final_address, (uint8_t*)¬ify, sizeof(nv3_notification_t), 4); + break; + } + + // we're done + nv3->pgraph.notify_pending = false; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index c9430018f..099248f66 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -194,7 +194,10 @@ nv_register_t pramdac_registers[] = { 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_START, "PRAMDAC - VBBlank Start", NULL, NULL}, { NV3_PRAMDAC_VBBLANK_END, "PRAMDAC - VBBlank End", NULL, NULL}, + { NV3_PRAMDAC_HBLANK_END, "PRAMDAC - Horizontal Blanking Interval End", NULL, NULL}, + { NV3_PRAMDAC_HBLANK_START, "PRAMDAC - Horizontal Blanking Interval Start", 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}, @@ -220,7 +223,7 @@ uint32_t nv3_pramdac_read(uint32_t address) // todo: friendly logging - nv_log("PRAMDAC Read from 0x%08x", address); + nv_log("PRAMDAC Read from 0x%08x\n", address); // if the register actually exists if (reg) @@ -301,7 +304,7 @@ 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("PRAMDAC Write 0x%08x -> 0x%08x", value, address); + nv_log("PRAMDAC Write 0x%08x -> 0x%08x\n", value, address); // if the register actually exists if (reg) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index f85ae3f8e..439d44bd5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -434,14 +434,14 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // Perform more validation - if (obj_context_struct.class_id > NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID - || obj_context_struct.class_id < NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID) + if (obj_context_struct.class_id < NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID + || obj_context_struct.class_id > NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID) { fatal("NV3: Invalid graphics object class ID name=0x%04x type=%04x, interpreted by pgraph as: %04x (Contact starfrost)", name, obj_context_struct.class_id, obj_context_struct.class_id & 0x1F); } else if (obj_context_struct.channel > NV3_DMA_CHANNELS) - fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-8", obj_context_struct.channel); + fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-7", obj_context_struct.channel); // Illegal accesses sent to RAMRO, so ignore here // TODO: SEND THESE TO RAMRO!!!!! From 275aef30c23e61e794f017ecbffe3192de48d0cf Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 20 Mar 2025 16:06:03 -0500 Subject: [PATCH 124/274] Fix build on Arch Linux --- src/video/nv/nv3/nv3_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index c56551b14..e7e71961d 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -1060,7 +1060,9 @@ void nv3_close(void* priv) { // Shut down logging log_close(nv3->nvbase.log); +#ifdef ENABLE_NV_LOG nv_log_set_device(NULL); +#endif // Shut down I2C and the DDC ddc_close(nv3->nvbase.ddc); From 57d725742e476d85c3e785ef12810cf5e208eae5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 20 Mar 2025 21:35:56 +0000 Subject: [PATCH 125/274] it's definitely argb + some fixes --- src/include/86box/nv/classes/vid_nv3_classes.h | 15 +++------------ src/include/86box/nv/vid_nv3.h | 3 +-- .../nv3/classes/nv3_class_01c_image_in_memory.c | 4 ++-- .../nv/nv3/classes/nv3_class_shared_methods.c | 6 +++--- src/video/nv/nv3/render/nv3_render_core.c | 3 ++- src/video/nv/nv3/subsystems/nv3_pfb.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 8 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index e1458671e..f3788040e 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -100,22 +100,13 @@ typedef struct nv3_class_ctx_switch_method_s } nv3_class_ctx_switch_method_t; -/* 32-bit BGRA format colour for 2D acceleration */ -typedef struct nv3_color_bgra_32_s -{ - uint8_t b; - uint8_t g; - uint8_t r; - uint8_t a; -} nv3_color_bgra_32_t; - /* 32-bit ARGB format colour for internal D3D5 stuff */ typedef struct nv3_color_argb_32_s { + uint8_t a; uint8_t r; uint8_t g; uint8_t b; - uint8_t a; } nv3_color_argb_32_t; /* 30-bit colour format for internal PGRAPH use */ @@ -375,7 +366,7 @@ typedef struct nv3_object_class_007 /* In case your points weren't colourful enough */ typedef struct nv3_object_class_008_cpoint_s { - nv3_color_argb_32_t color; // BGRA-format 32-bit color + nv3_color_argb_32_t color; // argb-format 32-bit color nv3_position_16_t position; // } nv3_object_class_008_cpoint_t; @@ -1083,7 +1074,7 @@ typedef struct nv3_d3d5_alpha_control_s typedef struct nv3_d3d5_coordinate_s { nv3_d3d5_specular_t specular_reflection_parameters; - nv3_color_bgra_32_t color; // YOU HAVE TO FLIP THE ENDIANNESS. NVIDIA??? WHAT??? + nv3_color_argb_32_t color; // YOU HAVE TO FLIP THE ENDIANNESS. NVIDIA??? WHAT??? // Seems more plausible for these specifically to be floats. // Also makes my life easier... diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 3756a47ed..e5117366a 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -35,10 +35,9 @@ extern const device_config_t nv3_config[]; #define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start #define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN -// DMA channels are basically the number of contexts that the gpu can deal with at once. +// THere are 64 DMA channels grouped into 8 "channels" with 8 "subchannels" each. You can only use one channel at a time. An arbitrary number of 8 objects can be submitted. // Channel 0 is always taken up by NV drivers. -// Subchannels deal with specific parts of the GPU and are manipulated by the driver to manipulate the gpu. #define NV3_DMA_CHANNELS 8 #define NV3_DMA_SUBCHANNELS_PER_CHANNEL 8 diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 547ffc441..30d6aee17 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -69,13 +69,13 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.image_in_memory.pitch = param & 0x1FF0; nv3->pgraph.bpitch[src_buffer_id] = param & 0x1FF0; // 12:0 - nv_log("Image in Memory BUFL%d PITCH=0x%04x", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); + nv_log("Image in Memory BUF%d PITCH=0x%04x", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); break; /* Byte offset in GPU VRAM of top left pixel (22:0) */ case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: nv3->pgraph.boffset[src_buffer_id] = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); - nv_log("Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x", src_buffer_id, nv3->pgraph.image_in_memory.linear_address); + nv_log("Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 2141f238b..2f24daa41 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -84,8 +84,8 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t notify.nanoseconds = nv3->ptimer.time; notify.status = NV3_NOTIFICATION_STATUS_DONE_OK; // it should be fine to just signal that it's ok - // this is completely speculative and i have no idea - notify.info32 = grobj.grobj_0; + // these are only nonzero when there is an error + notify.info32 = notify.info16 = 0; // notify object base=grobj_1 >> 12 uint32_t notify_obj_base = grobj.grobj_1 >> 12; @@ -156,7 +156,7 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t } /* send the notification off */ - nv_log("About to send the notification to 0x%08x (Check target)", final_address); + nv_log("About to send hardware notification to 0x%08x (Check target)\n", final_address); switch (info_notification_target) { case NV3_NOTIFICATION_TARGET_NVM: diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 8b5771f36..57df42b39 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -33,4 +33,5 @@ uint32_t nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, nv3_render_operation_type rop) { return video_rop_gdi_ternary(rop, dst, pattern, src); -} \ 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 index f7d80a41a..160cbff08 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -172,7 +172,7 @@ void nv3_pfb_config0_write(uint32_t val) uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; // i don't think 16:9 is supported - uint32_t new_pfb_vtotal = new_pfb_htotal * (4.0/3.0); + uint32_t new_pfb_vtotal = new_pfb_htotal * (3.0/4.0); uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; nv_log("Framebuffer Config Change\n"); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 073599577..f9e72c71f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -824,7 +824,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; - nv_log("Submitted object [PIO]: Channel %d.%d, Object Name 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", + nv_log("Submitted object [PIO]: Channel %d.%d, Parameter 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", channel, subchannel, object_name, method_offset, nv3->pfifo.cache1_settings.put_address); // Now we're done. Phew! diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 439d44bd5..7a5ff749b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -440,7 +440,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u fatal("NV3: Invalid graphics object class ID name=0x%04x type=%04x, interpreted by pgraph as: %04x (Contact starfrost)", name, obj_context_struct.class_id, obj_context_struct.class_id & 0x1F); } - else if (obj_context_struct.channel > NV3_DMA_CHANNELS) + else if (obj_context_struct.channel > (NV3_DMA_CHANNELS - 1)) fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-7", obj_context_struct.channel); // Illegal accesses sent to RAMRO, so ignore here From 69c4b11f7b46786eff6ec7654d22276ed67c7409 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 21 Mar 2025 00:50:29 +0000 Subject: [PATCH 126/274] Rename name to param to represent that it is the parameter. Add pixel writing code, add rectangle rendering code. Implement ROP, Rectangle and Clip class methods. However, the drivers BSOD. --- doc/nvidia_notes/NV128.xlsx | Bin 9615 -> 9708 bytes .../86box/nv/classes/vid_nv3_classes.h | 112 ++++++++---- src/include/86box/nv/render/vid_nv3_render.h | 5 + src/include/86box/nv/vid_nv3.h | 57 +++--- src/include/86box/utils/video_stdlib.h | 2 +- src/utils/video/video_rop.c | 2 +- src/video/CMakeLists.txt | 1 + .../nv3/classes/nv3_class_001_beta_factor.c | 2 +- src/video/nv/nv3/classes/nv3_class_002_rop.c | 5 +- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 2 +- .../nv3_class_005_clipping_rectangle.c | 12 +- .../nv/nv3/classes/nv3_class_006_pattern.c | 2 +- .../nv/nv3/classes/nv3_class_007_rectangle.c | 33 +++- .../nv/nv3/classes/nv3_class_008_point.c | 2 +- src/video/nv/nv3/classes/nv3_class_009_line.c | 2 +- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- .../nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- .../classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../nv3_class_00e_scaled_image_from_mem.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- .../nv/nv3/classes/nv3_class_011_image.c | 2 +- .../nv/nv3/classes/nv3_class_012_bitmap.c | 2 +- .../classes/nv3_class_014_transfer2memory.c | 2 +- .../nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 2 +- .../classes/nv3_class_018_point_zeta_buffer.c | 2 +- .../classes/nv3_class_01c_image_in_memory.c | 1 + .../nv/nv3/classes/nv3_class_shared_methods.c | 1 + src/video/nv/nv3/render/nv3_render_core.c | 171 ++++++++++++++++++ .../nv/nv3/render/nv3_render_primitives.c | 46 +++++ 32 files changed, 395 insertions(+), 89 deletions(-) create mode 100644 src/video/nv/nv3/render/nv3_render_primitives.c diff --git a/doc/nvidia_notes/NV128.xlsx b/doc/nvidia_notes/NV128.xlsx index 919297d56816e5278deb74887f1c321198297f08..fbf57c5eb136926abfc26b411f6aec0ae899a3fc 100644 GIT binary patch delta 2944 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 delta 2864 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 #include <86box/nv/vid_nv3.h> -void nv3_class_001_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index b771551d3..9286c180c 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -28,10 +28,13 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_002_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + case NV3_ROP_SET_ROP: + nv3->pgraph.rop = param & 0xFF; + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 73901d08d..06750521e 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_003_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 48a96ab96..10788b5ce 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_004_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 1119147e7..8ba698aa5 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -27,10 +27,20 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_005_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + case NV3_CLIP_POSITION: + nv3->pgraph.clip_start.x = (param >> 16) & 0xFFFF; + nv3->pgraph.clip_start.y = (param) & 0xFFFF; + nv_log("Clip Position: %d,%d", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + break; + case NV3_CLIP_SIZE: + nv3->pgraph.clip_size.x = (param >> 16) & 0xFFFF; + nv3->pgraph.clip_size.y = (param) & 0xFFFF; + nv_log("Clip Size: %d,%d", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index c8c81066f..ee3348669 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_006_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 69e66ca31..dd82030ca 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -28,12 +28,41 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_007_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + case NV3_RECTANGLE_COLOR: + nv3->pgraph.rectangle.color = param; + break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + /* Check for any rectangle point or size method. */ + if (method_id >= NV3_RECTANGLE_START && method_id <= NV3_RECTNAGLE_END) + { + uint32_t index = (method_id - NV3_RECTANGLE_START) / 8; + + // If the size is submitted, render it. + if (method_id & 0x04) + { + nv3->pgraph.rectangle.size[index].w = (param >> 16) & 0xFFFF; + nv3->pgraph.rectangle.size[index].h = param & 0xFFFF; + + nv_log("Rect%d Size=%d,%d\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h); + + nv3_render_rect(nv3->pgraph.rectangle.position[index], nv3->pgraph.rectangle.size[index], nv3->pgraph.rectangle.color, grobj); + } + else // position + { + nv3->pgraph.rectangle.position[index].x = (param >> 16) & 0xFFFF; + nv3->pgraph.rectangle.position[index].y = param & 0xFFFF; + + nv_log("Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); + } + + return; + } + + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 0cd44b8cd..8647b27cb 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_008_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index 8d71f4d5e..dddbf5719 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_009_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 141a2422c..45f1c97b9 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_00a_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 91cbaa837..e3bbf9f07 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_00b_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 3ba352b3c..66eddee71 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_00c_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 888ac89cf..687e1e763 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_00d_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index bf0286c20..f4bc8d6ee 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_00e_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index da2f16bda..c502bdd28 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_010_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 4499693df..36679cb7f 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_011_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index ca8540b75..2ce3fb685 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_012_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index 6a18ff1d7..86be6a74a 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_014_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index c8560ad0f..5b499b73d 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_015_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 951ba862d..4f1e73774 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -28,7 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_class_017_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 80f10797f..4829fafb9 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -30,7 +30,7 @@ struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; -void nv3_class_018_method(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) +void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 30d6aee17..d55ee9511 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -60,6 +60,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Set the format */ nv3->pgraph.bpixel[src_buffer_id] = ((real_format & 0x03) | NV3_BPIXEL_FORMAT_IS_VALID); + nv_log("Image in Memory BUF%d COLOR_FORMAT=0x%04x", src_buffer_id, param); break; diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 2f24daa41..38d45f70e 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -157,6 +157,7 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t /* send the notification off */ nv_log("About to send hardware notification to 0x%08x (Check target)\n", final_address); + switch (info_notification_target) { case NV3_NOTIFICATION_TARGET_NVM: diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 57df42b39..4dff864e0 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -35,3 +35,174 @@ uint32_t nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, nv3_rende return video_rop_gdi_ternary(rop, dst, pattern, src); } +/* Expand a colour. + NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! +*/ +nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) +{ + // grobj0 = seems to share the format of PGRAPH_CONTEXT_SWITCH register. + + uint8_t format = (grobj.grobj_0 & 0x07); + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + + nv3_color_expanded_t color_final; + // set the pixel format + color_final.pixel_format = format; + + #ifdef ENABLE_NV_LOG + nv_log("Expanding Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d", color, format, alpha_enabled); + #endif + + // default to fully opaque in case alpha is disabled + color_final.a = 0xFF; + + switch (format) + { + // ALL OF THESE TYPES ARE 32 BITS IN SIZE + + // 555 + case nv3_pgraph_pixel_format_r5g5b5: + nv3_color_16_a1r5g5b5_t new_color = *(nv3_color_16_a1r5g5b5_t*)&color; + // "stretch out" the colour + color_final.r = new_color.r * 0x20; + color_final.g = new_color.g * 0x20; + color_final.b = new_color.b * 0x20; + + if (alpha_enabled) + color_final.a = new_color.a; + + break; + // 888 (standard colour + 8-bit alpha) + case nv3_pgraph_pixel_format_r8g8b8: + if (alpha_enabled) + color_final.a = ((color >> 24) & 0xFF) * 4; + + color_final.r = ((color >> 16) & 0xFF) * 4; + color_final.g = ((color >> 8) & 0xFF) * 4; + color_final.b = (color & 0xFF) * 4; + + if (alpha_enabled) + color_final.a = new_color.a; + break; + case nv3_pgraph_pixel_format_r10g10b10: + nv3_color_x2a10g10b10_t new_color_rgb10 = *(nv3_color_x2a10g10b10_t*)&color; + + color_final.r = new_color_rgb10.r; + color_final.g = new_color_rgb10.g; + color_final.b = new_color_rgb10.b; + + if (alpha_enabled) + color_final.a = new_color.a; + + break; + case nv3_pgraph_pixel_format_y8: + color_final.a = (color >> 8) & 0xFF; + + // yuv + color_final.r = color_final.g = color_final.b = (color & 0xFF) * 4; // convert to rgb10 + break; + case nv3_pgraph_pixel_format_y16: + color_final.a = (color >> 16) & 0xFFFF; + + // yuv + color_final.r = color_final.g = color_final.b = (color & 0xFFFF) * 4; // convert to rgb10 + break; + default: + + } + + // i8 is a union under i16 + color_final.i16 = (color & 0xFFFF); + + return color_final; +} + +/* Convert expanded colour format to chroma key format */ +uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) +{ + // convert the alpha to 1 bit. then return packed rgb10 + return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.a << 10); +} + +/* Plots a pixel. */ +void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) +{ + uint8_t alpha = 0xFF; + + // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. + // It seems, you are meant to + + /* put this here for debugging + it may be needed later. */ + #ifdef DEBUG + uint8_t color_format_object = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_COLOR_FORMAT) & 0x07; + #endif + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too? + uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + + /* doesn't seem*/ + nv3_color_argb_t color_data = *(nv3_color_argb_t*)&color; + + if (framebuffer_bpp == 32) + alpha = color_data.a; + + int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; + int32_t clip_end_y = nv3->pgraph.clip_start.y + nv3->pgraph.clip_size.y; + + /* First, check our current buffer. */ + /* Then do the clip. */ + if (position.x < nv3->pgraph.clip_start.x + || position.y < nv3->pgraph.clip_start.y + || position.x > clip_end_x + || position.y > clip_end_y) + { + // DO NOT DRAW THE PIXEL + return; + } + + /* TODO: Chroma Key, Pattern, Plane Mask...*/ + + /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from. */ + uint32_t pixel_addr_vram = position.x + (nv3->pgraph.bpitch[current_buffer]) * position.y + nv3->pgraph.boffset[current_buffer]; + + pixel_addr_vram &= nv3->nvbase.svga.vram_mask; + + /* Go to vram and do the final ROP for a basic bitblit. + It seems we can skip the downconversion step *for now*, since (framebuffer bits per pixel) == (object bits per pixel) + I'm not sure how games will react. But it depends on how the D3D drivers operate, we may need ro convert texture formats to the current bpp internally. + + TODO: MOVE TO BPIXEL DEPTH or GROBJ0 to determine this, once we figure out how to get the bpixel depth. + */ + + uint32_t src = 0, dst = 0; + + switch (framebuffer_bpp) + { + case 8: + src = color & 0xFF; + dst = nv3->nvbase.svga.vram[pixel_addr_vram]; + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + break; + case 16: + uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); + pixel_addr_vram >>= 1; + + src = color & 0xFFFF; + dst = vram_16[pixel_addr_vram]; + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + break; + case 32: + uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); + pixel_addr_vram >>= 2; + + src = color; + dst = vram_32[pixel_addr_vram]; + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + break; + } + + nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; + +} \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c new file mode 100644 index 000000000..e44636acb --- /dev/null +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -0,0 +1,46 @@ +/* +* 86Box A hypervisor and IBM PC system emulator that specializes in +* running old operating systems and software designed for IBM +* PC systems and compatibles from 1981 through fairly recent +* system designs based on the PCI bus. +* +* This file is part of the 86Box distribution. +* +* NV3 code to render basic objects. +* +* +* +* Authors: Connor Hyde, I need a better email address ;^) +* +* Copyright 2024-2025 Connor Hyde +*/ + +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> +#include <86box/utils/video_stdlib.h> + +void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj) +{ + nv3_position_16_t current_pos = {0}; + + for (int32_t y = position.y; y < (position.y + size.h); y++) + { + current_pos.y = y; + for (int32_t x = position.x; x < (position.x + size.w); x++) + { + current_pos.x = x; + + nv3_render_pixel(current_pos, color, grobj); + } + } +} \ No newline at end of file From b5321903de5b734de7bc44cc6ccae59c27d1847b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 00:57:31 +0000 Subject: [PATCH 127/274] rename intr_1_invalid_method to indicate that it seems to also be used for software ethods --- src/include/86box/nv/classes/vid_nv3_classes.h | 2 ++ src/include/86box/nv/vid_nv3.h | 6 ++++-- src/video/nv/nv3/classes/nv3_class_001_beta_factor.c | 2 +- src/video/nv/nv3/classes/nv3_class_002_rop.c | 2 +- src/video/nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- src/video/nv/nv3/classes/nv3_class_004_plane_mask.c | 2 +- src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_006_pattern.c | 2 +- src/video/nv/nv3/classes/nv3_class_007_rectangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_008_point.c | 2 +- src/video/nv/nv3/classes/nv3_class_009_line.c | 2 +- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- src/video/nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- src/video/nv/nv3/classes/nv3_class_011_image.c | 2 +- src/video/nv/nv3/classes/nv3_class_012_bitmap.c | 2 +- src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c | 2 +- .../nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c | 2 +- src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c | 2 +- src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c | 6 +++++- src/video/nv/nv3/classes/nv3_class_shared_methods.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 -- 26 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index b4f41fc30..c5c014245 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -79,6 +79,8 @@ typedef enum nv3_pgraph_class_e #define NV3_ROP_SET_ROP 0x0300 // Set GDI standard rop +#define NV3_BETA_FACTOR 0x0300 + #define NV3_CLIP_POSITION 0x0300 // S16:S16, 0=topleft #define NV3_CLIP_SIZE 0x0304 // U16:U16 diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index c1d0e3bdd..d7d7dc144 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -210,6 +210,8 @@ extern const device_config_t nv3_config[]; #define NV3_PBUS_END 0x1FFF #define NV3_PFIFO_START 0x2000 // FIFO for DMA Object Submission (uses hashtable to store the objects) +#define NV3_PFIFO_MINIMUM_GUARANTEED_DEPTH 0x7C + #define NV3_PFIFO_DELAY_0 0x2040 // PFIFO Config Register #define NV3_PFIFO_DEBUG_0 0x2080 // PFIFO Debug Register #define NV3_PFIFO_CACHE0_ERROR_PENDING 0 @@ -286,7 +288,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE0_METHOD_END 0x3200 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 -#define NV3_PFIFO_CACHE1_PUSH0 0x3200 +#define NV3_PFIFO_CACHE1_PUSH0 0x3200 #define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 @@ -527,7 +529,7 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY 28 // Fired on software notification #define NV3_PGRAPH_INTR_1 0x400104 -#define NV3_PGRAPH_INTR_1_INVALID_METHOD 0 // Invalid method +#define NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING 0 // Software or invalid method #define NV3_PGRAPH_INTR_1_INVALID_DATA 4 // Invalid data. Not sure when this would be triggered. #define NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY 12 // Tried to notify while a notify was pending. #define NV3_PGRAPH_INTR_1_CTXSW_NOTIFY 16 // Notify fired for software context diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 2acf3a8f4..784da2eb0 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -34,7 +34,7 @@ void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index 9286c180c..38fe29d78 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -37,7 +37,7 @@ void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 06750521e..92a3cb225 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -34,7 +34,7 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 10788b5ce..9c9bced95 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -34,7 +34,7 @@ void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 8ba698aa5..10136feac 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -43,7 +43,7 @@ void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index ee3348669..2c2174447 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -34,7 +34,7 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index dd82030ca..f0e85f4db 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -63,7 +63,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 8647b27cb..4ffffba92 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -34,7 +34,7 @@ void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index dddbf5719..edb432884 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -34,7 +34,7 @@ void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 45f1c97b9..fc7dcc879 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -35,7 +35,7 @@ void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index e3bbf9f07..08a205ac2 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -34,7 +34,7 @@ void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 66eddee71..2b14ec2ff 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -34,7 +34,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 687e1e763..fa1f08811 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -34,7 +34,7 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index f4bc8d6ee..c4b68c34f 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -34,7 +34,7 @@ void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index c502bdd28..3ff52f68a 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -34,7 +34,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 36679cb7f..b40b9025c 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -34,7 +34,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index 2ce3fb685..bbbb6997d 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -35,7 +35,7 @@ void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index 86be6a74a..47e4b0c32 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -35,7 +35,7 @@ void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index 5b499b73d..f1c571943 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -34,7 +34,7 @@ void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 4f1e73774..838c64aa7 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -34,7 +34,7 @@ void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 4829fafb9..904cac757 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -36,7 +36,7 @@ void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index d55ee9511..9ba86c0eb 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -64,6 +64,10 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Image in Memory BUF%d COLOR_FORMAT=0x%04x", src_buffer_id, param); break; + /* DOn't log invalid */ + case NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE: + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + break; /* Pitch - length between scanlines */ case NV3_IMAGE_IN_MEMORY_PITCH: @@ -80,7 +84,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 38d45f70e..7338c45ad 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -56,7 +56,7 @@ void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t break; default: nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_METHOD); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 93fe8c32f..eddf61dea 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -602,8 +602,6 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe } } - - nv3_notify_if_needed(param, method, context, grobj); } From b170c51307f29d4074a884c73c344d94ac7ffd7f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 02:05:36 +0000 Subject: [PATCH 128/274] fix the bsod (I hate bitfields). --- src/include/86box/nv/vid_nv3.h | 14 +++---- .../nv/nv3/classes/nv3_class_shared_methods.c | 9 +++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 41 ++++++++++++++++--- src/video/nv/nv3/subsystems/nv3_pgraph.c | 5 --- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 1 - 5 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index d7d7dc144..16fcee485 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -266,7 +266,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 -#define NV3_PFIFO_CACHE0_PUSH0 0x3000 +#define NV3_PFIFO_CACHE0_PUSH0 0x3000 #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 @@ -309,11 +309,11 @@ extern const device_config_t nv3_config[]; #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 #define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULL0 0x3240 +#define NV3_PFIFO_CACHE1_PULL0 0x3240 //todo: merge stuff -#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware +#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 +#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 +#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE 0x3250 #define NV3_PFIFO_CACHE1_PULLER_CTX_STATE_DIRTY 4 #define NV3_PFIFO_CACHE1_GET 0x3270 @@ -997,8 +997,8 @@ typedef struct nv3_pfifo_cache_s typedef struct nv3_pfifo_cache_entry_s { - uint8_t subchannel : 3; - uint16_t method : 11; // method id depending on class (offset from entry channel start in ramin) + uint16_t method; // method id depending on class (offset from entry channel start in ramin) + uint8_t subchannel; uint32_t data; // is this the context } nv3_pfifo_cache_entry_t; diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 7338c45ad..0bb12a2eb 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -33,8 +33,13 @@ void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t { switch (method_id) { - // set up the current notification request/object] - // check for double notifiers. + /* mthdCreate in software(?)*/ + case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: + nv_log("mthdCreate\n"); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + break; + // set up the current notification request/object + // and check for double notifiers. case NV3_SET_NOTIFY: if (nv3->pgraph.notify_pending) { diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index f9e72c71f..81b13e5bf 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -295,28 +295,57 @@ uint32_t nv3_pfifo_read(uint32_t address) { nv_log("PFIFO Cache0 Read\n"); - if (address & 4) + // See if we want the object name or the channel/subchannel information. + if (address & 4) + { + nv_log("Data=0x%08x\n", nv3->pfifo.cache0_entry.data); return nv3->pfifo.cache0_entry.data; + } else + { + uint32_t method = nv3->pfifo.cache0_entry.method; + uint32_t subchannel = nv3->pfifo.cache0_entry.subchannel; + uint32_t final = nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + + nv_log("Method=0x%08x, ", nv3->pfifo.cache0_entry.method); + nv_log("Subchannel=0x%08x\n", nv3->pfifo.cache0_entry.subchannel); + nv_log("Final=0x%08x\n", final); + return nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + } } else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) { // Not sure if REV C changes this. It should... uint32_t slot = 0; + // shift right by 3, convert from address, to slot. if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) slot = (address >> 3) & 0x3F; else slot = (address >> 3) & 0x1F; - nv_log("PFIFO Cache1 Read slot=%d\n", slot); + nv_log("PFIFO Cache1 Read slot=%d", slot); // See if we want the object name or the channel/subchannel information. if (address & 4) + { + nv_log("Data=0x%08x\n", nv3->pfifo.cache1_entries[slot].data); return nv3->pfifo.cache1_entries[slot].data; + } else + { + uint32_t method = nv3->pfifo.cache1_entries[slot].method; + uint32_t subchannel = nv3->pfifo.cache1_entries[slot].subchannel; + uint32_t final = nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + + nv_log("Method=0x%08x, ", nv3->pfifo.cache1_entries[slot].method); + nv_log("Subchannel=0x%08x\n", nv3->pfifo.cache1_entries[slot].subchannel); + nv_log("Final=0x%08x\n", final); + return nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + } + } else { @@ -556,7 +585,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) } else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { - nv_log("PFIFO Cache0 Write"); + nv_log("PFIFO Cache0 Write\n"); // 3104 always written after 3100 if (address & 4) @@ -736,7 +765,7 @@ void nv3_pfifo_context_switch(uint32_t new_channel) // NV_USER writes go here! // Pushes graphics objects into cache1 -void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) +void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) { bool oh_shit = false; // RAMRO needed nv3_ramin_ramro_reason oh_shit_reason = 0x00; // It's all good for now @@ -811,7 +840,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) uint32_t current_put_address = nv3->pfifo.cache1_settings.put_address >> 2; nv3->pfifo.cache1_entries[current_put_address].subchannel = subchannel; nv3->pfifo.cache1_entries[current_put_address].method = method_offset; - nv3->pfifo.cache1_entries[current_put_address].data = object_name; + nv3->pfifo.cache1_entries[current_put_address].data = param; // now we have to recalculate the cache1 put address uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address); @@ -825,7 +854,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t object_name) nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; nv_log("Submitted object [PIO]: Channel %d.%d, Parameter 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", - channel, subchannel, object_name, method_offset, nv3->pfifo.cache1_settings.put_address); + channel, subchannel, param, method_offset, nv3->pfifo.cache1_settings.put_address); // Now we're done. Phew! } diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index eddf61dea..993232c9d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -613,11 +613,6 @@ void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t switch (method) { - // This method is how we figure out which methods exist. - case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("I'm an Nvidia Object! channel=%d.%d class=0x%02x (%s) method=0x%04x parameter=0x%08x, context=0x%08x\n", - channel, subchannel, class_id, nv3_class_names[class_id], method, param, context); - break; default: // Object Method orchestration nv3_pgraph_arbitrate_method(param, method, channel, subchannel, class_id, context); diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index e46b46618..cce1d74a9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -32,7 +32,6 @@ It is used to get the offset within RAMHT of a graphics object. */ - uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) { // the official nvidia hash algorithm, tweaked for readability From fea06b6d9ae9605c38e15fd9d42bb54a16d496be Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 03:05:09 +0000 Subject: [PATCH 129/274] Don't flip x/y coordinates and w/h of rectangles. --- src/include/86box/nv/vid_nv3.h | 1 + src/video/nv/nv3/classes/nv3_class_007_rectangle.c | 10 ++++++---- src/video/nv/nv3/nv3_core.c | 8 +++++--- src/video/nv/nv3/render/nv3_render_core.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 1 - 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 16fcee485..e966f79b6 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1442,6 +1442,7 @@ extern nv3_t* nv3; void* nv3_init(const device_t *info); void nv3_close(void* priv); void nv3_speed_changed(void *priv); +void nv3_recalc_timings(svga_t* svga); void nv3_force_redraw(void* priv); // Memory Mapping diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index f0e85f4db..856c1c6cd 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -44,8 +44,9 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // If the size is submitted, render it. if (method_id & 0x04) { - nv3->pgraph.rectangle.size[index].w = (param >> 16) & 0xFFFF; - nv3->pgraph.rectangle.size[index].h = param & 0xFFFF; + nv3->pgraph.rectangle.size[index].w = param & 0xFFFF; + nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; + nv_log("Rect%d Size=%d,%d\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h); @@ -53,8 +54,9 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } else // position { - nv3->pgraph.rectangle.position[index].x = (param >> 16) & 0xFFFF; - nv3->pgraph.rectangle.position[index].y = param & 0xFFFF; + nv3->pgraph.rectangle.position[index].x = param & 0xFFFF; + nv3->pgraph.rectangle.position[index].y = (param >> 16) & 0xFFFF; + nv_log("Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); } diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e7e71961d..3f9fb0274 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -488,22 +488,24 @@ void nv3_recalc_timings(svga_t* svga) // 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->map8 = svga->pallook; svga->render = svga_render_8bpp_highres; break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: @@ -703,14 +705,14 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) Additionally only do it if the value actually changed*/ if (old_value != val) { - // Thx to Fuel who basically wrote all the SVGA compatibility code already (although I fixed some issues), because VGA is boring + // Thx to Fuel who basically wrote most of the SVGA compatibility code already (although I fixed some issues), because VGA is boring // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" if (nv3->nvbase.svga.crtcreg < 0xE && nv3->nvbase.svga.crtcreg > 0x10) { nv3->nvbase.svga.fullchange = changeframecount; - svga_recalctimings(&nv3->nvbase.svga); + nv3_recalc_timings(&nv3->nvbase.svga); } } diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 4dff864e0..aebfb6466 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -172,7 +172,7 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr TODO: MOVE TO BPIXEL DEPTH or GROBJ0 to determine this, once we figure out how to get the bpixel depth. */ - + uint32_t src = 0, dst = 0; switch (framebuffer_bpp) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 099248f66..b887b83f9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -384,5 +384,4 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) { nv_log(": Unknown register write (address=0x%08x)\n", address); } - } \ No newline at end of file From ceb4ff7a7301bfe67daf86e69901da64c5f218b9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 12:02:49 +0000 Subject: [PATCH 130/274] Produce recognisable graphical output and, evne if they don't do anything yet, implement the beta factor/chroma key methods. --- .../86box/nv/classes/vid_nv3_classes.h | 2 + src/include/86box/nv/render/vid_nv3_render.h | 5 +- .../nv3/classes/nv3_class_001_beta_factor.c | 8 ++ .../nv/nv3/classes/nv3_class_003_chroma_key.c | 6 ++ src/video/nv/nv3/render/nv3_render_core.c | 75 ++++++++++++++++--- 5 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index c5c014245..96df80eb1 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -81,6 +81,8 @@ typedef enum nv3_pgraph_class_e #define NV3_BETA_FACTOR 0x0300 +#define NV3_CHROMA_KEY 0x0304 + #define NV3_CLIP_POSITION 0x0300 // S16:S16, 0=topleft #define NV3_CLIP_SIZE 0x0304 // U16:U16 diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 58ecf9df5..50e41c17e 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -19,6 +19,9 @@ /* Core */ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); +uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); +nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color); /* Primitives */ -void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); \ No newline at end of file +void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); +void nv3_render_chroma_test(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 784da2eb0..f56a328dd 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -32,6 +32,14 @@ void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + /* even if we don't do anything with this yet... */ + case NV3_BETA_FACTOR: + if (param & 0x80000000) /* bit0 */ + nv3->pgraph.beta_factor = 0; + else + nv3->pgraph.beta_factor = param & 0x7F800000; + + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 92a3cb225..a1364bd98 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -32,6 +32,12 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + case NV3_CHROMA_KEY: + nv3_color_expanded_t expanded_color = nv3_render_expand_color(grobj, param); + + uint32_t color = nv3_render_to_chroma(expanded_color); + nv3->pgraph.chroma_key = *(nv3_color_x2a10g10b10_t*)&color; + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index aebfb6466..8d7bdd189 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -29,12 +29,6 @@ #include <86box/nv/vid_nv3.h> #include <86box/utils/video_stdlib.h> -/* Render Core: Performs a ROP */ -uint32_t nv3_perform_rop(uint32_t src, uint32_t dst, uint32_t pattern, nv3_render_operation_type rop) -{ - return video_rop_gdi_ternary(rop, dst, pattern, src); -} - /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! */ @@ -108,6 +102,8 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) color_final.r = color_final.g = color_final.b = (color & 0xFFFF) * 4; // convert to rgb10 break; default: + warning("nv3_render_expand_color unknown format %d", format); + break; } @@ -117,6 +113,39 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) return color_final; } +/* Used for chroma test */ +uint32_t nv3_render_downconvert(nv3_grobj_t grobj, nv3_color_expanded_t color) +{ + uint8_t format = (grobj.grobj_0 & 0x07); + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + + #ifdef ENABLE_NV_LOG + nv_log("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d", color, format, alpha_enabled); + #endif + + uint32_t packed_color = 0x00; + + switch (format) + { + case nv3_pgraph_pixel_format_r5g5b5: + break; + case nv3_pgraph_pixel_format_r8g8b8: + break; + case nv3_pgraph_pixel_format_r10g10b10: + break; + case nv3_pgraph_pixel_format_y8: + break; + case nv3_pgraph_pixel_format_y16: + break; + default: + warning("nv3_render_downconvert_color unknown format %d", format); + break; + + } + + return packed_color; +} + /* Convert expanded colour format to chroma key format */ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) { @@ -162,7 +191,23 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr /* TODO: Chroma Key, Pattern, Plane Mask...*/ /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from. */ - uint32_t pixel_addr_vram = position.x + (nv3->pgraph.bpitch[current_buffer]) * position.y + nv3->pgraph.boffset[current_buffer]; + + uint32_t vram_x = position.x; + + // we have to multiply the x position by the number of bytes per pixel + switch (framebuffer_bpp) + { + case 8: + break; + case 16: + vram_x = position.x * 2; + break; + case 32: + vram_x = position.x * 4; + break; + } + + uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * position.y) + nv3->pgraph.boffset[current_buffer]; pixel_addr_vram &= nv3->nvbase.svga.vram_mask; @@ -180,7 +225,9 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr case 8: src = color & 0xFF; dst = nv3->nvbase.svga.vram[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + nv3->nvbase.svga.vram[pixel_addr_vram] = (int8_t)video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; break; case 16: @@ -189,7 +236,9 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr src = color & 0xFFFF; dst = vram_16[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + vram_16[pixel_addr_vram] = (int16_t)video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; break; case 32: @@ -198,11 +247,13 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr src = color; dst = vram_32[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + vram_32[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + + nv3->nvbase.svga.changedvram[pixel_addr_vram >> 10] = changeframecount; break; } - nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; -} \ No newline at end of file +} + From 97a06fa007608aa55881f26d5eb623ba34a9c1a3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 13:25:33 +0000 Subject: [PATCH 131/274] Implement RAMRO, and also fix runout_put and runout_get so that it can actually runout without freezing (I don't think it should be even if it's only running out on a few objects, but it should be able to runout anyway.) --- .../nv/nv3/classes/nv3_class_007_rectangle.c | 2 +- src/video/nv/nv3/render/nv3_render_core.c | 14 ++++++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 35 ++++++++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 856c1c6cd..a92a1f761 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -48,7 +48,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; - nv_log("Rect%d Size=%d,%d\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h); + nv_log("Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h, nv3->pgraph.rectangle.color); nv3_render_rect(nv3->pgraph.rectangle.position[index], nv3->pgraph.rectangle.size[index], nv3->pgraph.rectangle.color, grobj); } diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 8d7bdd189..7b9b3e303 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -128,6 +128,10 @@ uint32_t nv3_render_downconvert(nv3_grobj_t grobj, nv3_color_expanded_t color) switch (format) { case nv3_pgraph_pixel_format_r5g5b5: + packed_color = (color.r / 0x20) << 10 | + (color.g / 0x20) << 5 | + (color.b / 0x20); + break; case nv3_pgraph_pixel_format_r8g8b8: break; @@ -146,6 +150,12 @@ uint32_t nv3_render_downconvert(nv3_grobj_t grobj, nv3_color_expanded_t color) return packed_color; } +/* Runs the chroma key test */ +void nv3_render_chroma_test(nv3_grobj_t grobj) +{ + +} + /* Convert expanded colour format to chroma key format */ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) { @@ -200,10 +210,10 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr case 8: break; case 16: - vram_x = position.x * 2; + vram_x = position.x << 1; break; case 32: - vram_x = position.x * 4; + vram_x = position.x << 2; break; } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 81b13e5bf..bac84ede9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -556,20 +556,22 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache1_settings.get_address = val; break; case NV3_PFIFO_RUNOUT_GET: + uint32_t size_get = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_get == 0) //512b - nv3->pfifo.runout_get = ((val & 0x3F) << 3); + nv3->pfifo.runout_get = val & (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); else - nv3->pfifo.runout_get = ((val & 0x3FF) << 3); + nv3->pfifo.runout_get = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); break; case NV3_PFIFO_RUNOUT_PUT: uint32_t size_put = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_put == 0) //512b - nv3->pfifo.runout_put = ((val & 0x3F) << 3); + nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); else - nv3->pfifo.runout_put = ((val & 0x3FF) << 3); + nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); + break; /* Cache1 is handled below */ case NV3_PFIFO_CACHE0_CTX: @@ -833,6 +835,31 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) if (oh_shit) { nv_log("WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); + + nv3_ramro_write(nv3->pfifo.runout_put, new_address); + nv3_ramro_write(nv3->pfifo.runout_put + 4, param); + + nv3->pfifo.runout_put += 0x08; + + uint32_t ramro_size = (nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01; + + /* Make sure it's valid */ + switch (ramro_size) + { + case 0: + nv3->pfifo.runout_put &= (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); + break; + case 1: + nv3->pfifo.runout_put &= (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); + break; + } + + /* Fire the interrupt. Also the very bad interrupt...*/ + if (nv3->pfifo.runout_get == nv3->pfifo.runout_put) + nv3_pfifo_interrupt(NV3_PFIFO_INTR_RUNOUT_OVERFLOW, true); + else + nv3_pfifo_interrupt(NV3_PFIFO_INTR_RUNOUT, true); + return; } From 4e4ed738b202a194d6e92c4fe420357b00ba2a07 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 13:50:56 +0000 Subject: [PATCH 132/274] Fix fifo free spaces check, which was so assbackwards I have no idea how anything worked up to now. It was causing spurious runouts --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 32 ++++++++++--------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index bac84ede9..d8b7cef96 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -303,15 +303,11 @@ uint32_t nv3_pfifo_read(uint32_t address) } else { - uint32_t method = nv3->pfifo.cache0_entry.method; - uint32_t subchannel = nv3->pfifo.cache0_entry.subchannel; uint32_t final = nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - nv_log("Method=0x%08x, ", nv3->pfifo.cache0_entry.method); - nv_log("Subchannel=0x%08x\n", nv3->pfifo.cache0_entry.subchannel); - nv_log("Final=0x%08x\n", final); + nv_log("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); - return nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + return final; } } else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) @@ -335,15 +331,9 @@ uint32_t nv3_pfifo_read(uint32_t address) } else { - uint32_t method = nv3->pfifo.cache1_entries[slot].method; - uint32_t subchannel = nv3->pfifo.cache1_entries[slot].subchannel; uint32_t final = nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - - nv_log("Method=0x%08x, ", nv3->pfifo.cache1_entries[slot].method); - nv_log("Subchannel=0x%08x\n", nv3->pfifo.cache1_entries[slot].subchannel); - nv_log("Final=0x%08x\n", final); - - return nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); + nv_log("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); + return final; } } @@ -835,7 +825,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) if (oh_shit) { nv_log("WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); - + nv3_ramro_write(nv3->pfifo.runout_put, new_address); nv3_ramro_write(nv3->pfifo.runout_put + 4, param); @@ -854,7 +844,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) break; } - /* Fire the interrupt. Also the very bad interrupt...*/ + //Fire the interrupt. Also the very bad interrupt... if (nv3->pfifo.runout_get == nv3->pfifo.runout_put) nv3_pfifo_interrupt(NV3_PFIFO_INTR_RUNOUT_OVERFLOW, true); else @@ -953,9 +943,13 @@ void nv3_pfifo_cache1_pull() // THIS IS PER SUBCHANNEL! uint32_t nv3_pfifo_cache1_num_free_spaces() { - // convert to gray code - uint32_t real_get_address = nv3_pfifo_cache1_normal2gray(nv3->pfifo.cache1_settings.get_address); - uint32_t real_put_address = nv3_pfifo_cache1_normal2gray(nv3->pfifo.cache1_settings.put_address); + // get the index + + uint32_t get_index = nv3->pfifo.cache1_settings.get_address >> 2; + uint32_t put_index = nv3->pfifo.cache1_settings.put_address >> 2; + + uint32_t real_get_address = nv3_pfifo_cache1_gray2normal(get_index) << 2; + uint32_t real_put_address = nv3_pfifo_cache1_gray2normal(put_index) << 2; // There is no hope of being able to understand it. Nobody can understand return (real_get_address - real_put_address - 4) & 0x7C; // there are 64 entries what From 8d4096fffc34e971f249b38011cd4ef6b14ce6c5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 22 Mar 2025 15:37:14 +0000 Subject: [PATCH 133/274] Fix the colours. Wtf is wrong with NVidia? --- src/include/86box/nv/render/vid_nv3_render.h | 1 + src/include/86box/nv/vid_nv3.h | 1 + src/video/nv/nv3/nv3_core.c | 22 ++++++++++++++----- src/video/nv/nv3/render/nv3_render_core.c | 21 +++++++++++++----- src/video/nv/nv3/subsystems/nv3_pramdac.c | 1 + .../nv/nv3/subsystems/nv3_pramin_ramro.c | 4 ++-- 6 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 50e41c17e..8481213ac 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -24,4 +24,5 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color); /* Primitives */ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); + void nv3_render_chroma_test(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index e966f79b6..640a0c951 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -699,6 +699,7 @@ extern const device_config_t nv3_config[]; #define NV3_PRAMDAC_COEFF_SELECT 0x68050C #define NV3_PRAMDAC_GENERAL_CONTROL 0x680600 +#define NV3_PRAMDAC_GENERAL_CONTROL_565_MODE 12 // These are all 10-bit values, but aligned to 32bits // so treating them as 32bit should be fine diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 3f9fb0274..b1d96f8d9 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -481,8 +481,10 @@ 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; + + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; // should these actually use separate values? // i don't we should force the top 2 bits to 1... @@ -495,7 +497,6 @@ void nv3_recalc_timings(svga_t* svga) 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 @@ -509,9 +510,20 @@ void nv3_recalc_timings(svga_t* svga) 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; + /* sometimes it really renders in 15bpp, so you need to do this */ + if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) + { + svga->bpp = 16; + svga->lowres = 0; + svga->render = svga_render_16bpp_highres; + } + else + { + svga->bpp = 16; // HACK: DO NOT change this + svga->lowres = 0; + svga->render = svga_render_15bpp_highres; + } + break; case NV3_CRTC_REGISTER_PIXELMODE_32BPP: svga->bpp = 32; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 7b9b3e303..8ccca2e6f 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -23,6 +23,7 @@ #include <86box/device.h> #include <86box/mem.h> #include <86box/pci.h> +#include <86box/plat.h> #include <86box/rom.h> #include <86box/video.h> #include <86box/nv/vid_nv.h> @@ -175,6 +176,8 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr #ifdef DEBUG uint8_t color_format_object = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_COLOR_FORMAT) & 0x07; #endif + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too? uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; @@ -235,7 +238,7 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr case 8: src = color & 0xFF; dst = nv3->nvbase.svga.vram[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = (int8_t)video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00) & 0xFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; @@ -244,9 +247,18 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 1; - src = color & 0xFFFF; + // mask off the alpha bit. Even though the drivers should send this. What! + if (!alpha_enabled) + src = ((color & (~0x8000)) & 0xFFFF); + else + src = color & 0xFFFF; + + // convert to 16bpp + // forcing it to render in 15bpp fixes it, + + dst = vram_16[pixel_addr_vram]; - vram_16[pixel_addr_vram] = (int16_t)video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + vram_16[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00) & 0xFFFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; @@ -263,7 +275,4 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr break; } - - } - diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index b887b83f9..c640d5f10 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -328,6 +328,7 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) break; case NV3_PRAMDAC_GENERAL_CONTROL: nv3->pramdac.general_control = value; + nv3_recalc_timings(&nv3->nvbase.svga); break; case NV3_PRAMDAC_VSERR_WIDTH: //vslines? diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index d421b28dd..1209faa2f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -30,12 +30,12 @@ uint32_t nv3_ramro_read(uint32_t address) { - nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", address); + nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), Probably shouldn't happenn\n", address); } void nv3_ramro_write(uint32_t address, uint32_t value) { - nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", value, address); + nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), Probably shouldn't happenn", value, address); } void nv3_ramro_send() From 493020ca51a3012bd5d183febb279384a797c011 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 23 Mar 2025 01:24:42 +0000 Subject: [PATCH 134/274] NV_IMAGE_FROM_CPU implemented, leading to the mouse cursor being drawn, and the windows start menu --- doc/nvidia_notes/status.xlsx | Bin 0 -> 15227 bytes .../86box/nv/classes/vid_nv3_classes.h | 10 +- src/include/86box/nv/vid_nv3.h | 3 +- .../nv/nv3/classes/nv3_class_007_rectangle.c | 2 +- .../nv/nv3/classes/nv3_class_011_image.c | 99 +++++++++++++++++- 5 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 doc/nvidia_notes/status.xlsx diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a301eba64e4f3c03a76698c87a828d2365c86b89 GIT binary patch literal 15227 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 Q{|Fhd4x(Qu`_H}q2j$D`YybcN literal 0 HcmV?d00001 diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 96df80eb1..80e2c1437 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -92,7 +92,15 @@ typedef enum nv3_pgraph_class_e #define NV3_RECTANGLE_START 0x0400 #define NV3_RECTANGLE_MAX 16 -#define NV3_RECTNAGLE_END 0x0480 +#define NV3_RECTANGLE_END 0x0480 + +// image_from_cpu +#define NV3_IMAGE_START_POSITION 0x0304 // starting position of image from cpu +#define NV3_IMAGE_SIZE 0x0308 +#define NV3_IMAGE_SIZE_IN 0x030C +#define NV3_IMAGE_COLOR_START 0x0400 +#define NV3_IMAGE_COLOR_MAX 32 +#define NV3_IMAGE_COLOR_END 0x0480 #define NV3_IMAGE_IN_MEMORY_COLOR_FORMAT 0x0300 #define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x0304 diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 640a0c951..29b2f70b9 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 20 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 22 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -1227,6 +1227,7 @@ typedef struct nv3_pgraph_s struct nv3_object_class_00E scaled_image_from_memory; struct nv3_object_class_010 blit; struct nv3_object_class_011 image; + nv3_position_16_t image_current_position; /* This is here so we can hold the current state of the image */ struct nv3_object_class_012 bitmap; struct nv3_object_class_014 transfer2memory; struct nv3_object_class_015 stretched_image_from_cpu; diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index a92a1f761..8f304fdac 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -37,7 +37,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: /* Check for any rectangle point or size method. */ - if (method_id >= NV3_RECTANGLE_START && method_id <= NV3_RECTNAGLE_END) + if (method_id >= NV3_RECTANGLE_START && method_id <= NV3_RECTANGLE_END) { uint32_t index = (method_id - NV3_RECTANGLE_START) / 8; diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index b40b9025c..9b4446cff 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -28,13 +28,108 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> +/* Check the line bounds */ +void nv3_class_011_check_line_bounds() +{ + uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; + //uint32_t relative_y = nv3->pgraph.image_current_position.y - nv3->pgraph.image.point.y; + + /* In theory, relative_y should never be exceeded...because it only submits enough pixels to render the image*/ + if (relative_x >= nv3->pgraph.image.size_in.w) + { + nv3->pgraph.image_current_position.y++; + nv3->pgraph.image_current_position.x = nv3->pgraph.image.point.x; + } +} + void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) { + case NV3_IMAGE_START_POSITION: + nv3->pgraph.image.point.x = (param & 0xFFFF); + nv3->pgraph.image.point.y = (param >> 16); + nv_log("Image Point=%d,%d", nv3->pgraph.image.point.x, nv3->pgraph.image.point.y); + break; + /* Seems to allow scaling of the bitblt. */ + case NV3_IMAGE_SIZE: + nv3->pgraph.image.size.w = (param & 0xFFFF); + nv3->pgraph.image.size.h = (param >> 16); + break; + case NV3_IMAGE_SIZE_IN: + nv3->pgraph.image.size_in.w = (param & 0xFFFF); + nv3->pgraph.image.size_in.h = (param >> 16); + nv3->pgraph.image_current_position = nv3->pgraph.image.point; + break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) + { + // shift left by 2 because it's 4 bits per si\e.. + uint32_t pixel_slot = (method_id - NV3_IMAGE_COLOR_START) >> 2; + uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + + /* todo: a lot of stuff */ + + uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; + + /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ + /* the reverse order is due to the endianness */ + switch (nv3->nvbase.svga.bpp) + { + // 4pixels packed into one + case 8: + + //pixel3 + pixel3 = param & 0xFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel3, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel2 = (param >> 8) & 0xFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel2, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel1 = (param >> 16) & 0xFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel0 = (param >> 24) & 0xFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + //2pixels packed into 1 + case 16: + pixel1 = (param) & 0xFFFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel0 = (param >> 16) & 0xFFFF; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + // just one + case 32: + pixel0 = param; + nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + } + } + else + { + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + + } return; } } \ No newline at end of file From 160e8a8071c7ba3de0843d887378080aed28fba0 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 23 Mar 2025 17:06:53 +0000 Subject: [PATCH 135/274] Clean up stupid colour structs and implement patterns (class 0x06). Now, you can select taskbar items and some lines draw. Replace some multiplications with shifts. --- doc/nvidia_notes/status.xlsx | Bin 15227 -> 15356 bytes .../86box/nv/classes/vid_nv3_classes.h | 61 +++---- src/include/86box/nv/render/vid_nv3_render.h | 6 +- src/include/86box/nv/vid_nv3.h | 21 ++- src/video/CMakeLists.txt | 3 +- .../nv3/classes/nv3_class_001_beta_factor.c | 2 +- src/video/nv/nv3/classes/nv3_class_002_rop.c | 2 +- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 7 +- .../nv/nv3/classes/nv3_class_006_pattern.c | 41 ++++- .../classes/nv3_class_01c_image_in_memory.c | 2 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 2 +- src/video/nv/nv3/render/nv3_render_core.c | 154 ++++++++++++++---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 50 +++--- 13 files changed, 227 insertions(+), 124 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index a301eba64e4f3c03a76698c87a828d2365c86b89..4118b2c5609b86ddcf989d9b715effdc97ee38e4 100644 GIT binary patch delta 6313 zcmZ8_1ymecuk%kU@ew$q;04cMB3EXn-L&gn{7h&OmT? zx5v$Y|KGoEuU_4IRqb=SS9hJN+ULgo)}vw>19&@OCutPFjzIY#MSYFDy4DTyASEGz|03rGk5O=XWrc^ws&gaj!p*|I zNK@(X_xWIqz1r7s|088H=&!zvP4Yc2C=eCxUHE5O4c$(%wY!VenyunonkL^iLK>Z zy9UrfWO#>l7;QD9wKu50l!W@)ku#IHp@G=6x$nlm%xY(LC6jY7=xHsTFFq}84s^g0 ze~v)AU`CAgsPXmXzcVWv0?dlVygouom2Wg-Bkp-IsHLFCM)I}&tB2hRuwOl_qGmxq zOsTHTd7tM^Kp0_5cTD<@tFXQB6TXtoJBbEjNE(}Zx{_9n7G6FHaI9Kxm%Ay9@)Hm3 z9Fx5su*4I8Y1o}J%DE*;TVg7&?35C#J~+4+s~roo6F-o%)Q0-$%a20_FD32mw9Qc)i^OTT=atcy8^w3r1by-;VRu!at2hTe zL^m2qTJhSOg4JrK&l2Am1Qr{Y?!Ww5ez0<%3Oe<}@-x$E zP6k08PFr2TKF^0{L~*K8NrX_+%)5qiTjk+6A}^r71v}u}UthLmEk)q07{+KPbEXOm zhwe$GsV23=e){z%r_Q2-Ez;`r*s1?=f8@s<@3V9&kD*PKW}Dwl`Zv@OBxcFeQNf6! z!rC8=ACEBET(RlGW1cew*K;P=E12#A&Ee6jY4%%`dWZ-o6!Nd3cNF;=f$AAT;c8q# zjuEj?f|qo=AL3h|w?Ve8tmlthFwq+qpGY^3{4o-nx5+iI4qwQ?7Sj9~F#tZUI*bUG z+NoZ^v44wZaJzU8V^#ZEaPn*z5lVl(N%#rt8Re<<_Ah}<{zyLZ8jHTleUjQSgxb9+ zR3}yH?Y+ap#GR=J*YtuLpf=iArI=#Y4=fz2t0Edm-yXexrjy@@#~LG%BucH^_2QTk z9pZe8&0zf}x`O9q!xJxp=K)jc_h%oNt2`+YukMY^+0hAaB{X#v+ve4y6k z+61uWU7nb2Ez<3XI{uKF2o8Cojryei$vj#zIsiaO93P7ZhH`Uw+S_HsD@UhufnW!T z*VOFDP;(l+H?$>o)8Bncia%#i@}RSkYRp}!PdqGsv8!TR8@6UN#rfuC3G=c#_Z-Ka z32^JJ7Fzqp%{Z)}QS2T+Mvc^wkPJP^eJNwE-@S!R@hJm(&nrUO=*H*AEN5+`VtddS z!Od(t+!m>X34Q6!YX0-^(ng@)jaXU;eG3+}a+Cnc(f4Nha83m)Vr9(XaxTm{vkD24 z^(;kB6uXe5C0#64SIila$bA!$F4g*W3-MFWwK%VWL=x>_+`K;4m2P;m>Q!qYX9Kz-m{;5S4+HA0IGRe8F#tb`vhq^g86=ot@Oc& zf3E0NA@olNn(9+3pjryCLFk+y{4}OQ#i+P@u{X)02FFerN>l}6 zsYW9QL(pX25{X`umn=E>jh#@QV9sp*pIyzVrUA{8RmYls@h_B1O$k-Ji+OSB4&|%2 zJ#g|FqXNXfk4_2UI(1Dh-71{DP*@%G`UcI+yd`pa)>(vuur>*(ynT>?=lDYb0Kmh; z-%CQujtVL_NY*517Y(Z!)sivxkPcn`HXqu_XuP=SE>e9;E^m3Cde%Mjac(8a$y+Ro zGk9gKj$9o1izG`m08BjAb!4v~E;jrA$o%%`^zzt8P_^)6ZVjRB2kzPT#yqee+eC;bvK$qV}Saugg6g>F#l=cL0Awbg5cmYjQ z9pIX84r=6-qaZ$$=(Lv+b{S6L!LfY4?w{wReNsc4DYTI3+xZG-9kA{{ALX?`tZHR! z+YiQYL3fcD<#FhnynCn?Aaj8snlM}>7}m==Gv0GKv>Jj!XG(duQ!zXY8Z1cg==DCx zPEk|Q454b0j&y6Oy=qdRDv^$mD~GZs)+ZH}y^mI-m1WQY4jkrQ)qJrYd`cfzl4j{S zE-D*I8f%!y*WS2ZY5O~5wYUoWl|LFn|H$naIBf7LCFiEAFLr=o4w^58#7cJ-KM@%F zv@A*-McUI{R_w^L(1-SjV+}86JD!qE`{=xvcGg;E=QM0>!+U(f6Du$1GU{J~R^6$8 zyVD;^$CS@9gl*oOvH!t*~Rb7q6jaBiJ*cI9^QaZB+>}Hn~=VH(RFAk;DKVR$r zIC6U{Ch~ftR%Sv|GJ7>Asy?}#V8)|R*kb3sS2q?x0|08{WAON)yTz}kqU!(V4zHqZ zXqIgXLt<5^Eb52;HIa?ku9L!eyB?Hkyka)UCNSYmmlk7#y*%_@SM`-bgoROu{)B}5 zkm=^wpOxS%wXIADfL%h;N?}%aj-v1+v}8Fl`bgVJv+a8MsndBK zRqZ8MrW4=14#Hkp%{vM^jN*LNew!IbXvp@>W`}l$S`1m6byGidndpjqkV@e?;-d(h zYb9s(&dTR%uF556|IBRX9aO(I+F`88ct<|;`o|v&La36q9#=ZU4x=UefKL1i+_;Ui zS2)M)4{u3M6qcSfmWRL^^0L~@z@6hJ{lS1cd`I4g$WV*)gJINKKRQ}Qjx6$g_wqwv zA*ys*PAe_A_77laxF#y%ygy4IQKIda4f}fSk&J`bhwBn1qH#Oqti$#t@OMq+iwsY- zuz`kZLg<7S5N58Vwh*4-R<0ko6fd_NMP3k*DchJ^r7REc?i=MzwdBbo^>H6*FUBQp zB#u(WMv|AT9#p>e{T3xU504dygrg^$mi9DrVHXTe5`}2rBKp|=zm$?-@Fai>BvXrD z<+puVBc0O`8A5bh)e&Ovfds#-gkp#CisS$3N$pEf>&`jYD9D`CSGr(MXML8@^HxN` zFq3`|1}0&#;*^`+_w^BI-o3^XXxB(M)^~MXmkef&5^Ra7YaEXWH!YGG{uH|dThgGF zDT)Dvm!MaEZPnHzVsalSEw!+&WunqHb7K>%GPC;J=$4YG+pa$iw|ulIx!DrVmDLA7 zTF7R~f*$V+`hvpmymdqQ_WkJD0;y(+_?t`*P)Ma*JaM(B4~1Jum!$7LH9tbjsHWHS z7)ju{V4w7P(}!qZz(3*rgb#rcd;S>SDY*YsPB=Od4=U71CRTe|0wlGqBS19%u{J9* z{SAIJqxj3V=SOySQhdg+_co11B_(qk^!1s6gguuNMsEd$lj!P|903o=s?F<_E>)3; zJIA2-nQ&GaqchI|a${F~1qKl5@^15B@u{&6*x*g5GPg5b=^CZ6s^ z8kRikV|3_@+gHQS!p%HYE*23@E=XB|lxxIDfo`S3-Sd1=No+lXo_hOl*eaoBC<=b) zI@mLNtPs@1JdL&_e^bja!(-KF4cya@)>jaJw_H=dF^)EDy}2SsSqXwt0w7w(Ii5=Q ztjcKOC>EOx0#GCsNLUp$K8kFH(sIWb5qjBG17;e}<3%WD>FSnKVc!OV^Y34Xw-y4$C*u*odBK8cK=vu7 z4KuhC1t}_Y;h8@Tp`S^|5#Wk?b+~LYT+n5wI8Csm_JBeaO>_n(5V4GjyppJ{Kfm+* z*i|#qeh0?=DJS!RYHy&mv{%}d^1XW4W+Jh@j?v=AqU#+lalDasn5;+LMo791il8r7 zg)pC9&PvgU(}j#mV5@cPI6ilsJ-{)Hx;1UuJ2n$4<-Qo_g+1eA%rez+g~45=w5yV7 z>r5wo{pYTsZkk5cokbhmmjWWSSJQ0afVE&Glj$W@nwd!^WL55T=5`H7nLdv3H8qpW zUg9_3Do0A<>|qhpHxep-<))<-gB7EP$6A;@;?JGiLkF&p6DApz2PUS)fSl6oxQ4j! zH6EI1JWXHHpn6nZ-Gwa#oTK~`v~VCz6E<|@8Qk6HF>zQ!Gzhq;8-!E4F1!$)#6v|0M$ccHi~Er|S*=dBdEg zOZp1Xr{dR@WhW1n<$(dqFg*|M6l*(z2oX{wnQUnP>h|W-zA~2$yA~snekBg{KsNw6 z1oaWH-Bqb*#O!28#hUnViJ0}?zEM7EWw0-I9U$~As*W9R8@IbH;Eza(oMQ;0#*e#< za}M$eEMiwdi5>Tyn>5a`P=0CRg4io>Z<8Y)izd&H2aZC;rQ5)IjSq1L(Es4_w6HdO(k-&~=moqB#hrWa>s z{VwW2!MX(;b~GtNOTz}1n$HM$BMvj_3KF2 z;c;V;+M0E&wMcFpK#i&7e0}&)^ftD;0qxJk`f6%gp`|dlGbi!DQc{?hvAENvc{P;} z&W_+ODw3ZQ5(vdcG@47%lJ<|%JNmoxnXl%FIo}64gz)_VR+vK_np!f4 zgO1k(bRRn1_7Fi4G)@9UwtIybX~Lf0%9g?RH||dHaVe2_Z(-b&D~L_?eBA<^csPb$x+Jc!4Z_*NJ5gTfC*Zde4E%hHi&nrIc zrWZK`4snKE##ETgtf9oUPE^r#T~_=Xzn}ZJ40x=r6s?iQVV9p*&c3x~528VwGRt1x z$X#}sR9t!dqspKT9b(@v9tD}=vqw?(zl+oQy|cv!4OeH#N2|YUG(*S08GkTbvRH8WY2eVFrrxOv`TfIf|xTPvR=@c*jS&?PHg}TOp4;)@b237x65W z!DPKB$afHq4#)uV!p4s-37ceWY<9)-uu=mJYM&yf%L@EDsr}t{!|K<)bM8+RVYX9< zy`|FUdFxG27f}&TJnXFkXcY1TfahjwcE?0-03XGs7 zC<$p(8HJmT6?0lvW(u6vCY2aQ7|3lSm4HSd(WP4Mfemynk@ZdBG z<+5VN^|^11pyM;pMlvRQc+WGWr0aPtT%=+4sqU$G-3d`F8X`?3Ga=n!o*1V+Tg-hL z{qIPXC=zEe7mu@75*ZhQ%{`N~7q>kZtM-JLy@T$P&DCYUup9@DWDeW4QEKF=jM-8N z^Ak5Toq`OLlN*wsi$u8ju2AzQXP$q7!WVZv6?f{6J6}*lHIkbn(TdE2o{lzkOXSGC zQ>&w#Itwc*ptw9JaL*MDmd+*qMSdNqTj^VvrufT#IpKtBWem)kLU?qmTzD-h-z$Db z&&Zc1#x+`z;OqczRCLN}t373T$kdH!dmZ9X_5)0Ei}rpN)2Rf z$YgC*Bl6Qi$z^?G2$U-Z=%O4pv$hTrdPWzR5Pkf`n)X?X_paFVN(*u9;q9!8r6-uk zyQFC2;jTZ25UHaI1gYJ;wDNr@e@bH33rzCZ=UQe@?v0(_*7&7{b>=h;eIEU#Qxx~m zb3Ngqnb)_i2-hK*IPF#Oo%sh*o6b@Nj~EqZllND$3s3( zS3Dj@9t?pxR)dSy2#K}o<+liCw(k7(q*@T3mrSl&aY*D!ee3!5cN`Nps`D~# z3Yuiw3~3<|wE;FiE=;I079u6z_L+yUC{OmJWg|QdP{}L}# zbL#ivrNJmG5Q!@-sQbGTNWJk;6~4C#h+D6*k4#(kStzI%$6Gp*w>fr_Fp)RA%`bvk zK8%KW$04aLSI~piEieUA%xuuVFt@RvjMv1SB_SBNxUy!g*sPALCb2_duA;{`H=fAh zJg0W?{rWo$_f`wE1zqV&3)!(jXpf`VwJ%06Jy0}YeD4EMf#7uc8(%(66`i?+U29%_ z%)JvKUC-hilntN?uzL6>MFNf5`%LBG?5~(mplXWkT!cYL;Db~T+-P)C2-_Z01@`=j zKT$_*%)EUEKel}J&MyJ`TK&xDubDRHIX4XBI4<0XeElo)g6mt7*vs?riR%3hYS#wDe$aNAxme{LAIs)I^}3MxrNO- z^mfFq((*Rb9;GYLaG8vPpr3mlMM?~R1cl`?WSqKS8^z!Jdp5=vdz2;K{kbsL&pVfF z9j3ts*Ru1>XHJE72uvr}1A8H|^nz4AdFu9!=);}%I}H;)c8glJXD?KNh4TQH-c^q> zzi`w1JWZw!f*b?0j#8h)o5cj0kFzF=QW^^`H)LoO(n|p&Ih|@=^4hf>WNX}^8?^Fm zf*iTPX2W5x<4Lgx^OmQ=hU3AY2U;dzC=0D?OA093Pqc$Mi~a#M{)n0o{+$Z{J%Zzzwg=~ zpCn8}{@=3tuirU-M3|ZUZ%m;801#9F0PkPtBTA1a5P6MC&KIvCLWhAR_>b>@0DSw} A>i_@% delta 6187 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.chroma_key = *(nv3_color_x2a10g10b10_t*)&color; + + nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; + break; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 2c2174447..36ce70e19 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -32,9 +32,48 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + /* Valid software method, suppress logging */ + case NV3_PATTERN_FORMAT: + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + break; + case NV3_PATTERN_SHAPE: + /* If the shape is not valid, tell the software that it's invalid */ + + /* + Technically you are meant to do this: + + But in practice, I don't know, because it always submits 0x20 or 0x40, which are valid when param & 0x03, + and appear to be deliberate behaviour in the drivers rather than bugs. What + if (param > NV3_PATTERN_SHAPE_LAST_VALID) + { + warning("NV3 class 0x06 (Pattern) invalid shape %d (This is a bug)", param); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_DATA); + return; + } + + */ + nv3->pgraph.pattern_shape = param & 0x03; + + break; + case NV3_PATTERN_COLOR0: + nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(grobj, param); + nv3_render_set_pattern_color(expanded_colour0, false); + break; + case NV3_PATTERN_COLOR1: + nv3_color_expanded_t expanded_colour1 = nv3_render_expand_color(grobj, param); + nv3_render_set_pattern_color(expanded_colour1, true); + break; + case NV3_PATTERN_BITMAP_HIGH: + nv3->pgraph.pattern_bitmap = 0; //reset + nv3->pgraph.pattern_bitmap |= ((uint64_t)param << 32); + break; + case NV3_PATTERN_BITMAP_LOW: + nv3->pgraph.pattern_bitmap |= param; + + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; + break; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 9ba86c0eb..a6cb52fbf 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -85,6 +85,6 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; + break; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 0bb12a2eb..9985845cf 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -35,7 +35,7 @@ void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t { /* mthdCreate in software(?)*/ case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("mthdCreate\n"); + nv_log("mthdCreate obj_name=0x%08x\n", param); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; // set up the current notification request/object diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 8ccca2e6f..741f8e422 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -57,14 +57,12 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) // 555 case nv3_pgraph_pixel_format_r5g5b5: - nv3_color_16_a1r5g5b5_t new_color = *(nv3_color_16_a1r5g5b5_t*)&color; // "stretch out" the colour - color_final.r = new_color.r * 0x20; - color_final.g = new_color.g * 0x20; - color_final.b = new_color.b * 0x20; - if (alpha_enabled) - color_final.a = new_color.a; + color_final.a = (color >> 15) & 0x01; // will be ignored if alpha_enabled isn't used + color_final.r = ((color >> 10) & 0x1F) << 5; + color_final.g = ((color >> 5) & 0x1F) << 5; + color_final.b = (color & 0x1F) << 5; break; // 888 (standard colour + 8-bit alpha) @@ -76,18 +74,12 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) color_final.g = ((color >> 8) & 0xFF) * 4; color_final.b = (color & 0xFF) * 4; - if (alpha_enabled) - color_final.a = new_color.a; break; case nv3_pgraph_pixel_format_r10g10b10: - nv3_color_x2a10g10b10_t new_color_rgb10 = *(nv3_color_x2a10g10b10_t*)&color; - - color_final.r = new_color_rgb10.r; - color_final.g = new_color_rgb10.g; - color_final.b = new_color_rgb10.b; - - if (alpha_enabled) - color_final.a = new_color.a; + color_final.a = (color << 31) & 0x01; + color_final.r = (color << 30) & 0x3FF; + color_final.g = (color << 20) & 0x1FF; + color_final.b = (color << 10); break; case nv3_pgraph_pixel_format_y8: @@ -115,7 +107,7 @@ nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) } /* Used for chroma test */ -uint32_t nv3_render_downconvert(nv3_grobj_t grobj, nv3_color_expanded_t color) +uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color) { uint8_t format = (grobj.grobj_0 & 0x07); bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; @@ -129,18 +121,30 @@ uint32_t nv3_render_downconvert(nv3_grobj_t grobj, nv3_color_expanded_t color) switch (format) { case nv3_pgraph_pixel_format_r5g5b5: - packed_color = (color.r / 0x20) << 10 | - (color.g / 0x20) << 5 | - (color.b / 0x20); + packed_color = (color.r >> 5) << 10 | + (color.g >> 5) << 5 | + (color.b >> 5); break; case nv3_pgraph_pixel_format_r8g8b8: + packed_color = (color.a) << 24 | // is this a thing? + (color.r >> 2) << 16 | + (color.g >> 2) << 8 | + color.b; break; case nv3_pgraph_pixel_format_r10g10b10: + /* sometimes alpha isn't used but we should incorporate it anyway */ + if (color.a > 0x00) packed_color | (1 << 31); + + packed_color |= (color.r << 30); + packed_color |= (color.g << 20); + packed_color |= (color.b << 10); break; case nv3_pgraph_pixel_format_y8: + nv_log("nv3_render_downconvert: Y8 not implemented"); break; case nv3_pgraph_pixel_format_y16: + nv_log("nv3_render_downconvert: Y16 not implemented"); break; default: warning("nv3_render_downconvert_color unknown format %d", format); @@ -164,6 +168,37 @@ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.a << 10); } +/* Convert a rgb10 colour to a pattern colour */ +uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1) +{ + /* reset the colour */ + if (!use_color1) + nv3->pgraph.pattern_color_0_rgb.r = nv3->pgraph.pattern_color_0_rgb.g = nv3->pgraph.pattern_color_0_rgb.b = 0x00; + else + nv3->pgraph.pattern_color_1_rgb.r = nv3->pgraph.pattern_color_1_rgb.g = nv3->pgraph.pattern_color_1_rgb.b = 0x00; + + /* select the right pattern colour, _rgb is already in RGB10 format, so we don't need to do any conversion */ + + if (!use_color1) + { + nv3->pgraph.pattern_color_0_alpha = (pattern_colour.a) & 0xFF; + nv3->pgraph.pattern_color_0_rgb.r = pattern_colour.r; + nv3->pgraph.pattern_color_0_rgb.g = pattern_colour.g; + nv3->pgraph.pattern_color_0_rgb.b = pattern_colour.b; + + } + else + { + nv3->pgraph.pattern_color_1_alpha = (pattern_colour.a) & 0xFF; + nv3->pgraph.pattern_color_1_rgb.r = pattern_colour.r; + nv3->pgraph.pattern_color_1_rgb.g = pattern_colour.g; + nv3->pgraph.pattern_color_1_rgb.b = pattern_colour.b; + } + + + +} + /* Plots a pixel. */ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) { @@ -224,6 +259,46 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr pixel_addr_vram &= nv3->nvbase.svga.vram_mask; + uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; + uint8_t bit = 0x00; + + /* Get our pattern data, may move to another function */ + switch (nv3->pgraph.pattern.shape) + { + + /* This logic is from NV1 envytoos docs, but seems to be same on NV3*/ + case NV3_PATTERN_SHAPE_8X8: + bit = (position.x & 7) | (position.y & 7) << 3; + break; + case NV3_PATTERN_SHAPE_1X64: + bit = (position.x & 0x3f); + break; + case NV3_PATTERN_SHAPE_64X1: + bit = (position.y & 0x3f); + break; + } + + // pull out the actual bit and see which colour we need to use + + bool use_color1 = (nv3->pgraph.pattern_bitmap >> bit) & 0x01; + + if (!use_color1) + { + if (!nv3->pgraph.pattern_color_0_alpha) + return; + + /* This is stupid */ + rop_pattern = nv3_render_downconvert_color(grobj, nv3->pgraph.pattern_color_0_rgb); + } + else + { + if (!nv3->pgraph.pattern_color_1_alpha) + return; + + rop_pattern = nv3_render_downconvert_color(grobj, nv3->pgraph.pattern_color_1_rgb); + } + + /* Go to vram and do the final ROP for a basic bitblit. It seems we can skip the downconversion step *for now*, since (framebuffer bits per pixel) == (object bits per pixel) I'm not sure how games will react. But it depends on how the D3D drivers operate, we may need ro convert texture formats to the current bpp internally. @@ -231,14 +306,12 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr TODO: MOVE TO BPIXEL DEPTH or GROBJ0 to determine this, once we figure out how to get the bpixel depth. */ - uint32_t src = 0, dst = 0; - switch (framebuffer_bpp) { case 8: - src = color & 0xFF; - dst = nv3->nvbase.svga.vram[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00) & 0xFF; + rop_src = color & 0xFF; + rop_dst = nv3->nvbase.svga.vram[pixel_addr_vram]; + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern) & 0xFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; @@ -246,19 +319,28 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr case 16: uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 1; + + // mask to 16bit - // mask off the alpha bit. Even though the drivers should send this. What! - if (!alpha_enabled) - src = ((color & (~0x8000)) & 0xFFFF); - else - src = color & 0xFFFF; + rop_src = color & 0xFFFF; + + /* if alpha is turned on and we aren't in 565 mode, reject transparent pixels */ + bool is_16bpp = (nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01; + + // a1r5g5b5 + if (!is_16bpp) + { + if (alpha_enabled && + !(color & 0x8000)) + return; + } // convert to 16bpp // forcing it to render in 15bpp fixes it, - - dst = vram_16[pixel_addr_vram]; - vram_16[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00) & 0xFFFF; + rop_dst = vram_16[pixel_addr_vram]; + + vram_16[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern) & 0xFFFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; @@ -267,9 +349,9 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 2; - src = color; - dst = vram_32[pixel_addr_vram]; - vram_32[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, src, dst, 0x00); + rop_src = color; + rop_dst = vram_32[pixel_addr_vram]; + vram_32[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern); nv3->nvbase.svga.changedvram[pixel_addr_vram >> 10] = changeframecount; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 993232c9d..70aef4270 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -63,10 +63,10 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_SRC_CANVAS_MAX, "PGRAPH Source Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_DST_CANVAS_MIN, "PGRAPH Destination Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_DST_CANVAS_MAX, "PGRAPH Destination Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_PATTERN_COLOR_0_0, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_0_1, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_1_0, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_1_1, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_pattern_color_0_rgb, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_pattern_color_0_alpha, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_pattern_color_1_rgb, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_pattern_color_1_alpha, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, { NV3_PGRAPH_PATTERN_BITMAP_HIGH, "PGRAPH Pattern Bitmap (High 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_BITMAP_LOW, "PGRAPH Pattern Bitmap (Low 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_SHAPE, "PGRAPH Pattern Shape (1:0 - 0=8x8, 1=64x1, 2=1x64)", NULL, NULL}, @@ -185,23 +185,23 @@ uint32_t nv3_pgraph_read(uint32_t address) ret = *(uint32_t*)&nv3->pgraph.src_canvas_max; break; // Pattern - case NV3_PGRAPH_PATTERN_COLOR_0_0: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_0; + case NV3_PGRAPH_pattern_color_0_rgb: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb; break; - case NV3_PGRAPH_PATTERN_COLOR_0_1: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_1; + case NV3_PGRAPH_pattern_color_0_alpha: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha; break; - case NV3_PGRAPH_PATTERN_COLOR_1_0: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_0; + case NV3_PGRAPH_pattern_color_1_rgb: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb; break; - case NV3_PGRAPH_PATTERN_COLOR_1_1: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_1; + case NV3_PGRAPH_pattern_color_1_alpha: + ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha; break; case NV3_PGRAPH_PATTERN_BITMAP_HIGH: - ret = nv3->pgraph.pattern_bitmap_high; + ret = (nv3->pgraph.pattern_bitmap >> 32) & 0xFFFFFFFF; break; case NV3_PGRAPH_PATTERN_BITMAP_LOW: - ret = nv3->pgraph.pattern_bitmap_low; + ret = (nv3->pgraph.pattern_bitmap & 0xFFFFFFFF); break; // Beta factor case NV3_PGRAPH_BETA: @@ -388,23 +388,23 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) *(uint32_t*)&nv3->pgraph.src_canvas_max = value; break; // Pattern - case NV3_PGRAPH_PATTERN_COLOR_0_0: - *(uint32_t*)&nv3->pgraph.pattern_color_0_0 = value; + case NV3_PGRAPH_pattern_color_0_rgb: + *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb = value; break; - case NV3_PGRAPH_PATTERN_COLOR_0_1: - *(uint32_t*)&nv3->pgraph.pattern_color_0_1 = value; + case NV3_PGRAPH_pattern_color_0_alpha: + *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha = value; break; - case NV3_PGRAPH_PATTERN_COLOR_1_0: - *(uint32_t*)&nv3->pgraph.pattern_color_1_0 = value; + case NV3_PGRAPH_pattern_color_1_rgb: + *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb = value; break; - case NV3_PGRAPH_PATTERN_COLOR_1_1: - *(uint32_t*)&nv3->pgraph.pattern_color_1_1 = value; + case NV3_PGRAPH_pattern_color_1_alpha: + *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha = value; break; case NV3_PGRAPH_PATTERN_BITMAP_HIGH: - nv3->pgraph.pattern_bitmap_high = value; + nv3->pgraph.pattern_bitmap |= ((uint64_t)value << 32); break; case NV3_PGRAPH_PATTERN_BITMAP_LOW: - nv3->pgraph.pattern_bitmap_low = value; + nv3->pgraph.pattern_bitmap |= value; break; // Beta factor case NV3_PGRAPH_BETA: @@ -415,7 +415,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) nv3->pgraph.rop = value & 0xFF; break; case NV3_PGRAPH_CHROMA_KEY: - nv3->pgraph.chroma_key = *(nv3_color_x2a10g10b10_t*)value; + nv3->pgraph.chroma_key = value; break; case NV3_PGRAPH_PLANE_MASK: nv3->pgraph.plane_mask = value; From 0b0e6c5256504e72378b319b42e2ca45b5b5107e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 23 Mar 2025 19:12:02 +0000 Subject: [PATCH 136/274] More refactoring + implement screen2screen blits --- .../86box/nv/classes/vid_nv3_classes.h | 5 + src/include/86box/nv/render/vid_nv3_render.h | 14 ++- src/include/86box/nv/vid_nv3.h | 10 +- src/video/CMakeLists.txt | 4 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 16 ++++ .../nv/nv3/classes/nv3_class_011_image.c | 14 +-- src/video/nv/nv3/render/nv3_render_blit.c | 92 +++++++++++++++++++ src/video/nv/nv3/render/nv3_render_core.c | 87 +++++++++++++----- .../nv/nv3/render/nv3_render_primitives.c | 2 +- src/video/nv/nv3/subsystems/nv3_pgraph.c | 24 ++--- 10 files changed, 216 insertions(+), 52 deletions(-) create mode 100644 src/video/nv/nv3/render/nv3_render_blit.c diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 83d378031..5e564fe29 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -113,6 +113,11 @@ typedef enum nv3_pgraph_class_e #define NV3_RECTANGLE_MAX 16 #define NV3_RECTANGLE_END 0x0480 +// blit +#define NV3_BLIT_POSITION_IN 0x0300 +#define NV3_BLIT_POSITION_OUT 0x0304 +#define NV3_BLIT_SIZE 0x0308 + // image_from_cpu #define NV3_IMAGE_START_POSITION 0x0304 // starting position of image from cpu #define NV3_IMAGE_SIZE 0x0308 diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index c5741f30c..e1cb0f65f 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,15 +18,25 @@ #pragma once /* Core */ -void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); +void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj); +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj); +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj); + uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color); // Convert a colour to full RGB10 format from the current working format. uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color); // Convert a colour from the current working format to RGB10 format. + + /* Pattern */ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); /* Primitives */ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); -void nv3_render_chroma_test(nv3_grobj_t grobj); \ No newline at end of file +/* Chroma */ +void nv3_render_chroma_test(nv3_grobj_t grobj); + +/* Blit */ +void nv3_render_blit_screen2screen(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index ed8d9c4fc..aa59f7e69 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -569,10 +569,10 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, Y=30:16, X=10:0 #define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, Y=30:16, X=10:0 #define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_pattern_color_0_rgb 0x400600 -#define NV3_PGRAPH_pattern_color_0_alpha 0x400604 -#define NV3_PGRAPH_pattern_color_1_rgb 0x400608 -#define NV3_PGRAPH_pattern_color_1_alpha 0x40060C // pattern color +#define NV3_PGRAPH_PATTERN_COLOR_0_RGB 0x400600 +#define NV3_PGRAPH_PATTERN_COLOR_0_ALPHA 0x400604 +#define NV3_PGRAPH_PATTERN_COLOR_1_RGB 0x400608 +#define NV3_PGRAPH_PATTERN_COLOR_1_ALPHA 0x40060C // pattern color #define NV3_PGRAPH_PATTERN_BITMAP_HIGH 0x400610 // pattern bitmap [31:0] #define NV3_PGRAPH_PATTERN_BITMAP_LOW 0x400614 // pattern bitmap [63:32] #define NV3_PGRAPH_PATTERN_SHAPE 0x400618 @@ -1183,7 +1183,7 @@ typedef struct nv3_pgraph_s uint64_t pattern_bitmap; // pattern bitmap for blit. it's alwaus 64 bits to simplify pixel rendering code uint32_t pattern_shape; // may need to be an enum - 0=8x8, 1=64x1, 2=1x64 uint32_t plane_mask; // only 7:0 relevant - uint32_t chroma_key; // color key + uint32_t chroma_key; // color key uint32_t beta_factor; nv3_pgraph_dma_settings_t dma_settings; uint8_t rop; // Current GDI Ternary Render Operation diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 43e7cf2a0..2e6d3af08 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -128,7 +128,9 @@ add_library(vid OBJECT nv/nv3/classes/nv3_class_01c_image_in_memory.c nv/nv3/render/nv3_render_core.c - nv/nv3/render/nv3_render_primitives.c + nv/nv3/render/nv3_render_primitives.c + nv/nv3/render/nv3_render_blit.c + ) if(G100) diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 3ff52f68a..0f15e82d6 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -32,6 +32,22 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + case NV3_BLIT_POSITION_IN: + nv3->pgraph.blit.point_in.x = (param & 0xFFFF); + nv3->pgraph.blit.point_in.y = ((param >> 16) & 0xFFFF); + break; + case NV3_BLIT_POSITION_OUT: + nv3->pgraph.blit.point_out.x = (param & 0xFFFF); + nv3->pgraph.blit.point_out.y = ((param >> 16) & 0xFFFF); + break; + case NV3_BLIT_SIZE: + /* This is the last one*/ + nv3->pgraph.blit.size.w = (param & 0xFFFF); + nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); + + nv3_render_blit_screen2screen(grobj); + + break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 9b4446cff..dc865243e 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -81,22 +81,22 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ //pixel3 pixel3 = param & 0xFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel3, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel2 = (param >> 8) & 0xFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel2, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel1 = (param >> 16) & 0xFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel0 = (param >> 24) & 0xFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); @@ -104,12 +104,12 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ //2pixels packed into 1 case 16: pixel1 = (param) & 0xFFFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel0 = (param >> 16) & 0xFFFF; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); @@ -117,7 +117,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // just one case 32: pixel0 = param; - nv3_render_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c new file mode 100644 index 000000000..ce836520b --- /dev/null +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -0,0 +1,92 @@ +/* +* 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 Core rendering code (Software version) +* +* +* +* Authors: Connor Hyde, I need a better email address ;^) +* +* Copyright 2024-2025 Connor Hyde +*/ + +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/plat.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv3.h> + +void nv3_render_blit_screen2screen(nv3_grobj_t grobj) +{ + nv3_position_16_t old_position = nv3->pgraph.blit.point_in; + nv3_position_16_t new_position = nv3->pgraph.blit.point_out; + + uint16_t end_x = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); + uint16_t end_y = (nv3->pgraph.blit.point_out.y + nv3->pgraph.blit.size.h); + + /* Read the old pixel */ + switch (nv3->nvbase.svga.bpp) + { + case 8: //8bpp + for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) + { + old_position.y++; + new_position.y++; + + for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) + { + old_position.x++; + new_position.x++; + + uint32_t pixel_to_copy = nv3_render_read_pixel_8(old_position, grobj) & 0xFF; + nv3_render_write_pixel(new_position, pixel_to_copy, grobj); + } + } + break; + case 16: //16bpp + for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) + { + old_position.y++; + new_position.y++; + + for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) + { + old_position.x++; + new_position.x++; + + uint32_t pixel_to_copy = nv3_render_read_pixel_16(old_position, grobj) & 0xFFFF; + nv3_render_write_pixel(new_position, pixel_to_copy, grobj); + } + } + break; + case 32: //32bpp + for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) + { + old_position.y++; + new_position.y++; + + for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) + { + old_position.x++; + new_position.x++; + + uint32_t pixel_to_copy = nv3_render_read_pixel_32(old_position, grobj); + nv3_render_write_pixel(new_position, pixel_to_copy, grobj); + } + } + break; + } +} \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 741f8e422..c7ffd5f44 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -194,13 +194,71 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool nv3->pgraph.pattern_color_1_rgb.g = pattern_colour.g; nv3->pgraph.pattern_color_1_rgb.b = pattern_colour.b; } - - } +/* /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ +uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj) +{ + uint32_t vram_x = position.x; + uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; + + // we have to multiply the x position by the number of bytes per pixel + switch (framebuffer_bpp) + { + case 8: + break; + case 16: + vram_x = position.x << 1; + break; + case 32: + vram_x = position.x << 2; + break; + } + + uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * position.y) + nv3->pgraph.boffset[current_buffer]; + + pixel_addr_vram &= nv3->nvbase.svga.vram_mask; + + return pixel_addr_vram; +} + +/* Read an 8bpp pixel from the framebuffer. */ +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj) +{ + // hope you call it with the right bit + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + + return nv3->nvbase.svga.vram[vram_address]; +} + +/* Read an 16bpp pixel from the framebuffer. */ +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) +{ + // hope you call it with the right bit + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + + uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); + vram_address >>= 1; //convert to 16bit pointer + + return vram_16[vram_address]; +} + +/* Read an 16bpp pixel from the framebuffer. */ +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) +{ + // hope you call it with the right bit + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + + uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); + vram_address >>= 1; //convert to 16bit pointer + + return vram_32[vram_address]; +} + /* Plots a pixel. */ -void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) +void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) { uint8_t alpha = 0xFF; @@ -213,7 +271,7 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr #endif bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; - uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too? + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; /* doesn't seem*/ @@ -238,26 +296,7 @@ void nv3_render_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t gr /* TODO: Chroma Key, Pattern, Plane Mask...*/ - /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from. */ - - uint32_t vram_x = position.x; - - // we have to multiply the x position by the number of bytes per pixel - switch (framebuffer_bpp) - { - case 8: - break; - case 16: - vram_x = position.x << 1; - break; - case 32: - vram_x = position.x << 2; - break; - } - - uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * position.y) + nv3->pgraph.boffset[current_buffer]; - - pixel_addr_vram &= nv3->nvbase.svga.vram_mask; + uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; uint8_t bit = 0x00; diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index e44636acb..f4bfdebf4 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -40,7 +40,7 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co { current_pos.x = x; - nv3_render_pixel(current_pos, color, grobj); + nv3_render_write_pixel(current_pos, color, grobj); } } } \ 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 index 70aef4270..251ec7a5f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -63,10 +63,10 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_SRC_CANVAS_MAX, "PGRAPH Source Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_DST_CANVAS_MIN, "PGRAPH Destination Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, { NV3_PGRAPH_DST_CANVAS_MAX, "PGRAPH Destination Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_pattern_color_0_rgb, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_pattern_color_0_alpha, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, - { NV3_PGRAPH_pattern_color_1_rgb, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_pattern_color_1_alpha, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_0_RGB, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_0_ALPHA, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_1_RGB, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, + { NV3_PGRAPH_PATTERN_COLOR_1_ALPHA, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, { NV3_PGRAPH_PATTERN_BITMAP_HIGH, "PGRAPH Pattern Bitmap (High 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_BITMAP_LOW, "PGRAPH Pattern Bitmap (Low 32bits)", NULL, NULL}, { NV3_PGRAPH_PATTERN_SHAPE, "PGRAPH Pattern Shape (1:0 - 0=8x8, 1=64x1, 2=1x64)", NULL, NULL}, @@ -185,16 +185,16 @@ uint32_t nv3_pgraph_read(uint32_t address) ret = *(uint32_t*)&nv3->pgraph.src_canvas_max; break; // Pattern - case NV3_PGRAPH_pattern_color_0_rgb: + case NV3_PGRAPH_PATTERN_COLOR_0_RGB: ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb; break; - case NV3_PGRAPH_pattern_color_0_alpha: + case NV3_PGRAPH_PATTERN_COLOR_0_ALPHA: ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha; break; - case NV3_PGRAPH_pattern_color_1_rgb: + case NV3_PGRAPH_PATTERN_COLOR_1_RGB: ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb; break; - case NV3_PGRAPH_pattern_color_1_alpha: + case NV3_PGRAPH_PATTERN_COLOR_1_ALPHA: ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha; break; case NV3_PGRAPH_PATTERN_BITMAP_HIGH: @@ -388,16 +388,16 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) *(uint32_t*)&nv3->pgraph.src_canvas_max = value; break; // Pattern - case NV3_PGRAPH_pattern_color_0_rgb: + case NV3_PGRAPH_PATTERN_COLOR_0_RGB: *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb = value; break; - case NV3_PGRAPH_pattern_color_0_alpha: + case NV3_PGRAPH_PATTERN_COLOR_0_ALPHA: *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha = value; break; - case NV3_PGRAPH_pattern_color_1_rgb: + case NV3_PGRAPH_PATTERN_COLOR_1_RGB: *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb = value; break; - case NV3_PGRAPH_pattern_color_1_alpha: + case NV3_PGRAPH_PATTERN_COLOR_1_ALPHA: *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha = value; break; case NV3_PGRAPH_PATTERN_BITMAP_HIGH: From be83a7c69db50c298b8446b1dae91bbd1cd5dfbc Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 23 Mar 2025 23:42:44 +0000 Subject: [PATCH 137/274] FIFO optimisation, hitches down to ~1-2 seconds from 5 seconds, 70% speed idle at desktop --- doc/nvidia_notes/status.xlsx | Bin 15356 -> 15398 bytes .../86box/nv/classes/vid_nv3_classes.h | 2 + src/video/nv/nv3/subsystems/nv3_pfifo.c | 48 ++++++++++-------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 4118b2c5609b86ddcf989d9b715effdc97ee38e4..7d58d5250c8be03718dd86b5bfe94561227001a6 100644 GIT binary patch delta 4330 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 delta 4298 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 J37OPi=s(wCCwc$? diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 5e564fe29..076502f63 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -133,6 +133,8 @@ typedef enum nv3_pgraph_class_e #define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END 22 #define NV3_W95TXT_COLORA 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. +#define NV3_W95TXT_COLORA_RECT_START 0x0400 +#define NV3_W95TXT_COLORA_RECT_END 0x05FF /* Class context switch method */ typedef struct nv3_class_ctx_switch_method_s diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index d8b7cef96..088ddf1ba 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -653,14 +653,27 @@ that existed here before didn't make any sense #define NV3_GRAY_TABLE_NUM_ENTRIES 64 uint8_t nv3_pfifo_cache1_gray_code_table[NV3_GRAY_TABLE_NUM_ENTRIES] = { - 0b000000, 0b000001, 0b000011, 0b000010, 0b000110, 0b000111, 0b000101, 0b000100, - 0b001100, 0b001101, 0b001111, 0b001110, 0b001010, 0b001011, 0b001001, 0b001000, - 0b011000, 0b011001, 0b011011, 0b011010, 0b011110, 0b011111, 0b011101, 0b011100, - 0b010100, 0b010101, 0b010111, 0b010110, 0b010010, 0b010011, 0b010001, 0b010000, - 0b110000, 0b110001, 0b110011, 0b110010, 0b110110, 0b110111, 0b110101, 0b110100, - 0b111100, 0b111101, 0b111111, 0b111110, 0b111010, 0b111011, 0b111001, 0b111000, - 0b101000, 0b101001, 0b101011, 0b101010, 0b101110, 0b101111, 0b101101, 0b101100, - 0b100100, 0b100101, 0b100111, 0b100110, 0b100010, 0b100011, 0b100001, 0b100000 + 0b000000, 0b000001, 0b000011, 0b000010, 0b000110, 0b000111, 0b000101, 0b000100, //0x07 + 0b001100, 0b001101, 0b001111, 0b001110, 0b001010, 0b001011, 0b001001, 0b001000, //0x0F + 0b011000, 0b011001, 0b011011, 0b011010, 0b011110, 0b011111, 0b011101, 0b011100, //0x17 + 0b010100, 0b010101, 0b010111, 0b010110, 0b010010, 0b010011, 0b010001, 0b010000, //0x1F + 0b110000, 0b110001, 0b110011, 0b110010, 0b110110, 0b110111, 0b110101, 0b110100, //0x27 + 0b111100, 0b111101, 0b111111, 0b111110, 0b111010, 0b111011, 0b111001, 0b111000, //0x2F + 0b101000, 0b101001, 0b101011, 0b101010, 0b101110, 0b101111, 0b101101, 0b101100, //0x37 + 0b100100, 0b100101, 0b100111, 0b100110, 0b100010, 0b100011, 0b100001, 0b100000 //0x3F +}; + +/* The function is called up to hundreds of thousands of times per second, it's too slow to do anything else */ +uint8_t nv3_pfifo_cache1_binary_code_table[NV3_GRAY_TABLE_NUM_ENTRIES] = +{ + 0x00, 0x01, 0x03, 0x02, 0x07, 0x06, 0x04, 0x05, // 0x07 (0) + 0x0F, 0x0E, 0x0C, 0x0D, 0x08, 0x09, 0x0B, 0x0A, // 0x0F (1000) + 0x1F, 0x1E, 0x1C, 0x1D, 0x18, 0x19, 0x1B, 0x1A, // 0x17 (10000) + 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, // 0x1F (11000) + 0x3F, 0x3E, 0x3C, 0x3D, 0x38, 0x39, 0x3B, 0x3A, // 0x27 (100000) + 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, // 0x2F (101000) + 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, // 0x37 (110000) + 0x2F, 0x2E, 0x2C, 0x2D, 0x28, 0x29, 0x2B, 0x2A, // 0X3f (111000) }; uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val) @@ -673,14 +686,7 @@ Back to sanity */ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) { - /* Is this a good idea? */ - for (uint32_t i = 0; i < NV3_GRAY_TABLE_NUM_ENTRIES; i++) - { - if (nv3_pfifo_cache1_gray_code_table[i] == val) - return i; - } - - return 0x00; + return nv3_pfifo_cache1_binary_code_table[val]; } // Submits graphics objects INTO cache0 @@ -854,13 +860,13 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) } // We didn't. Let's put it in CACHE1 - uint32_t current_put_address = nv3->pfifo.cache1_settings.put_address >> 2; - nv3->pfifo.cache1_entries[current_put_address].subchannel = subchannel; - nv3->pfifo.cache1_entries[current_put_address].method = method_offset; - nv3->pfifo.cache1_entries[current_put_address].data = param; + uint32_t current_put_index = nv3->pfifo.cache1_settings.put_address >> 2; + nv3->pfifo.cache1_entries[current_put_index].subchannel = subchannel; + nv3->pfifo.cache1_entries[current_put_index].method = method_offset; + nv3->pfifo.cache1_entries[current_put_index].data = param; // now we have to recalculate the cache1 put address - uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_address); + uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_index); next_put_address++; if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# From 57edb25a2720046aca75f14bb1d6c537011bceaa Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 01:13:04 +0000 Subject: [PATCH 138/274] GDI acceleration type A + chroma test --- .../86box/nv/classes/vid_nv3_classes.h | 60 +++++++++++++++---- src/include/86box/nv/render/vid_nv3_render.h | 4 +- src/include/86box/nv/vid_nv3.h | 14 +++-- .../nv/nv3/classes/nv3_class_007_rectangle.c | 6 +- .../classes/nv3_class_00c_win95_gdi_text.c | 36 ++++++++++- .../nv/nv3/classes/nv3_class_011_image.c | 6 +- src/video/nv/nv3/render/nv3_render_core.c | 24 +++++++- src/video/nv/nv3/subsystems/nv3_pbus.c | 7 +++ src/video/nv/nv3/subsystems/nv3_pfb.c | 8 +++ src/video/nv/nv3/subsystems/nv3_pgraph.c | 8 +++ 10 files changed, 146 insertions(+), 27 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 076502f63..309a1c5aa 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -132,9 +132,54 @@ typedef enum nv3_pgraph_class_e #define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET 0x030C #define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END 22 -#define NV3_W95TXT_COLORA 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. -#define NV3_W95TXT_COLORA_RECT_START 0x0400 -#define NV3_W95TXT_COLORA_RECT_END 0x05FF +/* GDI */ + +/* Type A: Unclipped Rectangle */ +#define NV3_W95TXT_A_COLOR 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. +#define NV3_W95TXT_A_RECT_START 0x0400 +#define NV3_W95TXT_A_RECT_SIZE 64 // Number of rects +#define NV3_W95TXT_A_RECT_END 0x05FF + +/* Type B: Clipped Rectangle */ +#define NV3_W95TXT_B_CLIP_TOPLEFT 0x07F4 +#define NV3_W95TXT_B_CLIP_BOTTOMRIGHT 0x07F8 +#define NV3_W95TXT_B_CLIP_CLIPRECT_START 0x0800 +#define NV3_W95TXT_B_CLIP_CLIPRECT_SIZE 128 // Number of rects +#define NV3_W95TXT_B_CLIP_CLIPRECT_END 0x09FF + +/* Type C: Unscaled Single-Colour Text (filled in from mono bitmap) */ +#define NV3_W95TXT_C_CLIP_TOPLEFT 0x0BEC +#define NV3_W95TXT_C_CLIP_BOTTOMRIGHT 0x0BF0 +#define NV3_W95TXT_C_CLIP_COLOR 0x0BF4 +#define NV3_W95TXT_C_CLIP_SIZE 0x0BF8 +#define NV3_W95TXT_C_CLIP_POSITION 0x0BFC /* TOP LEFT */ +#define NV3_W95TXT_C_CLIP_CLIPRECT_START 0x0C00 +#define NV3_W95TXT_C_CLIP_CLIPRECT_SIZE 64 // Number of rects +#define NV3_W95TXT_C_CLIP_CLIPRECT_END 0x0DFF + +/* Type D: Scaled Single-Colour Text (filled in from mono bitmap) */ +#define NV3_W95TXT_D_CLIP_TOPLEFT 0x0FE8 +#define NV3_W95TXT_D_CLIP_BOTTOMRIGHT 0x0FEC +#define NV3_W95TXT_D_CLIP_COLOR 0x0FF0 +#define NV3_W95TXT_D_CLIP_SIZE_IN 0x0FF4 +#define NV3_W95TXT_D_CLIP_SIZE_OUT 0x0FF8 +#define NV3_W95TXT_D_CLIP_POSITION 0x0FFC /* TOP LEFT */ +#define NV3_W95TXT_D_CLIP_CLIPRECT_START 0x1000 +#define NV3_W95TXT_D_CLIP_CLIPRECT_SIZE 128 // Number of rects +#define NV3_W95TXT_D_CLIP_CLIPRECT_END 0x11FF + +/* Type E: Scaled Pattern-type Bitmap Text (filled) */ +#define NV3_W95TXT_E_CLIP_TOPLEFT 0x13E4 +#define NV3_W95TXT_E_CLIP_BOTTOMRIGHT 0x13E8 +#define NV3_W95TXT_E_CLIP_COLOR_0 0x13EC +#define NV3_W95TXT_E_CLIP_COLOR_1 0x13F0 +#define NV3_W95TXT_E_CLIP_SIZE_IN 0x13F4 +#define NV3_W95TXT_E_CLIP_SIZE_OUT 0x13F8 +#define NV3_W95TXT_E_CLIP_POSITION 0x13FC /* TOP LEFT */ +#define NV3_W95TXT_E_CLIP_CLIPRECT_START 0x1400 +#define NV3_W95TXT_E_CLIP_CLIPRECT_SIZE 128 // Number of rects +#define NV3_W95TXT_E_CLIP_CLIPRECT_END 0x15FF + /* Class context switch method */ typedef struct nv3_class_ctx_switch_method_s @@ -523,12 +568,6 @@ typedef struct nv3_object_class_00B uint8_t reserved4[0x19FB]; } nv3_triangle_t; -typedef struct nv3_object_class_00C_nclip_s -{ - nv3_position_16_t position; - nv3_size_16_t size; -} nv3_object_class_00C_nclip_t; - /* Object Class 0x0C (real hardware) 0x0C (drivers) @@ -544,7 +583,8 @@ typedef struct nv3_object_class_00C uint32_t set_notify; // Set notifier uint8_t reserved2[0x2F4]; uint32_t color_a; // Color for Clip A - nv3_object_class_00C_nclip_t rect_nclip[64]; + nv3_position_16_t rect_a_position[64]; + nv3_size_16_t rect_a_size[64]; uint8_t reserved3[0x1F0]; nv3_clip_16_t clip_b; uint32_t color_b; // Color for Clip B diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index e1cb0f65f..5f6d39ae2 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -27,8 +27,6 @@ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color); // Convert a colour to full RGB10 format from the current working format. uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color); // Convert a colour from the current working format to RGB10 format. - - /* Pattern */ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); @@ -36,7 +34,7 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); /* Chroma */ -void nv3_render_chroma_test(nv3_grobj_t grobj); +bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color); /* Blit */ void nv3_render_blit_screen2screen(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index aa59f7e69..d4ceb5cf4 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -202,6 +202,7 @@ extern const device_config_t nv3_config[]; #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_DEBUG_0 0x1084 // Bus Control Debug #define NV3_PBUS_INTR 0x1100 // Bus Control - Interrupt Status #define NV3_PBUS_INTR_EN 0x1140 // Bus Control - Interrupt Enable @@ -375,6 +376,7 @@ extern const device_config_t nv3_config[]; #define NV3_PFB_BOOT_RAM_EXTENSION_8MB 0x1 #define NV3_PFB_DELAY 0x100044 +#define NV3_PFB_DEBUG_0 0x100080 // Debug register for pfb #define NV3_PFB_GREEN_0 0x1000C0 #define NV3_PFB_CONFIG_0 0x100200 // Framebuffer interface config register 0 @@ -569,10 +571,10 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, Y=30:16, X=10:0 #define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, Y=30:16, X=10:0 #define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_PATTERN_COLOR_0_RGB 0x400600 -#define NV3_PGRAPH_PATTERN_COLOR_0_ALPHA 0x400604 -#define NV3_PGRAPH_PATTERN_COLOR_1_RGB 0x400608 -#define NV3_PGRAPH_PATTERN_COLOR_1_ALPHA 0x40060C // pattern color +#define NV3_PGRAPH_PATTERN_COLOR_0_RGB 0x400600 +#define NV3_PGRAPH_PATTERN_COLOR_0_ALPHA 0x400604 +#define NV3_PGRAPH_PATTERN_COLOR_1_RGB 0x400608 +#define NV3_PGRAPH_PATTERN_COLOR_1_ALPHA 0x40060C // pattern color #define NV3_PGRAPH_PATTERN_BITMAP_HIGH 0x400610 // pattern bitmap [31:0] #define NV3_PGRAPH_PATTERN_BITMAP_LOW 0x400614 // pattern bitmap [63:32] #define NV3_PGRAPH_PATTERN_SHAPE 0x400618 @@ -581,6 +583,7 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_CHROMA_KEY 0x40062C #define NV3_PGRAPH_BETA 0x400640 // Beta factor (30:23 fractional, 22:0 before fraction) #define NV3_PGRAPH_DMA 0x400680 +#define NV3_PGRAPH_INSTANCE 0x400688 // Current instance (?) // Current notification object for pgraph #define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH @@ -905,6 +908,7 @@ typedef struct nv3_straps_s typedef struct nv3_pfb_s { uint32_t boot; + uint32_t debug_0; // debug stuff uint32_t config_0; // Framebuffer width, etc. uint32_t config_1; uint32_t green; @@ -960,6 +964,7 @@ typedef struct nv3_pbus_rma_s // Bus Configuration typedef struct nv3_pbus_s { + uint32_t debug_0; uint32_t interrupt_status; // Interrupt status uint32_t interrupt_enable; // Interrupt enable nv3_pbus_rma_t rma; @@ -1207,6 +1212,7 @@ typedef struct nv3_pgraph_s nv3_pgraph_status_t status; // Current status of the 3D engine. uint32_t trapped_address; uint32_t trapped_data; + uint32_t instance; // no idea what this is but possibly an object context uint32_t trapped_instance; /* This area is used for holding universal representations of the U* registers, which are actually mapped into MMIO */ diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 8f304fdac..fcf31132f 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -39,14 +39,13 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Check for any rectangle point or size method. */ if (method_id >= NV3_RECTANGLE_START && method_id <= NV3_RECTANGLE_END) { - uint32_t index = (method_id - NV3_RECTANGLE_START) / 8; + uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; // If the size is submitted, render it. if (method_id & 0x04) { nv3->pgraph.rectangle.size[index].w = param & 0xFFFF; - nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; - + nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; nv_log("Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h, nv3->pgraph.rectangle.color); @@ -57,7 +56,6 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.rectangle.position[index].x = param & 0xFFFF; nv3->pgraph.rectangle.position[index].y = (param >> 16) & 0xFFFF; - nv_log("Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); } diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 2b14ec2ff..9fbc92006 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -32,9 +32,43 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + /* Type A: Unclipped Rectangle */ + + /* NOTE: This method is used by the GDI driver as part of the notification engine. */ + case NV3_W95TXT_A_COLOR: + nv3->pgraph.win95_gdi_text.color_a = param; + break; default: + /* These are the same things as rectangles */ + if (method_id >= NV3_W95TXT_A_RECT_START && method_id <= NV3_W95TXT_A_RECT_END) + { + uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; + + // If the size is submitted, render it. + if (method_id & 0x04) + { + nv3->pgraph.win95_gdi_text.rect_a_size[index].w = param & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_size[index].h = (param >> 16) & 0xFFFF; + + nv_log("Rect GDI-A%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, + nv3->pgraph.win95_gdi_text.rect_a_size[index].h, nv3->pgraph.win95_gdi_text.color_a); + + nv3_render_rect(nv3->pgraph.win95_gdi_text.rect_a_position[index], + nv3->pgraph.win95_gdi_text.rect_a_size[index], nv3->pgraph.win95_gdi_text.color_a, grobj); + } + else // position + { + nv3->pgraph.win95_gdi_text.rect_a_position[index].x = param & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_position[index].y = (param >> 16) & 0xFFFF; + + nv_log("Rect GDI-A%d Position=%d,%d\n", index, + nv3->pgraph.win95_gdi_text.rect_a_position[index].x, nv3->pgraph.win95_gdi_text.rect_a_position[index].y); + } + return; + } + nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; + break; } } \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index dc865243e..8dee40a21 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -76,7 +76,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* the reverse order is due to the endianness */ switch (nv3->nvbase.svga.bpp) { - // 4pixels packed into one + // 4pixels packed into one param case 8: //pixel3 @@ -101,7 +101,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3_class_011_check_line_bounds(); break; - //2pixels packed into 1 + //2pixels packed into one param case 16: pixel1 = (param) & 0xFFFF; nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); @@ -114,7 +114,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3_class_011_check_line_bounds(); break; - // just one + // just one pixel in 32bpp case 32: pixel0 = param; nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index c7ffd5f44..7ef08e665 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -155,10 +155,28 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co return packed_color; } -/* Runs the chroma key test */ -void nv3_render_chroma_test(nv3_grobj_t grobj) +/* Runs the chroma key/color key test */ +bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color) { + bool chroma_enabled = ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_CHROMA_KEY) & 0x01); + if (!chroma_enabled) + return true; + + bool alpha = ((nv3->pgraph.chroma_key >> 31) & 0x01); + + if (!alpha) + return true; + + /* this is dumb but i'm lazy, if it kills perf, fix it later - we need to do some format shuffling */ + nv3_grobj_t grobj_fake = {0}; + grobj_fake.grobj_0 = 0x02; /* we don't care about any other bits */ + + nv3_color_expanded_t chroma_expanded = nv3_render_expand_color(grobj_fake, nv3->pgraph.chroma_key); + + uint32_t chroma_downconverted = nv3_render_downconvert_color(grobj, chroma_expanded); + + return !(chroma_downconverted == color); } /* Convert expanded colour format to chroma key format */ @@ -295,6 +313,8 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob } /* TODO: Chroma Key, Pattern, Plane Mask...*/ + if (!nv3_render_chroma_test(grobj, color)) + return; uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index ca631edfc..46d829fb2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -33,6 +33,7 @@ // Putting this in pbus because imo it makes the most sense (related to memory access/memory interface) nv_register_t pbus_registers[] = { + { NV3_PBUS_DEBUG_0, "PBUS - Debug Register", NULL, NULL}, { 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 @@ -65,6 +66,9 @@ uint32_t nv3_pbus_read(uint32_t address) { switch (reg->address) { + case NV3_PBUS_DEBUG_0: + ret = nv3->pbus.debug_0; + break; case NV3_PBUS_INTR: ret = nv3->pbus.interrupt_status; break; @@ -108,6 +112,9 @@ void nv3_pbus_write(uint32_t address, uint32_t value) { switch (reg->address) { + case NV3_PBUS_DEBUG_0: + nv3->pbus.debug_0 = value; + break; // Interrupt registers // Interrupt state: // Bit 0 - PCI Bus Error diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 160cbff08..644d4090d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -35,6 +35,7 @@ void nv3_pfb_config0_write(uint32_t val); nv_register_t pfb_registers[] = { { NV3_PFB_BOOT, "PFB Boot Config", NULL, NULL}, { NV3_PFB_DELAY, "PFB Delay", NULL, NULL}, + { NV3_PFB_DEBUG_0, "PFB Debug", NULL, NULL }, { NV3_PFB_GREEN_0, "PFB Green / Power Saving", NULL, NULL,}, { NV3_PFB_CONFIG_0, "PFB Framebuffer Config 0", nv3_pfb_config0_read, nv3_pfb_config0_write }, { NV3_PFB_CONFIG_1, "PFB Framebuffer Config 1", NULL, NULL }, @@ -85,6 +86,9 @@ uint32_t nv3_pfb_read(uint32_t address) case NV3_PFB_BOOT: ret = nv3->pfb.boot; break; + case NV3_PFB_DEBUG_0: + ret = nv3->pfb.debug_0; + break; // Config 0 has a read/write function case NV3_PFB_CONFIG_1: ret = nv3->pfb.config_1; @@ -136,6 +140,10 @@ void nv3_pfb_write(uint32_t address, uint32_t value) { switch (reg->address) { + // Boot is read only + case NV3_PFB_DEBUG_0: + nv3->pfb.debug_0 = value; + break; // Config 0 has a read/write function case NV3_PFB_CONFIG_1: // Config Register 1 nv3->pfb.config_1 = value; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 251ec7a5f..ece125b0f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -240,6 +240,7 @@ uint32_t nv3_pgraph_read(uint32_t address) case NV3_PGRAPH_CLIP_MISC: ret = *(uint32_t*)&nv3->pgraph.clip_misc_settings; break; + // Overall Status case NV3_PGRAPH_STATUS: ret = *(uint32_t*)&nv3->pgraph.status; @@ -251,6 +252,9 @@ uint32_t nv3_pgraph_read(uint32_t address) case NV3_PGRAPH_TRAPPED_DATA: ret = nv3->pgraph.trapped_data; break; + case NV3_PGRAPH_INSTANCE: + ret = nv3->pgraph.instance; + break; case NV3_PGRAPH_TRAPPED_INSTANCE: ret = nv3->pgraph.trapped_instance; break; @@ -454,9 +458,13 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) case NV3_PGRAPH_TRAPPED_DATA: nv3->pgraph.trapped_data = value; break; + case NV3_PGRAPH_INSTANCE: + nv3->pgraph.instance = value; + break; case NV3_PGRAPH_TRAPPED_INSTANCE: nv3->pgraph.trapped_instance = value; break; + } } } From c3ebf327d90d1cf3d86ec8d35f9816b3f2113c76 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 13:17:22 +0000 Subject: [PATCH 139/274] partially working text acceleration, looks inverted(??) --- doc/nvidia_notes/status.xlsx | Bin 15398 -> 15459 bytes src/include/86box/nv/render/vid_nv3_render.h | 5 +- src/include/86box/nv/vid_nv3.h | 1 + .../classes/nv3_class_00c_win95_gdi_text.c | 47 ++++++++++++- .../nv/nv3/render/nv3_render_primitives.c | 63 ++++++++++++++++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 3 +- src/video/nv/nv3/subsystems/nv3_pramin.c | 4 +- 7 files changed, 118 insertions(+), 5 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 7d58d5250c8be03718dd86b5bfe94561227001a6..f36ba573a387bc65b2c1cd642c0d77d58c5ef604 100644 GIT binary patch delta 4045 zcmZ8kWmFUj)82)pk&?~@M1-YTln#+@q(i!60YO;6U8F&JX_oGiR&)gfE+O3^(y1to zz!Ll3_x||qd%pQGXU@#?W8$2dXDWg=gBsTfN#H9?w$}Inz&YwCAs6y58G`%ZFiq1G zDzEb88h6ZcmU@N*+Llga{;@`lJMNkdl{U2_@2^k-A_@r&b>11dygBWBmmo-&cKlFu zGRJ|rmGiUh=tI%2Rl&LmXjzZW8_3GUU!f&aH=)Plm8WbB$&fLQdjMBQ`%KNYCNC|+ zDdu7El_^4#Ijb47?pox+LNgsLLvxwYY{qYCUEBF=i5(y(JxBN2z67`bn1<=JDw`sx zz7Y@G>{!n_;eJBh&ir=ZT^n(OWtdr0e03%J4Ah;*#+zB_s0Q!D#o&`ac%8tT6nx4+ zNs>(6q#7n_tCKcqeAjD5>|e;O1>z2p?mH+Uhw_Q1(g*F`qd}x5sOu}gq&-1{?ckLI z6g2g-jYjzhaeq|7@8#&*Q;FKWTcuBg-b)axeQo0k1H0;za6XF%JG3vZk~(v!tn&iu z!i}GtEq`ehPV(!dS5=yCubp8dhKI!r5r+?+xA72j*VEG7Gx6_dA&?F|-q(|ucNo{C zAbsM!*Tr_9mXr*6M$p-FAH~?PC1FxN8OJZ`a(sdTU1Sy6_h;Fq9N#vtIWBsdcONPD z(q#;%Y9G7l+=T0kPOdDi-M!gZ$6f0Ko>NoWe>Uj}!)2W|q@-AWjyek?4zsuE$f80* z;jXcfBwTMUX(81xdyHRe{lwbOhMnm*n{Ju2YtCmlMf7LO{=lg{3%MADZ6FQc~1n0rX1m% z4<4O8d?#_b<9(oa3NN^_XvM}S;QCc2pM(dshe!<5kOlCHz$00%qu+@O6c1rPfBV7^ z?{Dixi4nTPZ_y@o$0Nawlz?+U0Kh#|7daOSJ1G%S_V^aIgM&*#5 z-|oHaAb3HkVbSC$+v$)yoK!#@)e&Wvt4dmxu;8|LLt?Mwn|zBg`E#H>y+M!Od4<{M zw)0PpZ8MtcBG=b!eN^x`zbUld=fDB&tCU3F06ILga=t_uYiQXeiYIb#=Hv%=vMabq zZEigMRDgS^jTR*fVWPi`)|8(!l>LZoOnYL6Ra#gkphsj0zG@cwQRwN*v1^Q0bxSif zc$~j839Y3!xXbRlxGJ+S`EeqgKkgpKLh+oMOh<#k_c&XZFK?U>c$@Cya~Mi<*8bR; z1y9e+C4rcT^qD^4y6c}Kf^_~LVkEhDa{3X;>aXlNFUD_Xi`HS8GZRPoC*wPMM|4{i z=Rvj)CCyV*4Z4ihK<+eJ$!Quzq@`4E^h%s0t*ZDCtiP1zItATa9q)}bI4T-&y&7Y= z-KR~kp{{ha8c0d_BO%4^f*W>LCc*()zECXUkzTRF1XTXsqm(45FXT)}F`*zYPc+Ih zIZuQtY87&>V1}S1#3OmeS?fGqb!~Ev{gusW)}8%rOve~^on(hH8q#W|0Xsw=qU|V7 zD^c!)z=VuXg08fY>5344z9lau4q= zEM(?K8CyyS`@D)|T#u0&<%t^tXxT5~&_rI|>|+HO ze+LNTF0k_fd9DImcEQ$i@)(7#w!^TO1Y7T4Jwk(g<43WrBSWX8bzibef~i9GpFSb( zh8RrYytBm+%)f@FW(h_&z*^IeX*#L)%ZqB1wU2F=ErR8F>|yyPT`}0bMCl_RXMC90 zSYzd;K8nbIM)z|)gsQ%AWf%eg0C2c}RC+pck|G>T8*S5f4EQlA;I(CPs>1jY?j(qw zT?(X-h!=PD-DE_B-Y6r=nPTh?X6vNka;wezFTw^#RCEZ{Fouqs2@w{Kfo6~n898kC zo?XvZd*ithRPG$B>*Nb?k}&z05Syn{r@z=dZ0qvr(T|jh0QICi3@D=z+}g5~ZHCNS zG+*NKwB|L7JFlnu8qa(B+Crg)X1GpAa{pnsW*tnhwlN^jmM7DExUo&6k!IgAZ;+&w z#KeA)E!HuLOUc7-C+dg^02v+ku(jJs3>E#Ndi%OVVB~Y2`bv7dQjtTUU&v@wdYXCm z_O{@#0pof4G91M3QAPGAUpD#BrRe!=a$k1ZvHBl zki}3_>?3(rO4&zA{4$M;a_%J}{U#pMH2kgkL+Z#152cP4aSh_B%F>q#g-PS|Zg}HW z(5#KJjl&Q+Me48FZ@+46+szsAO)sUa&tFl3up@c;RjF3I1}_)4DYSB`YNT64xiP_a?|YROjQ` zA@Q_qEVIhQ?y221aj=q)$4iKjU$ni;e@rxhuTxzuLRo9Ft-8hK-VeVV6WnR&J8md02T@E=BYL{6J zsOsFGP<1aQ_t7W3)F*V!1j=UuEu6x7#Q+feNT;wWa;hv~BWNweDJ-Awh*_sg;5)(` zdfV)ZbwYNZ42N$Yo^62{!~*%N#~Ef0ezm`k_<)?H1eg7>9JyTaVj1g;BojFJZa-rF zFoXN!WGSMQs;0DvffbktXI`6};ls(eU1mRLSrO{W1K)=V#kNTXUYtN9JMH zYB~@DAB*xJf^M*h&fjeq8w{n8eU0PL8grJ)KJp+(v{=m&a$^kTvZpX1b+!N zu|&c?(U|;-?627@cnjW>@+OHc%@c0)@@S7zP&BrCz#Hx{#gf0~t&sNChMifE&P8`Y zt;#j51tjml#hqKMo3kYzDT75l?G+vV)>5n#hn9&iaFGoU5qi48tkV6AMK~^uK8xdH z?6y@eN_peGrOt!*^A*9kC`^@ZoThTdv-`-@<5&8>XYZyvS}+ECKE5F8@~`~n4p2rf zEydmhEklUBb;y+=P{_8=f?SBa%yI}f8oVc7u$6A3LL~7sb3Z@;QhY%`jmp5(*{N)W z5f^J0E6=D;bGxY+XyEgctHW7&iTyrfo%saf#VZYCVyi2HoBO_Z!ab2pJ-JtB5lEmv zyHK>{&kXliQVSPG-=Ufx74Q5D7Lz`y&8kUwu374qAxE<$sOPuQg4Os7(H%@WcTBDK z^s_j@74WCy_}7ikYj9El*1+p(nR+&SD^G;4@`rZ>v#S!b{jypVEI( z=?e4G5q%s=Nhkj1r8=*7#=GnQwT@ni%|lNjNvQAfHo_ikDgd5|u62~IkQ;8S&&hAQ2^5Vg9a>hfkU zz}6r*G1k6{=3J87%-P1$7#M*S9KusRMN&Y#Orz!etN(Y1Wmvlx5I-zccz@K?tIf1nE(;(p0Sf9j*ot0C2+t0I2^L1HcnUb;-y9!%?>~+=MX6|Hl3U DM$~nn delta 3945 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.win95_gdi_text.color_a = param; break; + /* Type B and C not implemented YET, as they are not used by NT GDI driver */ + case NV3_W95TXT_D_CLIP_TOPLEFT: + nv3->pgraph.win95_gdi_text.clip_d.left = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_d.top = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_D_CLIP_BOTTOMRIGHT: + nv3->pgraph.win95_gdi_text.clip_d.right = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_d.bottom = ((param >> 16) & 0xFFFF); + /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ + break; + case NV3_W95TXT_D_CLIP_COLOR: + nv3->pgraph.win95_gdi_text.color1_d = param; + break; + case NV3_W95TXT_D_CLIP_SIZE_IN: + nv3->pgraph.win95_gdi_text.size_in_d.w = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_in_d.h = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_D_CLIP_SIZE_OUT: + nv3->pgraph.win95_gdi_text.size_out_d.w = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_out_d.h = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_D_CLIP_POSITION: + nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); + + nv3->pgraph.win95_gdi_text_current_position = nv3->pgraph.win95_gdi_text.point_d; + break; default: - /* These are the same things as rectangles */ + /* Type A submission: these are the same things as rectangles */ if (method_id >= NV3_W95TXT_A_RECT_START && method_id <= NV3_W95TXT_A_RECT_END) { uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; @@ -66,6 +93,24 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } return; } + else if (method_id >= NV3_W95TXT_D_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_D_CLIP_CLIPRECT_END) + { + /* lol */ + uint32_t index = (method_id - NV3_W95TXT_D_CLIP_CLIPRECT_START) >> 3; + + nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; + + /* Mammoth logger! */ + nv_log("Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", + index, param, nv3->pgraph.win95_gdi_text.size_in_d.w, nv3->pgraph.win95_gdi_text.size_in_d.h, + nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h, + nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y, + nv3->pgraph.win95_gdi_text.color1_d, + nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, nv3->pgraph.win95_gdi_text.clip_d.top, nv3->pgraph.win95_gdi_text.clip_d.bottom); + + nv3_render_gdi_type_d(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); + + } nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index f4bfdebf4..d8dfe7ba9 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -36,6 +36,7 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co for (int32_t y = position.y; y < (position.y + size.h); y++) { current_pos.y = y; + for (int32_t x = position.x; x < (position.x + size.w); x++) { current_pos.x = x; @@ -43,4 +44,66 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co nv3_render_write_pixel(current_pos, color, grobj); } } +} + +void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) +{ + // reset when a position is submitted + nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; + + // is this clip or point? + + uint16_t end_x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; + uint16_t end_y = nv3->pgraph.win95_gdi_text.point_d.y + nv3->pgraph.win95_gdi_text.size_in_d.h; + + /* set up our packed pixels */ + uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; + + /* Go through the bitmap that was sent, bit by bit. */ + for (int32_t bit_num = 0; bit_num <= 31; bit_num++) + { + bool bit = (param >> bit_num) & 0x01; + //bool bit = true; // debug test + + nv3->pgraph.win95_gdi_text_current_position.x++; + + /* let's hope NV never overflow the y */ + if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) + { + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; + nv3->pgraph.win95_gdi_text_current_position.y++; + } + + /* check if we are in the clipping rectangle */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) + { + return; + } + + // if it's 0 we don't need to do anything + if (!bit) + continue; + else + { + switch (nv3->nvbase.svga.bpp) + { + case 8: + uint32_t final_color8 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color8, grobj); + break; + case 16: + uint32_t final_color16 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color16, grobj); + break; + case 32: + uint32_t final_color32 = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color32, grobj); + break; + } + } + + } } \ 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 index 088ddf1ba..b782f2b56 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -939,9 +939,10 @@ void nv3_pfifo_cache1_pull() nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; nv3_debug_ramin_print_context_info(current_param, context_structure); + #endif nv3_pgraph_submit(current_param, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); - #endif + //Todo: finish it } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 7a5ff749b..b4858acca 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -477,7 +477,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u else { nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; + nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; } // It's an error but it isn't lol @@ -504,7 +504,7 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte { #ifndef RELEASE_BUILD nv_log("Found object:\n"); - nv_log("Name: 0x%04x\n", name); + nv_log("Param: 0x%04x\n", name); nv_log("Context:\n"); nv_log("DMA Channel %d (0-7 valid)\n", context.channel); From 94cd84363cb505fe10cf532eeb34f93011fb1640 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 13:34:22 +0000 Subject: [PATCH 140/274] Much improved text rendering. It was being read horizontally backwards and the position was being incremented too early. --- .../classes/nv3_class_00c_win95_gdi_text.c | 3 +- .../nv/nv3/render/nv3_render_primitives.c | 42 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index a834da989..9c1546cf1 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -63,7 +63,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); - nv3->pgraph.win95_gdi_text_current_position = nv3->pgraph.win95_gdi_text.point_d; + nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w); + nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); break; default: /* Type A submission: these are the same things as rectangles */ diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index d8dfe7ba9..8a9ca8598 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -65,28 +65,9 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) bool bit = (param >> bit_num) & 0x01; //bool bit = true; // debug test - nv3->pgraph.win95_gdi_text_current_position.x++; - /* let's hope NV never overflow the y */ - if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) - { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; - nv3->pgraph.win95_gdi_text_current_position.y++; - } - - /* check if we are in the clipping rectangle */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) - { - return; - } - - // if it's 0 we don't need to do anything - if (!bit) - continue; - else + // if it's a 0 bit we don't need to do anything + if (bit) { switch (nv3->nvbase.svga.bpp) { @@ -105,5 +86,24 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) } } + /* increment the position - the bitmap is stored horizontally backward */ + nv3->pgraph.win95_gdi_text_current_position.x--; + + /* let's hope NV never overflow the y */ + if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) + { + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; + nv3->pgraph.win95_gdi_text_current_position.y++; + } + + /* check if we are in the clipping rectangle */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) + { + return; + } + } } \ No newline at end of file From 997acb990e9393a137e6d85737b0470ef9c0b54e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 19:56:54 +0000 Subject: [PATCH 141/274] Most text seems to be fine now. --- .../classes/nv3_class_00c_win95_gdi_text.c | 18 ++- .../nv/nv3/render/nv3_render_primitives.c | 142 ++++++++++++------ 2 files changed, 112 insertions(+), 48 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 9c1546cf1..dcad8b088 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -63,8 +63,20 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); - nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w); - nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); + /* small case*/ + if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) + { + nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w); + nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); + } + /* large case: draw (7-0) (15-8)*/ + else + { + uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_d.w >> 1) ; + nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + large_start); + nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); + } + break; default: /* Type A submission: these are the same things as rectangles */ @@ -110,7 +122,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, nv3->pgraph.win95_gdi_text.clip_d.top, nv3->pgraph.win95_gdi_text.clip_d.bottom); nv3_render_gdi_type_d(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); - + return; } nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 8a9ca8598..91c0728c7 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -46,48 +46,50 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co } } -void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) +void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) { - // reset when a position is submitted - nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; + uint16_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_out_d.w; + uint16_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + nv3->pgraph.win95_gdi_text.size_out_d.h; - // is this clip or point? + /* they send more data than they need */ + if (nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) + bit = false; - uint16_t end_x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; - uint16_t end_y = nv3->pgraph.win95_gdi_text.point_d.y + nv3->pgraph.win95_gdi_text.size_in_d.h; - - /* set up our packed pixels */ - uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; - - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 31; bit_num++) + // if it's a 0 bit we don't need to do anything + if (bit) { - bool bit = (param >> bit_num) & 0x01; - //bool bit = true; // debug test - - - // if it's a 0 bit we don't need to do anything - if (bit) + switch (nv3->nvbase.svga.bpp) { - switch (nv3->nvbase.svga.bpp) - { - case 8: - uint32_t final_color8 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color8, grobj); - break; - case 16: - uint32_t final_color16 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color16, grobj); - break; - case 32: - uint32_t final_color32 = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color32, grobj); - break; - } + case 8: + uint32_t final_color8 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color8, grobj); + break; + case 16: + uint32_t final_color16 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color16, grobj); + break; + case 32: + uint32_t final_color32 = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color32, grobj); + break; } + } - /* increment the position - the bitmap is stored horizontally backward */ - nv3->pgraph.win95_gdi_text_current_position.x--; + /* increment the position - the bitmap is stored horizontally backward */ + nv3->pgraph.win95_gdi_text_current_position.x--; + + if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) + { + if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) + { + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; + nv3->pgraph.win95_gdi_text_current_position.y++; + } + } + /* dumb hack but it never seems to be any other sizes. draw lines (7-0) and then (15-8) */ + else + { + uint16_t midpoint_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_in_d.w << 1); /* let's hope NV never overflow the y */ if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) @@ -95,15 +97,65 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; nv3->pgraph.win95_gdi_text_current_position.y++; } - - /* check if we are in the clipping rectangle */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) - { - return; - } - } + + /* check if we are in the clipping rectangle */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) + { + return; + } + +} + +void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) +{ + // reset when a position is submitted + nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; + + // is this clip or point? + + + /* set up our packed pixels */ + uint32_t pixel0 = param & 0xFF; + uint32_t pixel1 = (param >> 8) & 0xFF; + uint32_t pixel2 = (param >> 16) & 0xFF; + uint32_t pixel3 = (param >> 24) & 0xFF; + + /* small stuff */ + + /* Go through the bitmap that was sent, bit by bit. */ + for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + { + bool bit = (pixel0 >> bit_num) & 0x01; + + nv3_render_text_1bpp(bit, grobj); + } + + /* Go through the bitmap that was sent, bit by bit. */ + for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + { + bool bit = (pixel1 >> bit_num) & 0x01; + + nv3_render_text_1bpp(bit, grobj); + } + + /* Go through the bitmap that was sent, bit by bit. */ + for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + { + bool bit = (pixel2 >> bit_num) & 0x01; + + nv3_render_text_1bpp(bit, grobj); + } + + /* Go through the bitmap that was sent, bit by bit. */ + for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + { + bool bit = (pixel3 >> bit_num) & 0x01; + + nv3_render_text_1bpp(bit, grobj); + } + } \ No newline at end of file From 0b458b8ee4c1ac4b9820384d67de24c69bd91188 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 20:02:36 +0000 Subject: [PATCH 142/274] Simplify the code. --- .../nv/nv3/render/nv3_render_primitives.c | 38 +------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 91c0728c7..52473f2d4 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -117,45 +117,11 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) // is this clip or point? - - /* set up our packed pixels */ - uint32_t pixel0 = param & 0xFF; - uint32_t pixel1 = (param >> 8) & 0xFF; - uint32_t pixel2 = (param >> 16) & 0xFF; - uint32_t pixel3 = (param >> 24) & 0xFF; - - /* small stuff */ - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + for (int32_t bit_num = 0; bit_num <= 31; bit_num++) { - bool bit = (pixel0 >> bit_num) & 0x01; + bool bit = (param >> bit_num) & 0x01; nv3_render_text_1bpp(bit, grobj); } - - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 7; bit_num++) - { - bool bit = (pixel1 >> bit_num) & 0x01; - - nv3_render_text_1bpp(bit, grobj); - } - - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 7; bit_num++) - { - bool bit = (pixel2 >> bit_num) & 0x01; - - nv3_render_text_1bpp(bit, grobj); - } - - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 7; bit_num++) - { - bool bit = (pixel3 >> bit_num) & 0x01; - - nv3_render_text_1bpp(bit, grobj); - } - } \ No newline at end of file From fcf01cb487b976a8abc681c5a07559711ff67cd7 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 24 Mar 2025 20:07:34 +0000 Subject: [PATCH 143/274] Remove even more redundant GDI-D code --- doc/nvidia_notes/status.xlsx | Bin 15459 -> 15504 bytes .../nv/nv3/render/nv3_render_primitives.c | 21 +++--------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index f36ba573a387bc65b2c1cd642c0d77d58c5ef604..0b76f7036b788e2a4943c59f11902094edbef605 100644 GIT binary patch delta 4997 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 BLsI|% delta 4971 zcmZ8lbyO5g*IzoNLE5E-rCAi|?v^fL>275KSsq$SVCj_ZTu^WU36bs==@bN{VW|&& z-}61^`{s{1_ssp>-s7vD?(9+n~z=DaJz@yR96Nbm{K_gEd0UT@~sj5x&u4=jyw1YxR6W9hb zW>ZF;m2-2j21<&@4Mlp>Df^R17B-8Kg0*>(EgRHr4cF zPRFFpv@yLkO}MqD!AAAb<)w^MU?;LyZnV6=E6_h(^gTaEZ%4aHz#x7jf|sfpUqM4^ zcHAV2;czXD`U=~a!)?RsyaQuDkvn#g`J}%6yH6Dp_NAXo1cFs-*>`0P#+=w~B9r?a zL_RF#~MPYFj-=0BYp5L8XP%*8`5{y#D>dKO+op{z~>tsmT2J7o|f>e^{6Tl{&TmTFAU@q z_yng|u=Xx;ICbrYutD+oJ1znHqhloIMcN0*w=}DSquYiRn|T-G&fjv~lqrLW>PLr<4X%GcmJ%d-d}42UXl_+S`E5_@6%3d6B0~Y!%u^8gCVcl(nw%ns6$lLc=w68 z-1rYI36Of**s&cvBjt0Vv+ei~bM9jWimg>Gn1>>!z-sf$O|>myABca#7QEQqPpS&Y zabR?$x;_uRGV%|+pq>eGiu8StwD;k7C0y_-?v{9lA}}qT%B!=y^iwS|=m-a-hsPX4 zPpnSl{p#sLatHO*%Qj;2o{y(QwU&y=6{p;HSM;{!>ox~n0~|q4(3o;-g%@Ggq^R5{ zu%e7l9wib|0rGW+)zXbm;!0{GO~dtZTLumOsFVXlK8}1Io_j6W;JduA zq}^Ys`DTe!RK!z0FT3DRlbWzy^pfM48)w1>%EbEgl)Xmy zWXtV9>jav6WzvX>j=le;F#bHmuQ@MW$<=-p^y`^!v8D<{7 zshpV17Wt@^G%|R-=_Qd{Q`&uVcGZUR&lzeo3Cfd9J)MlnF@VKN<-7{Im9R)X*<447 zx(3bhGi-U=!R(rER8T!h@k>N*LbdLZ(6{Ux`|(wiokb40K5i`>-75ksEi5~pv|14e z4p>eR;qU#`4v#Yn?-|1GY13v@^jF7eH=7^NZTNBGUUXZYTh=Jk+6x_u38qg@_uF%K zTM}8;^sT3VdV55;RsAj7ieHJ}kah8--DUCq@Lbb+gg^b0WlhxA*4cC+!Yzh$5Oan- zV!Vvt(LuY<9xt;Z>+D%1P9FlGujn-z#|2XjXH+_2C+d+Co6 z@?{>l+KTIx`{_W0h;TvQ`~W(x3go{vfQ)>itX#=S4`V z`$t9;(djSaKQeuUp%lw1XrSLL8%j^06ua@!RDTs;VgQ3Go;uO&;(O0B5qi7c4@?e~<@ol{RdlNO z^xj1G>jL^O0(#+yRu43SS?co_g}WJ>d@g7KGt}n_g%xRo;31BgN&)p^r<8SfYf@8% zA^w23hQx?%y=tygkrAV)Y9~Uv(gRRp8Yd*<2ZB$jbXPmyN^dS-49@;p>h*Oh!(lm> ze0=0nNK_OJ`)Pk~^2_kDF@7IHQUT_c|0-y^4d7g2@%9{$*R+#*dv|$RFaVs7-WmUj z-knQ>EQx*|otL>Q_3GQD^%1 z5e@zO>v$`;-hf&o8PGc7Fx!ggq!jMdhZdXina6=5ELj@l!@1xp1G_VNFQ{6z?%Gec zC@V8+`Ezn@J@QH?E2kQbUPjiLz(j0zm>N-l-Q)T}%;5;8QXIENru~ozA8WQgs(`Un~YMcW(ECK|^JRb|VxR=8h z*jY`I_>u3oGH&s$^o*3tVn?M_poVe0=>h z8`Cp-7}Yp5aDrdekzU|W60j%x9JdpsGjZ>hE*M1nYhYp;Yj_Q)HffWjkqB9wS0b)_ zX1QqMFUbaR&oTHCiQ0)1{q13g;Vw8*SGukZ$I&6vY^?^7RM#yHf&c)({r%r5IT&} z2<{tF(7;wg>e{TwhML&CF#@%TNuWA+th)Lk`ZERa%o%!zaSLEPAK?fugNtpukKnBP z#-;4Y&SCWKteT`hn&afHi4=ltuu4N@kG)g1 z%AKdO&Ns`FE!B9iu1TeiY|k{S53do=05Z=IWfRUU<7~AR{+k8>8Xk4FwAzXb6zEXA zecQ%8)S9KdlpHPd!8*??U^qNE$vAy;lV?!szLewN(>HJ`tHZ_VE%1dqu@lc9o<#bi zzcKh3B|ADy$1RkfP`Wn6D_91-(Oe`E-rGy z779CWoG^<1%V~2Z5z$oU7IhE6{c^(&YQ0xWEcVtT96lezrC@&MH%w>Y zL#E0~LcRK{ZKUGV!qD-abws|SmMK{SESb0%!z9i>RSSG7C9Va&im$?1&Ou^ zHF(>`wt6oslBc6O$4iXrRd0!l#bxn~P5WD13-L7HLC{o#F%9wN_qiO=Q;J@8rG{AA zMVlRK5q%bzdsr284Dl_n44&le!ZThd_fV7}uOi37$UZ04?E9`ckure`Qs@Mf%Y{)i z#KWd0$WGQA!Ck64B)hhvPSZX!f_`yzvNIobM;e^LT_!pn5&^2mal8oJYMLcRsn8FG z2fvojX_~&K(liaQ<)nSxI?i6*rvc;gDXK{*bK6y_!K#rG?j#mN%fAK!m`KU_X$0+V zM}JFUI8$3J!EmZJetMP;Zg=wi=YHT|dOL&^jVT5e_bmH11d&HijSgG<ZoMDJyKiY8H7P6DFzyd`R1L`c zFj(nE@1#(<8k#R{FZtX)q+D}@XvH_p+fi9gXd;39b)v$?`RnE5o)-$5VI2;MQ)B|o zt^kkdCmoq{{)ss-xz|fcP`vp73(nb|D{O%V^_`a$f!3w}RfWc*_TyKud`1hm~j6KWmm{3HNcSSdbKIxdKc~ z5)l=cF^RU22RZ25?&zV<17WIpO;}210*Pm)3;!rT*~|M@pr0iV5dBU4)ot`*=sDV- zhz`dSBSFXmbsyoalOCVwVgUd|@H`$~n69c|3t?J9QHuyhk;+9e%L0yeJ)2VU+ zX;_J~OdCQ-1$Uyf&|4}mew4})eY6amwpO%u7(gja+MgcNud-=11zWBNey0AFI}u%r zOKmCzF}VyAoB0mNp+pr43bTtkt1x+^u0VwyES4nGN@+X)&|vG&bd&|J_+qS zn_k4y%ZLEKFOkm64w~TZqzLbX&FPdwS9pj_EOaK!`9lmHd#Y2nc`|do3*dWs^l<)Y z_1vD=gD`&vw+v?AU5ndLYwMaAwTs2dHQnpApvd}o_Z#9fg-Ym3)vW1KmuOA103mka z`M0@Q>0Ra3Z1AX%_EU%+gnA<3l6y-*I@>T)*`VUtk5 zvWdq31_hh$>E(b_u1c45bqnV)SRSc8y?BO1Qe1Eb66E1`yzAlL`Nz*KP@`q#5O-2M zidJE4`^4%R*I&lN*&C$i6#)_3GWdq8<^Htn`^$i_M5wG}$`1`C5dCMt;q+A|t!Cr9 zFGVO%4Dsvh_AH;7u@{eFaj0K8%Tbw;zGbiZMv=2)uFF3NmG%#s$lWRf#z(Y%ZDL8* zJCqnvdzCgsB8royyrO$u7V6Bql~qFv*AAk78hci&8JBdP1S*fK7lV#@o5hxW%i8zG z6rBnQJ+!eewXq#i(IivROl*U@1py$8Fx%iVLXtGJI?R;-+u$4+=Wkk#FWf(ZjKQ}J z4k+8suNB@0Ln=8b9+d2?|L4r_;SMqK%+Ggq_) z^|pM-0`WMykG1c^nvk!M#JrFSn}HSDG=;r`41u!jXE(r0KfH{oAIDV8%zVn?s+)

    &uPDyu1cHL2VW=2q`rZs$+jNS`(R1`=TE z{+Z0+SJ=0T_1qZX&I>oZh{7zsI#=iBa4BhhD^`vW=Lx!;6*sA*m{*LnJe2mDb4q0n z!3a!AYi5?re9epvp)fHNT(((0NZt9*dHz@xCixi%Mtg=$r2*%A< z&zGB0o@8-U&{4tQB2Ra&m5yvxWUfbN|z(VHt;IhpHfCgxJwwI$ELp6yeJnH^fDwvIH(8;0`i- zswZX;X2o^W$t72L2NhG>n*airs1cke3*HB_-fo%P#QHzqhCB;T;>5=Y5l=9CCwriK znq7NKw?Q*lWLcb)P(Vd!Vwd(9ua?u0ma(ZqW&u5RTX{m;qSV3NRYTxeATg!TKicmk7K@wC$qKBQ z*R1D-Pl+Gq_ksdSF@xAScicEtGa z=Mw0JE}A(!=mihvpH3KF`+^D`K?rA}q=f%?K}r8_vjPB6KU4<)ju6tnAZAfY`hUx} z{|~yQcmUDDT}8RjIcfglYaZ}_D~gA5F97fVc0j_&D)c^nh diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 52473f2d4..49684edc3 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -78,25 +78,10 @@ void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) /* increment the position - the bitmap is stored horizontally backward */ nv3->pgraph.win95_gdi_text_current_position.x--; - if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) + if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) { - if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) - { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; - nv3->pgraph.win95_gdi_text_current_position.y++; - } - } - /* dumb hack but it never seems to be any other sizes. draw lines (7-0) and then (15-8) */ - else - { - uint16_t midpoint_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_in_d.w << 1); - - /* let's hope NV never overflow the y */ - if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) - { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; - nv3->pgraph.win95_gdi_text_current_position.y++; - } + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; + nv3->pgraph.win95_gdi_text_current_position.y++; } /* check if we are in the clipping rectangle */ From a9250c5d1965e6f9aa262f739f243234099ba8be Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 25 Mar 2025 02:19:21 +0000 Subject: [PATCH 144/274] Legitimately I don't even know how i mostly fixed it. What's going on? --- doc/nvidia_notes/status.xlsx | Bin 15504 -> 15607 bytes src/include/86box/nv/render/vid_nv3_render.h | 3 +- src/include/86box/nv/vid_nv3.h | 2 +- .../classes/nv3_class_00c_win95_gdi_text.c | 57 +++++++++- .../nv/nv3/render/nv3_render_primitives.c | 97 ++++++++++++++++-- 5 files changed, 144 insertions(+), 15 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 0b76f7036b788e2a4943c59f11902094edbef605..e23fe6c7248b0bed60a1a932a22f5bf1d20bfb23 100644 GIT binary patch delta 3349 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 delta 3282 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 QuQij;JR%01H~;_u0IUrzl>h($ diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index fd76042d6..65541fa0d 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -40,4 +40,5 @@ bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color); void nv3_render_blit_screen2screen(nv3_grobj_t grobj); /* GDI */ -void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param); \ No newline at end of file +void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param); /* GDI Type-D: Clipped 1bpp text */ +void nv3_render_gdi_type_e(nv3_grobj_t grobj, uint32_t param); /* GDI Type-E: Clipped 1bpp two-colour text */ \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 78cc3953a..e70a0bec6 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1233,7 +1233,7 @@ typedef struct nv3_pgraph_s struct nv3_object_class_00E scaled_image_from_memory; struct nv3_object_class_010 blit; struct nv3_object_class_011 image; - nv3_position_16_t image_current_position; /* This is here so we can hold the current state of the image */ + nv3_position_16_t image_current_position; /* This is here so we can hold the current state of the image */ struct nv3_object_class_012 bitmap; struct nv3_object_class_014 transfer2memory; struct nv3_object_class_015 stretched_image_from_cpu; diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index dcad8b088..8d126e6cc 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -66,17 +66,52 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* small case*/ if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) { - nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w); + nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_in_d.w - 1)); nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); } /* large case: draw (7-0) (15-8)*/ else { - uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_d.w >> 1) ; + uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_d.w >> 1) - 1; nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + large_start); nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); } + break; + /* Type E: Two-colour 1bpp */ + case NV3_W95TXT_E_CLIP_TOPLEFT: + nv3->pgraph.win95_gdi_text.clip_e.left = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_e.top = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_E_CLIP_BOTTOMRIGHT: + nv3->pgraph.win95_gdi_text.clip_e.right = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_e.bottom = ((param >> 16) & 0xFFFF); + /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ + break; + case NV3_W95TXT_E_CLIP_COLOR_0: + nv3->pgraph.win95_gdi_text.color0_e = param; + break; + case NV3_W95TXT_E_CLIP_COLOR_1: + nv3->pgraph.win95_gdi_text.color1_e = param; + break; + case NV3_W95TXT_E_CLIP_SIZE_IN: + nv3->pgraph.win95_gdi_text.size_in_e.w = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_in_e.h = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_E_CLIP_SIZE_OUT: + nv3->pgraph.win95_gdi_text.size_out_e.w = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_out_e.h = ((param >> 16) & 0xFFFF); + break; + case NV3_W95TXT_E_CLIP_POSITION: + nv3->pgraph.win95_gdi_text.point_e.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.point_e.y = ((param >> 16) & 0xFFFF); + + uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_e.w >> 1) - 1; + + //nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_e.x + large_start); + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; + nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_e.y); + break; default: /* Type A submission: these are the same things as rectangles */ @@ -124,6 +159,24 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3_render_gdi_type_d(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); return; } + else if (method_id >= NV3_W95TXT_E_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_E_CLIP_CLIPRECT_END) + { + /* lol */ + uint32_t index = (method_id - NV3_W95TXT_E_CLIP_CLIPRECT_START) >> 3; + + nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; + + /* Mammoth logger! */ + nv_log("Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", + index, param, nv3->pgraph.win95_gdi_text.size_in_e.w, nv3->pgraph.win95_gdi_text.size_in_e.h, + nv3->pgraph.win95_gdi_text.size_out_e.w, nv3->pgraph.win95_gdi_text.size_out_e.h, + nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y, + nv3->pgraph.win95_gdi_text.color1_e, + nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.top, nv3->pgraph.win95_gdi_text.clip_e.bottom); + + nv3_render_gdi_type_e(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); + return; + } nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 49684edc3..6190040a5 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -55,32 +55,37 @@ void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) if (nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) bit = false; + uint32_t final_color; + // if it's a 0 bit we don't need to do anything if (bit) { switch (nv3->nvbase.svga.bpp) { case 8: - uint32_t final_color8 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color8, grobj); + final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ break; case 16: - uint32_t final_color16 = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color16, grobj); + final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ break; case 32: - uint32_t final_color32 = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color32, grobj); + final_color = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ break; } } + /* in type d colour0 is always transparent */ + if (bit) + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color, grobj); + /* increment the position - the bitmap is stored horizontally backward */ nv3->pgraph.win95_gdi_text_current_position.x--; - if (nv3->pgraph.win95_gdi_text_current_position.x <= nv3->pgraph.win95_gdi_text.point_d.x) + /* check if we need to go down a line */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.point_d.x) { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w; + nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w - 1); + nv3->pgraph.win95_gdi_text_current_position.y++; } @@ -92,7 +97,6 @@ void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) { return; } - } void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) @@ -100,8 +104,6 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) // reset when a position is submitted nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; - // is this clip or point? - /* Go through the bitmap that was sent, bit by bit. */ for (int32_t bit_num = 0; bit_num <= 31; bit_num++) { @@ -109,4 +111,77 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) nv3_render_text_1bpp(bit, grobj); } +} + +/* 2-colour 1bpp color-expanded text from [7-0] */ +void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) +{ + for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + { + bool bit = (byte >> bit_num) & 0x01; + + uint16_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_out_e.w; + uint16_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + nv3->pgraph.win95_gdi_text.size_out_e.h; + + /* they send more data than they need */ + if (nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) + bit = false; + + // if it's a 0 bit we don't need to do anything + + uint32_t final_color; + + switch (nv3->nvbase.svga.bpp) + { + case 8: + final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFF); /* do we need to add anything? mul blend perhaps? */ + break; + case 16: + final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFFFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ + break; + case 32: + final_color = (bit) ? nv3->pgraph.win95_gdi_text.color1_e : nv3->pgraph.win95_gdi_text.color0_e; /* do we need to add anything? mul blend perhaps? */ + break; + } + + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color, grobj); + + /* increment the position - the bitmap is stored horizontally backward */ + nv3->pgraph.win95_gdi_text_current_position.x--; + + /* see if we need to go to the next line */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.point_e.x) + { + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_in_e.w - 1); + nv3->pgraph.win95_gdi_text_current_position.y++; + } + + /* check if we are in the clipping rectangle */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_e.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_e.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_e.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_e.bottom) + { + return; + } + } + + +} + +void nv3_render_gdi_type_e(nv3_grobj_t grobj, uint32_t param) +{ + // reset when a position is submitted + nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; + + /* we have to interpret every bit in reverse order but in the right bit order */ + uint8_t byte0 = ((param & 0xFF000000) >> 24); + uint8_t byte1 = ((param & 0xFF0000) >> 16); + uint8_t byte2 = ((param & 0xFF00) >> 8); + uint8_t byte3 = (param & 0xFF); + + nv3_render_text_1bpp_2color(byte0, grobj); + nv3_render_text_1bpp_2color(byte1, grobj); + nv3_render_text_1bpp_2color(byte2, grobj); + nv3_render_text_1bpp_2color(byte3, grobj); } \ No newline at end of file From 8b6185eeb19021b37c70f8d6c9c4560d0c10d8f6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 25 Mar 2025 20:38:57 +0000 Subject: [PATCH 145/274] make 15bpp actualyl 15bpp --- src/video/nv/nv3/classes/nv3_class_011_image.c | 1 + src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/render/nv3_render_blit.c | 1 + src/video/nv/nv3/render/nv3_render_core.c | 2 ++ src/video/nv/nv3/render/nv3_render_primitives.c | 12 +++++------- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 8dee40a21..b05013f5a 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -102,6 +102,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; //2pixels packed into one param + case 15: case 16: pixel1 = (param) & 0xFFFF; nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index b1d96f8d9..5016329a9 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -519,7 +519,7 @@ void nv3_recalc_timings(svga_t* svga) } else { - svga->bpp = 16; // HACK: DO NOT change this + svga->bpp = 15; // HACK: DO NOT change this svga->lowres = 0; svga->render = svga_render_15bpp_highres; } diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index ce836520b..539871884 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -56,6 +56,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) } } break; + case 15: case 16: //16bpp for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) { diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 7ef08e665..d1563cd76 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -227,6 +227,7 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro { case 8: break; + case 15: case 16: vram_x = position.x << 1; break; @@ -375,6 +376,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; break; + case 15: case 16: uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 1; diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 6190040a5..4695d05b7 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -65,6 +65,7 @@ void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) case 8: final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ break; + case 15: case 16: final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ break; @@ -114,7 +115,7 @@ void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) } /* 2-colour 1bpp color-expanded text from [7-0] */ -void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) +void nv3_render_text_1bpp_2color(uint32_t byte, nv3_grobj_t grobj) { for (int32_t bit_num = 0; bit_num <= 7; bit_num++) { @@ -123,10 +124,6 @@ void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) uint16_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_out_e.w; uint16_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + nv3->pgraph.win95_gdi_text.size_out_e.h; - /* they send more data than they need */ - if (nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) - bit = false; - // if it's a 0 bit we don't need to do anything uint32_t final_color; @@ -136,6 +133,7 @@ void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) case 8: final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFF); /* do we need to add anything? mul blend perhaps? */ break; + case 15: case 16: final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFFFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ break; @@ -143,7 +141,7 @@ void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) final_color = (bit) ? nv3->pgraph.win95_gdi_text.color1_e : nv3->pgraph.win95_gdi_text.color0_e; /* do we need to add anything? mul blend perhaps? */ break; } - + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color, grobj); /* increment the position - the bitmap is stored horizontally backward */ @@ -152,7 +150,7 @@ void nv3_render_text_1bpp_2color(uint8_t byte, nv3_grobj_t grobj) /* see if we need to go to the next line */ if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.point_e.x) { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_in_e.w - 1); + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.w - 1); nv3->pgraph.win95_gdi_text_current_position.y++; } From 64b16a43a1f9b4faf87c72387e69b9b854a098e8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 26 Mar 2025 01:43:46 +0000 Subject: [PATCH 146/274] Fix 32bpp bsod by actually creating framebuffer mappings. Don't remap memory if it's 0 (prevents trashing the low/real-mode memory map), general code simplification (some things were enabled 3 times what --- doc/nvidia_notes/status.xlsx | Bin 15607 -> 15648 bytes src/include/86box/nv/vid_nv.h | 1 + src/include/86box/nv/vid_nv3.h | 18 +- .../classes/nv3_class_01c_image_in_memory.c | 2 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 8 +- src/video/nv/nv3/nv3_core.c | 163 ++++++++++++------ src/video/nv/nv3/nv3_core_config.c | 8 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 6 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- 9 files changed, 140 insertions(+), 68 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index e23fe6c7248b0bed60a1a932a22f5bf1d20bfb23..c050432b51bc3ae5023ac7204abcea8ad1e97540 100644 GIT binary patch delta 3970 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 BRw@7h delta 3932 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 U9$Opgraph.bpixel[src_buffer_id] = ((real_format & 0x03) | NV3_BPIXEL_FORMAT_IS_VALID); + nv3->pgraph.bpixel[src_buffer_id] = (real_format | NV3_BPIXEL_FORMAT_IS_VALID); nv_log("Image in Memory BUF%d COLOR_FORMAT=0x%04x", src_buffer_id, param); diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 9985845cf..01f0e90c7 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -166,10 +166,10 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t switch (info_notification_target) { case NV3_NOTIFICATION_TARGET_NVM: - svga_writel_linear(final_address, (notify.nanoseconds & 0xFFFFFFFF), nv3); - svga_writel_linear(final_address + 4, (notify.nanoseconds >> 32), nv3); - svga_writel_linear(final_address + 8, notify.info32, nv3); - svga_writel_linear(final_address + 0x0C, (notify.info16 | notify.status), nv3); + svga_writel_linear(final_address, (notify.nanoseconds & 0xFFFFFFFF), &nv3->nvbase.svga); + svga_writel_linear(final_address + 4, (notify.nanoseconds >> 32), &nv3->nvbase.svga); + svga_writel_linear(final_address + 8, notify.info32, &nv3->nvbase.svga); + svga_writel_linear(final_address + 0x0C, (notify.info16 | notify.status), &nv3->nvbase.svga); break; case NV3_NOTIFICATION_TARGET_PCI: case NV3_NOTIFICATION_TARGET_AGP: diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 5016329a9..09d8e093a 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -129,9 +129,6 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) ret = nv3_mmio_arbitrate_read(addr); - // This may get around the riva shredding its own cache - //nv3_pfifo_cache0_pull(); - //nv3_pfifo_cache1_pull(); return ret; @@ -261,7 +258,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) // 66Mhz FSB capable case PCI_REG_COMMAND_L: - ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L]; // we actually respond to the fucking + ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L] ; // we actually respond to the fucking break; case PCI_REG_COMMAND_H: @@ -396,6 +393,11 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) // actually update the mappings nv3_update_mappings(); break; + case PCI_REG_COMMAND_H: + nv3->pci_config.pci_regs[PCI_REG_COMMAND_H] = 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); @@ -736,13 +738,57 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) } +/* DFB, sets up a dumb framebuffer */ +uint8_t nv3_dfb_read8(uint32_t addr, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + return nv3->nvbase.svga.vram[addr]; +} + +uint16_t nv3_dfb_read16(uint32_t addr, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + return (nv3->nvbase.svga.vram[addr + 1] << 8) | nv3->nvbase.svga.vram[addr]; +} + +uint32_t nv3_dfb_read32(uint32_t addr, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + return (nv3->nvbase.svga.vram[addr + 3] << 24) | (nv3->nvbase.svga.vram[addr + 2] << 16) + + (nv3->nvbase.svga.vram[addr + 1] << 8) | nv3->nvbase.svga.vram[addr]; +} + +void nv3_dfb_write8(uint32_t addr, uint8_t val, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + nv3->nvbase.svga.vram[addr] = val; +} + +void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; + nv3->nvbase.svga.vram[addr] = (val) & 0xFF; +} + +void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv) +{ + addr &= (nv3->nvbase.svga.vram_mask); + nv3->nvbase.svga.vram[addr + 3] = (val >> 24) & 0xFF; + nv3->nvbase.svga.vram[addr + 2] = (val >> 16) & 0xFF; + nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; + nv3->nvbase.svga.vram[addr] = (val) & 0xFF; +} + +/* Cursor shit */ void nv3_draw_cursor(svga_t* svga, int32_t drawline) { // sanity check if (!nv3) return; - // this is a 2kb bitmap in vram...somewhere... + // On windows, this shows up using NV_IMAGE_IN_MEMORY. + // Do we need to emulate it? nv_log("nv3_draw_cursor drawline=0x%04x", drawline); } @@ -829,25 +875,26 @@ void nv3_init_mappings_svga() { nv_log("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); + mem_mapping_add(&nv3->nvbase.framebuffer_mapping, 0, 0, + nv3_dfb_read8, + nv3_dfb_read16, + nv3_dfb_read32, + nv3_dfb_write8, + nv3_dfb_write16, + nv3_dfb_write32, + nv3->nvbase.svga.vram, 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); + mem_mapping_add(&nv3->nvbase.framebuffer_mapping_mirror, 0, 0, + nv3_dfb_read8, + nv3_dfb_read16, + nv3_dfb_read32, + nv3_dfb_write8, + nv3_dfb_write16, + nv3_dfb_write32, + nv3->nvbase.svga.vram, 0, &nv3->nvbase.svga); io_sethandler(0x03c0, 0x0020, nv3_svga_in, NULL, NULL, @@ -885,6 +932,12 @@ void nv3_update_mappings() nv3_svga_out, NULL, NULL, nv3); + if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND]) & PCI_COMMAND_MEM) + { + nv_log("The memory was turned off, not much is going to happen.\n"); + return; + } + // turn off bar0 and bar1 by defualt mem_mapping_disable(&nv3->nvbase.mmio_mapping); mem_mapping_disable(&nv3->nvbase.framebuffer_mapping); @@ -892,50 +945,56 @@ void nv3_update_mappings() 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("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 + // Setup BAR0 (MMIO) nv_log("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 (nv3->nvbase.bar0_mmio_base) + 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("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); + /* TODO: 2MB */ + // 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 + NV3_LFB_RAMIN_MIRROR_START, NV3_LFB_MAPPING_SIZE); - mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_2NDHALF_START, NV3_LFB_MAPPING_SIZE); - mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); - // TODO: RAMIN and its mirror + // 8MB VRAM memory map: + // LFB_BASE->LFB_BASE+VRAM_SIZE=LFB + // What is in 800000-c00000? + // LFB_BASE+0xC00000 = RAMIN + + if (nv3->nvbase.bar1_lfb_base) + { + if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_4MB) + { + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping, nv3->nvbase.bar1_lfb_base, NV3_VRAM_SIZE_4MB); + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_MIRROR_START, NV3_LFB_MAPPING_SIZE); + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_MIRROR_START, NV3_VRAM_SIZE_4MB); + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); + } + else if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_8MB) + { + // we don't need this one in the case of 8mb, because regular mapping is 8mb + mem_mapping_disable(&nv3->nvbase.ramin_mapping_mirror); + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping, nv3->nvbase.bar1_lfb_base, NV3_VRAM_SIZE_8MB); + mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_MIRROR_START, NV3_LFB_MAPPING_SIZE); + mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); + } + else + { + fatal("NV3-2MB not implemented yet (It never existed anyway)"); + } + } // Did we change the banked SVGA mode? switch (nv3->nvbase.svga.gdcreg[0x06] & 0x0c) @@ -977,7 +1036,7 @@ void* nv3_init(const device_t *info) // Allows nv_log to be used for multiple nvidia devices nv_log_set_device(nv3->nvbase.log); #endif - nv_log("initialising core\n"); + nv_log("Initialising core\n"); // Figure out which vbios the user selected const char* vbios_id = device_get_config_bios("vbios"); @@ -1001,7 +1060,7 @@ void* nv3_init(const device_t *info) nv_log("Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); // set the vram amount and gpu revision - uint32_t vram_amount = device_get_config_int("vram_size"); + nv3->nvbase.vram_amount = device_get_config_int("vram_size"); nv3->nvbase.gpu_revision = device_get_config_int("chip_revision"); // set up the bus and start setting up SVGA core @@ -1011,7 +1070,7 @@ void* nv3_init(const device_t *info) 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_amount, + svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); } else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) @@ -1020,7 +1079,7 @@ void* nv3_init(const device_t *info) 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_amount, + svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); } diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index e8beb6df7..256ae9dd9 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -140,23 +140,23 @@ const device_config_t nv3_config[] = .name = "vram_size", .description = "VRAM Size", .type = CONFIG_SELECTION, - .default_int = VRAM_SIZE_4MB, + .default_int = NV3_VRAM_SIZE_4MB, .selection = { #ifndef RELEASE_BUILD // This never existed officially but was planned. Debug only { .description = "2 MB (Never officially sold)", - .value = VRAM_SIZE_2MB, + .value = NV3_VRAM_SIZE_2MB, }, #endif { .description = "4 MB", - .value = VRAM_SIZE_4MB, + .value = NV3_VRAM_SIZE_4MB, }, { .description = "8 MB", - .value = VRAM_SIZE_8MB, + .value = NV3_VRAM_SIZE_8MB, }, } diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 644d4090d..0dae5ab39 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -57,9 +57,13 @@ void nv3_pfb_init() | (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) ); + if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_4MB) + nv3->pfb.boot |= (NV3_PFB_BOOT_RAM_AMOUNT_4MB << NV3_PFB_BOOT_RAM_AMOUNT); + else + nv3->pfb.boot |= (NV3_PFB_BOOT_RAM_AMOUNT_8MB << NV3_PFB_BOOT_RAM_AMOUNT); + nv_log("Done\n"); } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index b782f2b56..ef6af049e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -218,7 +218,7 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = nv3->pfifo.cache1_settings.dma_state; break; case NV3_PFIFO_CACHE1_DMA_CONFIG_1: - ret = nv3->pfifo.cache1_settings.dma_length & (VRAM_SIZE_8MB) - 4; //MAX vram size + ret = nv3->pfifo.cache1_settings.dma_length & (NV3_VRAM_SIZE_8MB) - 4; //MAX vram size break; case NV3_PFIFO_CACHE1_DMA_CONFIG_2: ret = nv3->pfifo.cache1_settings.dma_address; From ce852caee13e778c34681b314757df3fcfde3353 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 26 Mar 2025 16:22:06 +0000 Subject: [PATCH 147/274] Clip images. Gate some ridiculous logging beyond ENABLE_NV_LOG_ULTRA. 2MB was real for at least one card, as well. --- .../nv3_class_005_clipping_rectangle.c | 4 +-- .../nv/nv3/classes/nv3_class_011_image.c | 17 ++++++----- src/video/nv/nv3/nv3_core.c | 29 +++++++++++++++---- src/video/nv/nv3/nv3_core_config.c | 5 ++-- src/video/nv/nv3/render/nv3_render_core.c | 5 ++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 7 +++++ 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 10136feac..5ad674b44 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -34,12 +34,12 @@ void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_CLIP_POSITION: nv3->pgraph.clip_start.x = (param >> 16) & 0xFFFF; nv3->pgraph.clip_start.y = (param) & 0xFFFF; - nv_log("Clip Position: %d,%d", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + nv_log("Clip Position: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); break; case NV3_CLIP_SIZE: nv3->pgraph.clip_size.x = (param >> 16) & 0xFFFF; nv3->pgraph.clip_size.y = (param) & 0xFFFF; - nv_log("Clip Size: %d,%d", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + nv_log("Clip Size: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); break; default: nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index b05013f5a..c6aa1aceb 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -72,6 +72,9 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; + /* Some extra data is sent as padding, we need to clip it off using size_out */ + + uint16_t clip_x = nv3->pgraph.image_current_position.x + nv3->pgraph.image.size.w; /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ /* the reverse order is due to the endianness */ switch (nv3->nvbase.svga.bpp) @@ -81,22 +84,22 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ //pixel3 pixel3 = param & 0xFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel2 = (param >> 8) & 0xFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel1 = (param >> 16) & 0xFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel0 = (param >> 24) & 0xFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); @@ -105,12 +108,12 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case 15: case 16: pixel1 = (param) & 0xFFFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel0 = (param >> 16) & 0xFFFF; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); @@ -118,7 +121,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // just one pixel in 32bpp case 32: pixel0 = param; - nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 09d8e093a..d9ca57ff0 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -68,7 +68,9 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3); - nv_log("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%02x\n", addr, ret); + #ifdef ENABLE_NV_LOG_ULTRA + nv_log("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + #endif return ret; } @@ -94,8 +96,9 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); + #ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - + #endif return ret; } @@ -121,7 +124,9 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - nv_log("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%08x\n", addr, ret); + #ifdef ENABLE_NV_LOG_ULTRA + nv_log("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + #endif return ret; } @@ -146,7 +151,10 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; + #ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + #endif + nv3_svga_out(real_address, val & 0xFF, nv3); return; @@ -172,7 +180,10 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%04x\n", addr, val); + #ifdef ENABLE_NV_LOG_ULTRA + nv_log("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + #endif + nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -199,7 +210,9 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - nv_log("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%08x\n", addr, val); + #ifdef ENABLE_NV_LOG_ULTRA + nv_log("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + #endif nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -992,7 +1005,7 @@ void nv3_update_mappings() } else { - fatal("NV3-2MB not implemented yet (It never existed anyway)"); + fatal("NV3 2MB not implemented yet"); } } @@ -1038,6 +1051,10 @@ void* nv3_init(const device_t *info) #endif nv_log("Initialising core\n"); +#ifdef ENABLE_NV_LOG_ULTRA + nv_log("ULTRA LOGGING enabled"); +#endif + // Figure out which vbios the user selected const char* vbios_id = device_get_config_bios("vbios"); const char* vbios_file = ""; diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index 256ae9dd9..a229ca3c7 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -144,9 +144,10 @@ const device_config_t nv3_config[] = .selection = { #ifndef RELEASE_BUILD - // This never existed officially but was planned. Debug only + // I thought this was never released, but it seems that at least one was released: + // The card was called the "NEC G7AGK" { - .description = "2 MB (Never officially sold)", + .description = "2 MB", .value = NV3_VRAM_SIZE_2MB, }, #endif diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index d1563cd76..7ae00efd6 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -219,6 +219,7 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj) { uint32_t vram_x = position.x; + uint32_t vram_y = position.y; uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; @@ -236,7 +237,7 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro break; } - uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * position.y) + nv3->pgraph.boffset[current_buffer]; + uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * vram_y) + nv3->pgraph.boffset[current_buffer]; pixel_addr_vram &= nv3->nvbase.svga.vram_mask; @@ -313,7 +314,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob return; } - /* TODO: Chroma Key, Pattern, Plane Mask...*/ + /* TODO: Plane Mask...*/ if (!nv3_render_chroma_test(grobj, color)) return; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index ef6af049e..55ec21e19 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -740,7 +740,9 @@ void nv3_pfifo_cache0_pull() nv3->pfifo.cache0_settings.get_address ^= 0x04; #ifndef RELEASE_BUILD + #ifdef ENABLE_NV_LOG_ULTRA nv_log("***** DEBUG: CACHE0 PULLED ****** Contextual information below\n"); + #endif nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; @@ -936,6 +938,11 @@ void nv3_pfifo_cache1_pull() nv3->pfifo.cache1_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; #ifndef RELEASE_BUILD + + #ifdef ENABLE_NV_LOG_ULTRA + nv_log("***** DEBUG: CACHE1 PULLED ****** Contextual information below\n"); + #endif + nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; nv3_debug_ramin_print_context_info(current_param, context_structure); From 2829669740d7e61689317de86fb9735d8f123be1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 16:23:18 +0000 Subject: [PATCH 148/274] Redirect all PRMCIO/PRMVIO writes to SVGA subsystem, not just a few random ones. --- doc/nvidia_notes/status.xlsx | Bin 15648 -> 15813 bytes src/include/86box/nv/vid_nv3.h | 7 ++++--- src/video/nv/nv3/nv3_core.c | 10 +++------- src/video/nv/nv3/subsystems/nv3_pmc.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_ptimer.c | 2 +- src/video/nv/nv3/subsystems/nv3_user.c | 2 +- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index c050432b51bc3ae5023ac7204abcea8ad1e97540..d43765e803cd7105a9c392f5c3b8fe7889659bd2 100644 GIT binary patch delta 6801 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 diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 40aa94ce2..2efca64b3 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 22 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 26 March 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -671,9 +671,10 @@ extern const device_config_t nv3_config[]; #define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers #define NV3_PGRAPH_REAL_END 0x5C1FFF +// PRMCIO is redirected to SVGA subsystem #define NV3_PRMCIO_START 0x601000 -// Following four are CRTC+I2C access registers -// and get redirected to VGA + + #define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO 0x6013B4 // Current CRTC Register Index - Monochrome #define NV3_PRMCIO_CRTC_REGISTER_CUR_MONO 0x6013B5 // Currently Selected CRTC Register - Monochrome #define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR 0x6013D4 // Current CRTC Register Index - Colour diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index d9ca57ff0..f8e98689d 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -41,13 +41,9 @@ void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); bool nv3_is_svga_redirect_address(uint32_t addr) { - return (addr >= NV3_PRMVIO_START - && addr <= NV3_PRMVIO_END - || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_COLOR - || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR - || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_MONO - || addr == NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO - || (addr >= NV3_VGA_DAC_START && addr <= NV3_VGA_DAC_END)); + return (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) // VGA + || (addr >= NV3_PRMCIO_START && addr <= NV3_PRMCIO_END) // CRTC + || (addr >= NV3_VGA_DAC_START && addr <= NV3_VGA_DAC_END); // Legacy RAMDAC support(?) } // All MMIO regs are 32-bit i believe internally diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 24f0ad44a..de1c21f24 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -88,11 +88,11 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) 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 + // PFB interrupt is VBLANK PGRAPH interrupt...what nvidia... if (nv3->pgraph.interrupt_status_0 & (1 << 8) && nv3->pgraph.interrupt_enable_0 & (1 << 8)) new_intr_value |= (NV3_PMC_INTERRUPT_PFB_PENDING << NV3_PMC_INTERRUPT_PFB); - + if (nv3->pgraph.interrupt_status_0 & ~(1 << 8) && nv3->pgraph.interrupt_enable_0 & ~(1 << 8)) // otherwise PGRAPH-0 interurpt new_intr_value |= (NV3_PMC_INTERRUPT_PGRAPH0_PENDING << NV3_PMC_INTERRUPT_PGRAPH0); diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index b5c278387..1e3347beb 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -84,7 +84,7 @@ void nv3_ptimer_tick(double real_time) // Check if the alarm has actually triggered... if (nv3->ptimer.time >= nv3->ptimer.alarm) { - nv_log("PTIMER alarm interrupt fired because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); + nv_log("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); } } diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index a6c9a588c..cb2c96fb4 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -37,7 +37,7 @@ uint32_t nv3_user_read(uint32_t address) //todo: print out the subchannel uint8_t method_offset = (address & 0x1FFC); -#ifndef RELEASE_BUILD +#ifdef ENABLE_NV_LOG_ULTRA uint8_t channel = (address - NV3_USER_START) / 0x10000; uint8_t subchannel = ((address - NV3_USER_START)) / 0x2000 % NV3_DMA_SUBCHANNELS_PER_CHANNEL; From cc68dcecc481632739451fb8cde2de43692a87db Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 16:52:21 +0000 Subject: [PATCH 149/274] general minor code cleanups. fix logs, send vram writes (0x1000000-0x17fffff), if these ever happen, to DFB, and more --- CMakeLists.txt | 7 +++++ src/include/86box/nv/vid_nv3.h | 9 +++---- .../nv3/classes/nv3_class_001_beta_factor.c | 4 ++- src/video/nv/nv3/classes/nv3_class_002_rop.c | 3 ++- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- .../nv/nv3/classes/nv3_class_004_plane_mask.c | 2 +- .../nv3_class_005_clipping_rectangle.c | 6 ++--- .../nv/nv3/classes/nv3_class_006_pattern.c | 2 +- .../nv/nv3/classes/nv3_class_007_rectangle.c | 4 +-- .../nv/nv3/classes/nv3_class_008_point.c | 2 +- src/video/nv/nv3/classes/nv3_class_009_line.c | 2 +- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- .../nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- .../classes/nv3_class_00c_win95_gdi_text.c | 26 ++++++++++++++----- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../nv3_class_00e_scaled_image_from_mem.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- .../nv/nv3/classes/nv3_class_011_image.c | 9 ++++--- .../nv/nv3/classes/nv3_class_012_bitmap.c | 2 +- .../classes/nv3_class_014_transfer2memory.c | 2 +- .../nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv3_class_017_d3d5_tri_zeta_buffer.c | 2 +- .../classes/nv3_class_018_point_zeta_buffer.c | 2 +- .../classes/nv3_class_01c_image_in_memory.c | 9 +++---- src/video/nv/nv3/nv3_core.c | 23 ++++++++-------- src/video/nv/nv3/nv3_core_arbiter.c | 11 +++----- src/video/nv/nv3/subsystems/nv3_pfb.c | 4 +++ src/video/nv/nv3/subsystems/nv3_pramin.c | 3 +-- src/video/nv/nv3/subsystems/nv3_user.c | 5 ++-- src/video/nv/nv_base.c | 8 ++++-- 30 files changed, 93 insertions(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ac7591dd8..1adcf4740 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,12 +137,18 @@ 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) +# Remove when merged, should just be -D option(NV_LOG "NVidia RIVA 128 debug logging" OFF) +option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) if (NV_LOG) add_compile_definitions(ENABLE_NV_LOG) endif() +if (NV_LOG_ULTRA) + add_compile_definitions(ENABLE_NV_LOG_ULTRA) +endif() + if((ARCH STREQUAL "arm64") OR (ARCH STREQUAL "arm")) set(NEW_DYNAREC ON) else() @@ -193,6 +199,7 @@ cmake_dependent_option(PCL "Generic PCL5e Printer" cmake_dependent_option(SIO_DETECT "Super I/O Detection Helper" ON "DEV_BRANCH" OFF) cmake_dependent_option(WACOM "Wacom Input Devices" ON "DEV_BRANCH" OFF) cmake_dependent_option(XL24 "ATI VGA Wonder XL24 (ATI-28800-6)" ON "DEV_BRANCH" OFF) +cmake_dependent_option(NV3 "NVidia RIVA 128/128ZX (NV3/NV3T)" ON "DEV_BRANCH" OFF) # Ditto but for Qt if(QT) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 2efca64b3..ed55e050d 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1454,7 +1454,7 @@ void nv3_speed_changed(void *priv); void nv3_recalc_timings(svga_t* svga); void nv3_force_redraw(void* priv); -// Memory Mapping +/* BAR0 GPU MMIO read */ void nv3_update_mappings(); // Update memory 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 @@ -1544,10 +1544,7 @@ 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 @@ -1641,5 +1638,5 @@ void nv3_ptimer_tick(double real_time); // NV3 PVIDEO void nv3_pvideo_init(); -// NV3 PMEDIA (Mediaport) +// NV3 PME (Mediaport) void nv3_pmedia_init(); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 1450acbeb..694e49085 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -39,9 +39,11 @@ void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ else nv3->pgraph.beta_factor = param & 0x7F800000; + nv_log("Method Execution: Beta Factor = %02x", nv3->pgraph.beta_factor); + break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index 1052da80d..8c3580f4f 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -34,9 +34,10 @@ void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { case NV3_ROP_SET_ROP: nv3->pgraph.rop = param & 0xFF; + nv_log("Method Execution: ROP = %02x\n", nv3->pgraph.rop); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index bf24f420f..54d52e197 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -38,7 +38,7 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 9c9bced95..5789b8e11 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -33,7 +33,7 @@ void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 5ad674b44..7bfbd4dd6 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -34,15 +34,15 @@ void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_CLIP_POSITION: nv3->pgraph.clip_start.x = (param >> 16) & 0xFFFF; nv3->pgraph.clip_start.y = (param) & 0xFFFF; - nv_log("Clip Position: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + nv_log("Method Execution: Clip Position: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); break; case NV3_CLIP_SIZE: nv3->pgraph.clip_size.x = (param >> 16) & 0xFFFF; nv3->pgraph.clip_size.y = (param) & 0xFFFF; - nv_log("Clip Size: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); + nv_log("Method Execution: Clip Size: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index 36ce70e19..c1fba2309 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -72,7 +72,7 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index fcf31132f..24193f8d5 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -47,7 +47,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.rectangle.size[index].w = param & 0xFFFF; nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; - nv_log("Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h, nv3->pgraph.rectangle.color); + nv_log("Method Execution: Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h, nv3->pgraph.rectangle.color); nv3_render_rect(nv3->pgraph.rectangle.position[index], nv3->pgraph.rectangle.size[index], nv3->pgraph.rectangle.color, grobj); } @@ -56,7 +56,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.rectangle.position[index].x = param & 0xFFFF; nv3->pgraph.rectangle.position[index].y = (param >> 16) & 0xFFFF; - nv_log("Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); + nv_log("Method Execution: Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); } return; diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index 4ffffba92..ec40419df 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -33,7 +33,7 @@ void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index edb432884..c0667af47 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -33,7 +33,7 @@ void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index fc7dcc879..839b4768c 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -34,7 +34,7 @@ void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index 08a205ac2..a790eabc3 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -33,7 +33,7 @@ void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 8d126e6cc..38969a5a0 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -37,32 +37,39 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* NOTE: This method is used by the GDI driver as part of the notification engine. */ case NV3_W95TXT_A_COLOR: nv3->pgraph.win95_gdi_text.color_a = param; + nv_log("Method Execution: GDI-A Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; /* Type B and C not implemented YET, as they are not used by NT GDI driver */ case NV3_W95TXT_D_CLIP_TOPLEFT: nv3->pgraph.win95_gdi_text.clip_d.left = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_d.top = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-A Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); break; case NV3_W95TXT_D_CLIP_BOTTOMRIGHT: nv3->pgraph.win95_gdi_text.clip_d.right = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_d.bottom = ((param >> 16) & 0xFFFF); /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ + nv_log("Method Execution: GDI-A Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); break; case NV3_W95TXT_D_CLIP_COLOR: nv3->pgraph.win95_gdi_text.color1_d = param; + nv_log("Method Execution: GDI-D Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; case NV3_W95TXT_D_CLIP_SIZE_IN: nv3->pgraph.win95_gdi_text.size_in_d.w = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.size_in_d.h = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-D Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_d.w, nv3->pgraph.win95_gdi_text.size_in_d.h); break; case NV3_W95TXT_D_CLIP_SIZE_OUT: nv3->pgraph.win95_gdi_text.size_out_d.w = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.size_out_d.h = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-D Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h); break; case NV3_W95TXT_D_CLIP_POSITION: nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); - + nv_log("Method Execution: GDI-D Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y); + /* small case*/ if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) { @@ -90,17 +97,21 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; case NV3_W95TXT_E_CLIP_COLOR_0: nv3->pgraph.win95_gdi_text.color0_e = param; + nv_log("Method Execution: GDI-E Color0 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; case NV3_W95TXT_E_CLIP_COLOR_1: nv3->pgraph.win95_gdi_text.color1_e = param; + nv_log("Method Execution: GDI-E Color1 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; case NV3_W95TXT_E_CLIP_SIZE_IN: nv3->pgraph.win95_gdi_text.size_in_e.w = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.size_in_e.h = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_e.w, nv3->pgraph.win95_gdi_text.size_in_e.h); break; case NV3_W95TXT_E_CLIP_SIZE_OUT: nv3->pgraph.win95_gdi_text.size_out_e.w = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.size_out_e.h = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_e.w, nv3->pgraph.win95_gdi_text.size_out_e.h); break; case NV3_W95TXT_E_CLIP_POSITION: nv3->pgraph.win95_gdi_text.point_e.x = (param & 0xFFFF); @@ -110,8 +121,9 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ //nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_e.x + large_start); nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; - nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_e.y); + nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_e.y; + nv_log("Method Execution: GDI-E Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y); break; default: /* Type A submission: these are the same things as rectangles */ @@ -125,7 +137,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.rect_a_size[index].w = param & 0xFFFF; nv3->pgraph.win95_gdi_text.rect_a_size[index].h = (param >> 16) & 0xFFFF; - nv_log("Rect GDI-A%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, + nv_log("Method Execution: Rect GDI-A%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, nv3->pgraph.win95_gdi_text.rect_a_size[index].h, nv3->pgraph.win95_gdi_text.color_a); nv3_render_rect(nv3->pgraph.win95_gdi_text.rect_a_position[index], @@ -136,7 +148,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.rect_a_position[index].x = param & 0xFFFF; nv3->pgraph.win95_gdi_text.rect_a_position[index].y = (param >> 16) & 0xFFFF; - nv_log("Rect GDI-A%d Position=%d,%d\n", index, + nv_log("Method Execution: Rect GDI-A%d Position=%d,%d\n", index, nv3->pgraph.win95_gdi_text.rect_a_position[index].x, nv3->pgraph.win95_gdi_text.rect_a_position[index].y); } return; @@ -149,7 +161,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; /* Mammoth logger! */ - nv_log("Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", + nv_log("Method Execution: Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", index, param, nv3->pgraph.win95_gdi_text.size_in_d.w, nv3->pgraph.win95_gdi_text.size_in_d.h, nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h, nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y, @@ -167,7 +179,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; /* Mammoth logger! */ - nv_log("Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", + nv_log("Method Execution: Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", index, param, nv3->pgraph.win95_gdi_text.size_in_e.w, nv3->pgraph.win95_gdi_text.size_in_e.h, nv3->pgraph.win95_gdi_text.size_out_e.w, nv3->pgraph.win95_gdi_text.size_out_e.h, nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y, @@ -178,7 +190,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ return; } - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index fa1f08811..06c5d93b6 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -33,7 +33,7 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index c4b68c34f..ea5db21ff 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -33,7 +33,7 @@ void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 0f15e82d6..c07c600ee 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -49,7 +49,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index c6aa1aceb..42574bc30 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -49,17 +49,19 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_IMAGE_START_POSITION: nv3->pgraph.image.point.x = (param & 0xFFFF); nv3->pgraph.image.point.y = (param >> 16); - nv_log("Image Point=%d,%d", nv3->pgraph.image.point.x, nv3->pgraph.image.point.y); + nv_log("Method Execution: Image Point=%d,%d\n", nv3->pgraph.image.point.x, nv3->pgraph.image.point.y); break; /* Seems to allow scaling of the bitblt. */ case NV3_IMAGE_SIZE: nv3->pgraph.image.size.w = (param & 0xFFFF); nv3->pgraph.image.size.h = (param >> 16); + nv_log("Method Execution: Image Size (Clip)=%d,%d\n", nv3->pgraph.image.size.w, nv3->pgraph.image.size.h); break; case NV3_IMAGE_SIZE_IN: nv3->pgraph.image.size_in.w = (param & 0xFFFF); nv3->pgraph.image.size_in.h = (param >> 16); nv3->pgraph.image_current_position = nv3->pgraph.image.point; + nv_log("Method Execution: Image SizeIn=%d,%d\n", nv3->pgraph.image.size_in.w, nv3->pgraph.image.size_in.h); break; default: if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) @@ -67,7 +69,8 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // shift left by 2 because it's 4 bits per si\e.. uint32_t pixel_slot = (method_id - NV3_IMAGE_COLOR_START) >> 2; uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - + nv_log("Method Execution: Pixel%d Colour%08x Format%x\n", pixel_slot, param, (grobj.grobj_0) & 0x07); + /* todo: a lot of stuff */ uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; @@ -130,7 +133,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } else { - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); } diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index bbbb6997d..799673391 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -34,7 +34,7 @@ void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index 47e4b0c32..a27538dcb 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -34,7 +34,7 @@ void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index f1c571943..d029dc080 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -33,7 +33,7 @@ void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index 838c64aa7..c4c7aa0cd 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -33,7 +33,7 @@ void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 904cac757..6a0632377 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -35,7 +35,7 @@ void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 5eee663f3..5d06ed678 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -61,7 +61,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.bpixel[src_buffer_id] = (real_format | NV3_BPIXEL_FORMAT_IS_VALID); - nv_log("Image in Memory BUF%d COLOR_FORMAT=0x%04x", src_buffer_id, param); + nv_log("Method Execution: Image in Memory BUF%d COLOR_FORMAT=0x%04x\n", src_buffer_id, param); break; /* DOn't log invalid */ @@ -70,20 +70,19 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; /* Pitch - length between scanlines */ case NV3_IMAGE_IN_MEMORY_PITCH: - nv3->pgraph.image_in_memory.pitch = param & 0x1FF0; nv3->pgraph.bpitch[src_buffer_id] = param & 0x1FF0; // 12:0 - nv_log("Image in Memory BUF%d PITCH=0x%04x", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); + nv_log("Method Execution: Image in Memory BUF%d PITCH=0x%04x\n", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); break; /* Byte offset in GPU VRAM of top left pixel (22:0) */ case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: nv3->pgraph.boffset[src_buffer_id] = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); - nv_log("Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); + nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index f8e98689d..28c6edab5 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -92,9 +92,9 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); - #ifdef ENABLE_NV_LOG_ULTRA + //#ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - #endif + //#endif return ret; } @@ -120,9 +120,9 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - #ifdef ENABLE_NV_LOG_ULTRA + //#ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - #endif + //#endif return ret; } @@ -147,9 +147,9 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - #ifdef ENABLE_NV_LOG_ULTRA + //#ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - #endif + //#endif nv3_svga_out(real_address, val & 0xFF, nv3); @@ -176,9 +176,9 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - #ifdef ENABLE_NV_LOG_ULTRA + //#ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - #endif + //#endif nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -206,9 +206,9 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - #ifdef ENABLE_NV_LOG_ULTRA + //#ifdef ENABLE_NV_LOG_ULTRA nv_log("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - #endif + //#endif nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -842,7 +842,7 @@ void nv3_prom_write(uint32_t address, uint32_t value) // Initialise the MMIO mappings void nv3_init_mappings_mmio() { - nv_log("Initialising 32MB MMIO area\n"); + nv_log("Initialising MMIO mapping\n"); // 0x0 - 1000000: regs // 0x1000000-2000000 @@ -1107,6 +1107,7 @@ void* nv3_init(const device_t *info) 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("Initialising GPU core...\n"); nv3_pextdev_init(); // Initialise Straps diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index e5e7ced6b..2bb3ef8b4 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -100,12 +100,12 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t 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); + ret = nv3_dfb_read32(address & nv3->nvbase.svga.vram_mask, &nv3->nvbase.svga); else if (address >= NV3_USER_START && address <= NV3_USER_END) ret = nv3_user_read(address); else { - nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); + warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); return 0x00; } @@ -161,13 +161,13 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t 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); + nv3_dfb_write32(address, value, &nv3->nvbase.svga); else if (address >= NV3_USER_START && address <= NV3_USER_END) nv3_user_write(address, value); //RAMIN is its own thing else { - nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); + warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); return; } } @@ -192,6 +192,3 @@ 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) {}; \ 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 index 0dae5ab39..0be3d14c8 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -187,6 +187,9 @@ void nv3_pfb_config0_write(uint32_t val) uint32_t new_pfb_vtotal = new_pfb_htotal * (3.0/4.0); uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; + + // This doesn't actually seem very useful + #ifdef ENABLE_NV_LOG_ULTRA nv_log("Framebuffer Config Change\n"); nv_log("Horizontal Size=%d pixels\n", new_pfb_htotal); nv_log("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); @@ -197,5 +200,6 @@ void nv3_pfb_config0_write(uint32_t val) nv_log("Bit Depth=16bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_32BPP) nv_log("Bit Depth=32bpp\n"); + #endif } \ 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 index b4858acca..2771d5d16 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -508,8 +508,7 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte nv_log("Context:\n"); nv_log("DMA Channel %d (0-7 valid)\n", context.channel); - nv_log("Class ID: as represented in ramin=%04x, Stupid 5 bit version (the actual id)=0x%04x (%s)\n", context.class_id, - context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); + nv_log("Class ID: =0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); nv_log("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); #endif diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index cb2c96fb4..99091b5b7 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -37,12 +37,11 @@ uint32_t nv3_user_read(uint32_t address) //todo: print out the subchannel uint8_t method_offset = (address & 0x1FFC); -#ifdef ENABLE_NV_LOG_ULTRA uint8_t channel = (address - NV3_USER_START) / 0x10000; uint8_t subchannel = ((address - NV3_USER_START)) / 0x2000 % NV3_DMA_SUBCHANNELS_PER_CHANNEL; nv_log("User Submission Area PIO Channel %d.%d method_offset=0x%04x\n", channel, subchannel, method_offset); -#endif + // 0x10 is free CACHE1 object // TODO: THERE ARE OTHER STUFF! @@ -55,7 +54,7 @@ uint32_t nv3_user_read(uint32_t address) } - nv_log("IT'S NOT IMPLEMENTED!!!!\n", method_offset); + nv_log("IT'S NOT IMPLEMENTED!!!! offset=0x%04x\n", method_offset); return 0x00; diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index 420d209b4..185c46183 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -72,8 +72,12 @@ void nv_log(const char *fmt, ...) } } #else -void -nv_log(const char *fmt, ...) +void nv_log(const char *fmt, ...) +{ + +} + +void nv_log_set_device(void* device) { } From c28a596a342b629b080a6b4d1ab32154732ddb41 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 17:04:41 +0000 Subject: [PATCH 150/274] Make unimplemented methods a warning rather than log them, and add logging for each method executed. --- src/include/86box/nv/classes/vid_nv3_classes.h | 1 + src/video/nv/nv3/classes/nv3_class_001_beta_factor.c | 2 +- src/video/nv/nv3/classes/nv3_class_002_rop.c | 2 +- src/video/nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- src/video/nv/nv3/classes/nv3_class_004_plane_mask.c | 2 +- .../nv/nv3/classes/nv3_class_005_clipping_rectangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_006_pattern.c | 7 +++++-- src/video/nv/nv3/classes/nv3_class_007_rectangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_008_point.c | 2 +- src/video/nv/nv3/classes/nv3_class_009_line.c | 2 +- src/video/nv/nv3/classes/nv3_class_00a_lin.c | 2 +- src/video/nv/nv3/classes/nv3_class_00b_triangle.c | 2 +- src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 2 +- .../nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 8 ++++++-- src/video/nv/nv3/classes/nv3_class_011_image.c | 2 +- src/video/nv/nv3/classes/nv3_class_012_bitmap.c | 2 +- src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c | 2 +- .../nv3/classes/nv3_class_015_stretched_image_from_cpu.c | 2 +- .../nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c | 2 +- .../nv/nv3/classes/nv3_class_018_point_zeta_buffer.c | 2 +- src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c | 2 +- src/video/nv/nv3/subsystems/nv3_ptimer.c | 4 +--- 24 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 309a1c5aa..0503c0c66 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -98,6 +98,7 @@ typedef enum nv3_pgraph_class_e #define NV3_PATTERN_SHAPE_1X64 2 #define NV3_PATTERN_SHAPE_LAST_VALID NV3_PATTERN_SHAPE_1X64 +#define NV3_PATTERN_UNUSED_DRIVER_BUG 0x030C #define NV3_PATTERN_COLOR0 0x0310 #define NV3_PATTERN_COLOR1 0x0314 #define NV3_PATTERN_BITMAP_HIGH 0x0318 diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c index 694e49085..4520a941f 100644 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c @@ -43,7 +43,7 @@ void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c index 8c3580f4f..56b135888 100644 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ b/src/video/nv/nv3/classes/nv3_class_002_rop.c @@ -37,7 +37,7 @@ void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: ROP = %02x\n", nv3->pgraph.rop); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 54d52e197..fae6771e2 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -38,7 +38,7 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c index 5789b8e11..d93de9bc1 100644 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c @@ -33,7 +33,7 @@ void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c index 7bfbd4dd6..c7ec3ae98 100644 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c @@ -42,7 +42,7 @@ void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: Clip Size: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index c1fba2309..f007d5fb8 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -54,6 +54,10 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ */ nv3->pgraph.pattern_shape = param & 0x03; + break; + /* Seems to be "SetPatternSelect" on Riva TNT and later, but possibly called by accident on Riva 128. There is no hardware equivalent for this. So let's just suppress + the warnings. */ + case NV3_PATTERN_UNUSED_DRIVER_BUG: break; case NV3_PATTERN_COLOR0: nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(grobj, param); @@ -69,10 +73,9 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; case NV3_PATTERN_BITMAP_LOW: nv3->pgraph.pattern_bitmap |= param; - break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 24193f8d5..6dfdf281e 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -62,7 +62,7 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ return; } - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c index ec40419df..41002f745 100644 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ b/src/video/nv/nv3/classes/nv3_class_008_point.c @@ -33,7 +33,7 @@ void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c index c0667af47..f149bb8b3 100644 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ b/src/video/nv/nv3/classes/nv3_class_009_line.c @@ -33,7 +33,7 @@ void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c index 839b4768c..473aaa214 100644 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ b/src/video/nv/nv3/classes/nv3_class_00a_lin.c @@ -34,7 +34,7 @@ void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c index a790eabc3..d08b7a72e 100644 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c @@ -33,7 +33,7 @@ void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 38969a5a0..ea886476d 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -190,7 +190,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ return; } - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 06c5d93b6..fd8f0d5f2 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -33,7 +33,7 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c index ea5db21ff..457c8ade1 100644 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c @@ -33,7 +33,7 @@ void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index c07c600ee..5eea98f85 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -35,21 +35,25 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_BLIT_POSITION_IN: nv3->pgraph.blit.point_in.x = (param & 0xFFFF); nv3->pgraph.blit.point_in.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: S2SB POINT_IN %04x,%04x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); break; case NV3_BLIT_POSITION_OUT: nv3->pgraph.blit.point_out.x = (param & 0xFFFF); nv3->pgraph.blit.point_out.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: S2SB POINT_OUT %04x,%04x\n", nv3->pgraph.blit.point_out.x, nv3->pgraph.blit.point_out.y); + break; case NV3_BLIT_SIZE: /* This is the last one*/ nv3->pgraph.blit.size.w = (param & 0xFFFF); nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); - + nv_log("Method Execution: S2SB Size %04x,%04x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); + nv3_render_blit_screen2screen(grobj); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 42574bc30..523aadff7 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -133,7 +133,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } else { - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); } diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c index 799673391..2ac47f0de 100644 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c @@ -34,7 +34,7 @@ void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c index a27538dcb..b49b1fc89 100644 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c @@ -34,7 +34,7 @@ void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c index d029dc080..3fa88ba39 100644 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c @@ -33,7 +33,7 @@ void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c index c4c7aa0cd..3bb8f66d7 100644 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c @@ -33,7 +33,7 @@ void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c index 6a0632377..87111b70c 100644 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c @@ -35,7 +35,7 @@ void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); return; } diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 5d06ed678..dd5768103 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -82,7 +82,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; default: - nv_log("%s: Invalid or Unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 1e3347beb..c1b8a2576 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -78,10 +78,8 @@ void nv3_ptimer_tick(double real_time) // truncate it nv3->ptimer.time += (uint64_t)current_time; + // Check if the alarm has actually triggered.. // Only log on ptimer alarm. Otherwise, it's too much spam. - //nv_log("PTIMER time ticked (The value is now 0x%08x)\n", nv3->ptimer.time); - - // Check if the alarm has actually triggered... if (nv3->ptimer.time >= nv3->ptimer.alarm) { nv_log("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); From 7234b432ac2301aa7fb234c69974e2f40c8f0ae3 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 19:06:46 +0000 Subject: [PATCH 151/274] inform the emulator of the video timings --- src/include/86box/nv/render/vid_nv3_render.h | 5 +- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 2 +- .../nv/nv3/classes/nv3_class_006_pattern.c | 4 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 5 + .../nv/nv3/classes/nv3_class_011_image.c | 77 +--------------- src/video/nv/nv3/nv3_core.c | 22 ++++- src/video/nv/nv3/render/nv3_render_blit.c | 91 ++++++++++++++++++- src/video/nv/nv3/render/nv3_render_core.c | 10 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 11 ++- 9 files changed, 131 insertions(+), 96 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 65541fa0d..fe429da74 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -24,7 +24,7 @@ uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); -nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color); // Convert a colour to full RGB10 format from the current working format. +nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color); // Convert a colour from the current working format to RGB10 format. /* Pattern */ @@ -34,9 +34,10 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); /* Chroma */ -bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color); +bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj); /* Blit */ +void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj); void nv3_render_blit_screen2screen(nv3_grobj_t grobj); /* GDI */ diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index fae6771e2..f2e575933 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -33,7 +33,7 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ switch (method_id) { case NV3_CHROMA_KEY: - nv3_color_expanded_t expanded_color = nv3_render_expand_color(grobj, param); + nv3_color_expanded_t expanded_color = nv3_render_expand_color(param, grobj); nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); break; diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index f007d5fb8..b66db499e 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -60,11 +60,11 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_PATTERN_UNUSED_DRIVER_BUG: break; case NV3_PATTERN_COLOR0: - nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(grobj, param); + nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(param, grobj); nv3_render_set_pattern_color(expanded_colour0, false); break; case NV3_PATTERN_COLOR1: - nv3_color_expanded_t expanded_colour1 = nv3_render_expand_color(grobj, param); + nv3_color_expanded_t expanded_colour1 = nv3_render_expand_color(param, grobj); nv3_render_set_pattern_color(expanded_colour1, true); break; case NV3_PATTERN_BITMAP_HIGH: diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 5eea98f85..57184054d 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -49,6 +49,11 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); nv_log("Method Execution: S2SB Size %04x,%04x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); + /* Some blits have (point_in == point_out) ???? */ + if (nv3->pgraph.blit.point_in.x == nv3->pgraph.blit.point_out.x + && nv3->pgraph.blit.point_in.y == nv3->pgraph.blit.point_out.y) + return; + nv3_render_blit_screen2screen(grobj); break; diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index 523aadff7..c1d1200b9 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -28,19 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -/* Check the line bounds */ -void nv3_class_011_check_line_bounds() -{ - uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; - //uint32_t relative_y = nv3->pgraph.image_current_position.y - nv3->pgraph.image.point.y; - /* In theory, relative_y should never be exceeded...because it only submits enough pixels to render the image*/ - if (relative_x >= nv3->pgraph.image.size_in.w) - { - nv3->pgraph.image_current_position.y++; - nv3->pgraph.image_current_position.x = nv3->pgraph.image.point.x; - } -} void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { @@ -65,71 +53,10 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; default: if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) - { - // shift left by 2 because it's 4 bits per si\e.. + { uint32_t pixel_slot = (method_id - NV3_IMAGE_COLOR_START) >> 2; - uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; nv_log("Method Execution: Pixel%d Colour%08x Format%x\n", pixel_slot, param, (grobj.grobj_0) & 0x07); - - /* todo: a lot of stuff */ - - uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; - - /* Some extra data is sent as padding, we need to clip it off using size_out */ - - uint16_t clip_x = nv3->pgraph.image_current_position.x + nv3->pgraph.image.size.w; - /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ - /* the reverse order is due to the endianness */ - switch (nv3->nvbase.svga.bpp) - { - // 4pixels packed into one param - case 8: - - //pixel3 - pixel3 = param & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel2 = (param >> 8) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel1 = (param >> 16) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel0 = (param >> 24) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - //2pixels packed into one param - case 15: - case 16: - pixel1 = (param) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel0 = (param >> 16) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - // just one pixel in 32bpp - case 32: - pixel0 = param; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - } + nv3_render_blit_image(param, grobj); } else { diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 28c6edab5..1718b4e7f 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -29,6 +29,13 @@ nv3_t* nv3; +/* These are a ****PLACEHOLDER**** and are copied from 3dfx VoodooBanshee/Voodoo3*/ +static video_timings_t timing_nv3_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; +static video_timings_t timing_nv3_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; +// Revision C +static video_timings_t timing_nv3t_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; +static video_timings_t timing_nv3t_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; + // Prototypes for functions only used in this translation unit void nv3_init_mappings_mmio(); void nv3_init_mappings_svga(); @@ -492,9 +499,7 @@ 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) << 2; // should these actually use separate values? @@ -530,7 +535,7 @@ void nv3_recalc_timings(svga_t* svga) } else { - svga->bpp = 15; // HACK: DO NOT change this + svga->bpp = 15; svga->lowres = 0; svga->render = svga_render_15bpp_highres; } @@ -1085,6 +1090,12 @@ void* nv3_init(const device_t *info) svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_pci); + else + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_pci); + } else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) { @@ -1094,6 +1105,11 @@ void* nv3_init(const device_t *info) svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_agp); + else + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_agp); } // set vram diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 539871884..9da3f05b1 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -29,9 +29,94 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> +/* Check the line bounds */ +void nv3_class_011_check_line_bounds() +{ + uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; + //uint32_t relative_y = nv3->pgraph.image_current_position.y - nv3->pgraph.image.point.y; + + /* In theory, relative_y should never be exceeded...because it only submits enough pixels to render the image*/ + if (relative_x >= nv3->pgraph.image.size_in.w) + { + nv3->pgraph.image_current_position.y++; + nv3->pgraph.image_current_position.x = nv3->pgraph.image.point.x; + } +} + +/* Renders an image from cpu */ +void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) +{ + // shift left by 2 because it's 4 bits per size.. + uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + + /* todo: a lot of stuff */ + + uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; + + /* Some extra data is sent as padding, we need to clip it off using size_out */ + + uint16_t clip_x = nv3->pgraph.image_current_position.x + nv3->pgraph.image.size.w; + /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ + /* the reverse order is due to the endianness */ + switch (nv3->nvbase.svga.bpp) + { + // 4pixels packed into one color + case 8: + + //pixel3 + pixel3 = color & 0xFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel2 = (color >> 8) & 0xFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel1 = (color >> 16) & 0xFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel0 = (color >> 24) & 0xFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + //2pixels packed into one color + case 15: + case 16: + pixel1 = (color) & 0xFFFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + pixel0 = (color >> 16) & 0xFFFF; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + // just one pixel in 32bpp + case 32: + pixel0 = color; + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + nv3->pgraph.image_current_position.x++; + nv3_class_011_check_line_bounds(); + + break; + } + +} + void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { - nv3_position_16_t old_position = nv3->pgraph.blit.point_in; + //nv3_position_16_t old_position = nv3->pgraph.blit.point_in + nv3->pgraph.blit.size.w; + nv3_position_16_t old_position = {0}; + old_position.x = nv3->pgraph.blit.point_in.x + nv3->pgraph.blit.size.w; + old_position.y = nv3->pgraph.blit.point_in.y + nv3->pgraph.blit.size.h; nv3_position_16_t new_position = nv3->pgraph.blit.point_out; uint16_t end_x = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); @@ -63,7 +148,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) old_position.y++; new_position.y++; - for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) + for (int32_t x = nv3->pgraph.blit.point_out.x; x >= end_x; x++) { old_position.x++; new_position.x++; @@ -79,7 +164,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) old_position.y++; new_position.y++; - for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) + for (int32_t x = nv3->pgraph.blit.point_out.x; x >= end_x; x++) { old_position.x++; new_position.x++; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 7ae00efd6..15c652e5b 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -33,7 +33,7 @@ /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! */ -nv3_color_expanded_t nv3_render_expand_color(nv3_grobj_t grobj, uint32_t color) +nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) { // grobj0 = seems to share the format of PGRAPH_CONTEXT_SWITCH register. @@ -113,7 +113,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; #ifdef ENABLE_NV_LOG - nv_log("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d", color, format, alpha_enabled); + nv_log("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); #endif uint32_t packed_color = 0x00; @@ -156,7 +156,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co } /* Runs the chroma key/color key test */ -bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color) +bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj) { bool chroma_enabled = ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_CHROMA_KEY) & 0x01); @@ -172,7 +172,7 @@ bool nv3_render_chroma_test(nv3_grobj_t grobj, uint32_t color) nv3_grobj_t grobj_fake = {0}; grobj_fake.grobj_0 = 0x02; /* we don't care about any other bits */ - nv3_color_expanded_t chroma_expanded = nv3_render_expand_color(grobj_fake, nv3->pgraph.chroma_key); + nv3_color_expanded_t chroma_expanded = nv3_render_expand_color(nv3->pgraph.chroma_key, grobj_fake); uint32_t chroma_downconverted = nv3_render_downconvert_color(grobj, chroma_expanded); @@ -315,7 +315,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob } /* TODO: Plane Mask...*/ - if (!nv3_render_chroma_test(grobj, color)) + if (!nv3_render_chroma_test(color, grobj)) return; uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index c640d5f10..fb83ffaf1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -341,14 +341,14 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) nv3->pramdac.vblank_end = value; break; case NV3_PRAMDAC_VBLANK_START: - nv3->nvbase.svga.vblankstart = value; + //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->pramdac.vtotal = value; nv3->nvbase.svga.vtotal = value; break; case NV3_PRAMDAC_HSYNC_WIDTH: @@ -361,21 +361,22 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) nv3->pramdac.hburst_end = value; break; case NV3_PRAMDAC_HBLANK_START: - nv3->nvbase.svga.hblankstart = value; + //nv3->nvbase.svga.hblankstart = value; nv3->pramdac.hblank_start = value; break; case NV3_PRAMDAC_HBLANK_END: - nv3->nvbase.svga.hblank_end_val = value; + //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; + //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; } From d8fae9abc57a54371f51527679514e61c5ffe40d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 20:39:02 +0000 Subject: [PATCH 152/274] fix clipping of images, black line is gone... --- src/include/86box/nv/vid_nv.h | 24 ++++++++-------- .../classes/nv3_class_00c_win95_gdi_text.c | 10 ++++--- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- src/video/nv/nv3/nv3_core.c | 28 +++++++++++-------- src/video/nv/nv3/render/nv3_render_blit.c | 6 ++-- 5 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 71c636c78..79a420243 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -48,20 +48,20 @@ void nv_log(const char *fmt, ...); #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 NV_PCI_DEVICE_NV1 0x0008 // Nvidia NV1 +#define NV_PCI_DEVICE_NV1_VGA 0x0009 // Nvidia NV1 VGA core +#define NV_PCI_DEVICE_NV2 0x0010 // Nvidia NV2 / Mutara V08 (cancelled) +#define NV_PCI_DEVICE_NV3 0x0018 // Nvidia NV3 (Riva 128) +#define NV_PCI_DEVICE_NV3T 0x0019 // Nvidia NV3T (Riva 128 ZX) +#define NV_PCI_DEVICE_NV4 0x0020 // Nvidia NV4 (RIVA TNT) -#define CHIP_REVISION_NV1_A0 0x0000 // 1994 -#define CHIP_REVISION_NV1_B0 0x0010 // 1995 -#define CHIP_REVISION_NV1_C0 0x0020 // +#define NV_CHIP_REVISION_NV1_A0 0x0000 // 1994 +#define NV_CHIP_REVISION_NV1_B0 0x0010 // 1995 +#define NV_CHIP_REVISION_NV1_C0 0x0020 // 1995-96? -#define CHIP_REVISION_NV3_A0 0x0000 // January 1997 -#define CHIP_REVISION_NV3_B0 0x0010 // October 1997 -#define CHIP_REVISION_NV3_C0 0x0020 // 1998 +#define NV_CHIP_REVISION_NV3_A0 0x0000 // January 1997 +#define NV_CHIP_REVISION_NV3_B0 0x0010 // October 1997 +#define NV_CHIP_REVISION_NV3_C0 0x0020 // 1998 // Architecture IDs #define NV_ARCHITECTURE_NV1 1 // NV1/STG2000 diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index ea886476d..30926020a 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -131,11 +131,13 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; + + // IN THIS ONE SPECIFIC PLACE, ****AND ONLY THIS ONE SPECIFIC PLACE****, X AND Y ARE SWAPPED???? */ // If the size is submitted, render it. if (method_id & 0x04) { - nv3->pgraph.win95_gdi_text.rect_a_size[index].w = param & 0xFFFF; - nv3->pgraph.win95_gdi_text.rect_a_size[index].h = (param >> 16) & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_size[index].w = (param >> 16) & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_size[index].h = param & 0xFFFF; nv_log("Method Execution: Rect GDI-A%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, nv3->pgraph.win95_gdi_text.rect_a_size[index].h, nv3->pgraph.win95_gdi_text.color_a); @@ -145,8 +147,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ } else // position { - nv3->pgraph.win95_gdi_text.rect_a_position[index].x = param & 0xFFFF; - nv3->pgraph.win95_gdi_text.rect_a_position[index].y = (param >> 16) & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_position[index].x = (param >> 16) & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_position[index].y = param & 0xFFFF; nv_log("Method Execution: Rect GDI-A%d Position=%d,%d\n", index, nv3->pgraph.win95_gdi_text.rect_a_position[index].x, nv3->pgraph.win95_gdi_text.rect_a_position[index].y); diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 57184054d..7bae42184 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -54,7 +54,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ && nv3->pgraph.blit.point_in.y == nv3->pgraph.blit.point_out.y) return; - nv3_render_blit_screen2screen(grobj); + //nv3_render_blit_screen2screen(grobj); break; default: diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 1718b4e7f..5bab81bc4 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -27,6 +27,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> +/* Main device object pointer */ nv3_t* nv3; /* These are a ****PLACEHOLDER**** and are copied from 3dfx VoodooBanshee/Voodoo3*/ @@ -257,11 +258,11 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) // device id case NV3_PCI_CFG_DEVICE_ID: - ret = (PCI_DEVICE_NV3 & 0xFF); + ret = (NV_PCI_DEVICE_NV3 & 0xFF); break; case NV3_PCI_CFG_DEVICE_ID+1: - ret = (PCI_DEVICE_NV3 >> 8); + ret = (NV_PCI_DEVICE_NV3 >> 8); break; // various capabilities @@ -498,6 +499,7 @@ void nv3_recalc_timings(svga_t* svga) return; nv3_t* nv3 = (nv3_t*)svga->priv; + uint32_t pixel_mode = svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03; svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; @@ -506,18 +508,22 @@ void nv3_recalc_timings(svga_t* svga) // i don't we should force the top 2 bits to 1... // required for VESA resolutions, force parameters higher + // only fuck around with any of this in VGAmode? - 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 + if (pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA) + { + 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) + switch (pixel_mode) { case NV3_CRTC_REGISTER_PIXELMODE_8BPP: svga->bpp = 8; diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 9da3f05b1..032b23b47 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -55,7 +55,7 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) /* Some extra data is sent as padding, we need to clip it off using size_out */ - uint16_t clip_x = nv3->pgraph.image_current_position.x + nv3->pgraph.image.size.w; + uint16_t clip_x = nv3->pgraph.image.point.x + nv3->pgraph.image.size.w; /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ /* the reverse order is due to the endianness */ switch (nv3->nvbase.svga.bpp) @@ -89,12 +89,12 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) case 15: case 16: pixel1 = (color) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); + if (nv3->pgraph.image_current_position.x < (clip_x)) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); pixel0 = (color >> 16) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + if (nv3->pgraph.image_current_position.x < (clip_x)) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); From 4e01ee82c7ffd853d6ae4174dffcd087ad3aac3c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 21:26:37 +0000 Subject: [PATCH 153/274] Mostly fix 32bpp (8bpp blackscreens instead of drawing garbage all over the screen) by shifting row offset appropriately for the pixel mode. --- src/video/nv/nv3/nv3_core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 5bab81bc4..204efaaa2 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -502,8 +502,7 @@ void nv3_recalc_timings(svga_t* svga) uint32_t pixel_mode = svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03; svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; - + // should these actually use separate values? // i don't we should force the top 2 bits to 1... @@ -526,13 +525,15 @@ void nv3_recalc_timings(svga_t* svga) switch (pixel_mode) { case NV3_CRTC_REGISTER_PIXELMODE_8BPP: + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 1; // ????? svga->bpp = 8; svga->lowres = 0; svga->map8 = svga->pallook; svga->render = svga_render_8bpp_highres; break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: - /* sometimes it really renders in 15bpp, so you need to do this */ + /* sometimes it really renders in 15bpp, so you need to do this */ + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) { svga->bpp = 16; @@ -548,6 +549,7 @@ void nv3_recalc_timings(svga_t* svga) break; case NV3_CRTC_REGISTER_PIXELMODE_32BPP: + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; svga->bpp = 32; svga->lowres = 0; svga->render = svga_render_32bpp_highres; From 0e16ef5498562655c150825be4e543f69bf93ce6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 29 Mar 2025 23:25:15 +0000 Subject: [PATCH 154/274] Implemented a dummy M2MF class implementation until I fully understand it. This is enough to get Windows 9x to display something. --- .../86box/nv/classes/vid_nv3_classes.h | 24 ++++++++- .../nv/nv3/classes/nv3_class_003_chroma_key.c | 7 +++ .../classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 51 ++++++++++++++++++- 4 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 0503c0c66..4f52bf789 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -84,6 +84,8 @@ typedef enum nv3_pgraph_class_e #define NV3_BETA_FACTOR 0x0300 // Chroma Key +// Can't figure out what this is, used in 9x but certainly software, can't find anywhere... +#define NV3_CHROMA_UNKNOWN_0200 0x0200 #define NV3_CHROMA_KEY 0x0304 // Clip @@ -114,6 +116,25 @@ typedef enum nv3_pgraph_class_e #define NV3_RECTANGLE_MAX 16 #define NV3_RECTANGLE_END 0x0480 +// M2MF +#define NV3_M2MF_IN_CTXDMA_OFFSET 0x030C +#define NV3_M2MF_OUT_CTXDMA_OFFSET 0x0310 +#define NV3_M2MF_IN_PITCH 0x0314 +#define NV3_M2MF_OUT_PITCH 0x0318 +#define NV3_M2MF_SCANLINE_LENGTH_IN_BYTES 0x031C +#define NV3_M2MF_NUM_SCANLINES 0x0320 +#define NV3_M2MF_FORMAT 0x0324 + +// M2MF formats (IN and OUT ORed together) +#define NV3_M2MF_FORMAT_INPUT_INC_1 0x1 +#define NV3_M2MF_FORMAT_INPUT_INC_2 0x2 +#define NV3_M2MF_FORMAT_INPUT_INC_4 0x4 +#define NV3_M2MF_FORMAT_OUTPUT_INC_1 0x100 +#define NV3_M2MF_FORMAT_OUTPUT_INC_2 0x200 +#define NV3_M2MF_FORMAT_OUTPUT_INC_4 0x400 + +#define NV3_M2MF_NOTIFY 0x0328 + // blit #define NV3_BLIT_POSITION_IN 0x0300 #define NV3_BLIT_POSITION_OUT 0x0304 @@ -634,8 +655,7 @@ typedef struct nv3_object_class_00D uint32_t pitch_out; uint32_t line_length_in; // Stride? uint32_t line_count; - uint8_t format_input_bits; // 1 2 or 4 to increment by bits - uint8_t format_output_bits; // 1 2 to 4 to increment by bits + uint8_t format; // input increment 1 2 or 4, output increment 1 2 or 4 (represented by << 8) uint8_t reserved3[2]; uint32_t buffer_notify; // Notify the Buffedr uint8_t reserved4[0x1CD3]; diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index f2e575933..139099e78 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -32,10 +32,17 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + case NV3_CHROMA_UNKNOWN_0200: + nv_log("Method Execution: Chroma Unknown 0x0200 0x%08x", param); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + + break; case NV3_CHROMA_KEY: nv3_color_expanded_t expanded_color = nv3_render_expand_color(param, grobj); nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); + + nv_log("Method Execution: Chroma = 0x%08x", nv3->pgraph.chroma_key); break; default: warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index 30926020a..e006c41f3 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -192,7 +192,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ return; } - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + nv_log("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index fd8f0d5f2..b75a8e546 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -32,9 +32,58 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { switch (method_id) { + case NV3_M2MF_IN_CTXDMA_OFFSET: + nv3->pgraph.m2mf.offset_in = param; + nv_log("Method Execution: M2MF Offset In = 0x%08x", param); + break; + case NV3_M2MF_OUT_CTXDMA_OFFSET: + nv3->pgraph.m2mf.offset_out = param; + nv_log("Method Execution: M2MF Offset Out = 0x%08x", param); + break; + case NV3_M2MF_IN_PITCH: + nv3->pgraph.m2mf.pitch_in = param; + nv_log("Method Execution: M2MF Pitch In = 0x%08x", param); + break; + case NV3_M2MF_OUT_PITCH: + nv3->pgraph.m2mf.pitch_out = param; + nv_log("Method Execution: M2MF Pitch Out = 0x%08x", param); + break; + case NV3_M2MF_SCANLINE_LENGTH_IN_BYTES: + nv3->pgraph.m2mf.line_length_in = param; + nv_log("Method Execution: M2MF Scanline Length in Bytes = 0x%08x", param); + break; + case NV3_M2MF_NUM_SCANLINES: + nv3->pgraph.m2mf.line_count = param; + nv_log("Method Execution: M2MF Num Scanlines = 0x%08x", param); + break; + case NV3_M2MF_FORMAT: + nv3->pgraph.m2mf.format = param; + nv_log("Method Execution: M2MF Format = 0x%08x", param); + break; + case NV3_M2MF_NOTIFY: + /* This is technically its own thing, but I don't know if it's ever a problem with how we've designed it */ + if (nv3->pgraph.notify_pending) + { + nv_log("M2MF notification with notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); + nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); + nv3_debug_ramin_print_context_info(param, context); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + + // disable + nv3->pgraph.notify_pending = false; + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + /* may need to disable fifo in this state */ + return; + } + + nv_log("Method Execution: TODO: ACTUALLY IMPLEMENT M2MF!!!!"); + // set a notify as pending. + nv3->pgraph.notifier = param; + nv3->pgraph.notify_pending = true; + break; default: warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; + break;; } } \ No newline at end of file From c8e7bfac9e05aff3a6f57722390cfde11f1e23e7 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 30 Mar 2025 02:38:59 +0100 Subject: [PATCH 155/274] make 2k and 9x live happily together, seems vtrace register bit 1 controls rowoffset shifting --- doc/nvidia_notes/status.xlsx | Bin 15813 -> 15983 bytes .../classes/nv3_class_00c_win95_gdi_text.c | 10 ++++++++++ src/video/nv/nv3/nv3_core.c | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index d43765e803cd7105a9c392f5c3b8fe7889659bd2..6fc2aaa7411f99190fe376bd6114dfa499f86c48 100644 GIT binary patch delta 8245 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 Oj?WaNg!AP4Yy2Poifpt1 delta 8072 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.win95_gdi_text.color_a = param; nv_log("Method Execution: GDI-A Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; + case NV3_W95TXT_B_CLIP_TOPLEFT: + nv3->pgraph.win95_gdi_text.clip_b.left = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_b.top = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-B Clip Left,Top %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.left, nv3->pgraph.win95_gdi_text.clip_b.top); + break; + case NV3_W95TXT_B_CLIP_BOTTOMRIGHT: + nv3->pgraph.win95_gdi_text.clip_b.bottom = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_b.right = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-B Clip Bottom,Right %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.left, nv3->pgraph.win95_gdi_text.clip_b.top); + break; /* Type B and C not implemented YET, as they are not used by NT GDI driver */ case NV3_W95TXT_D_CLIP_TOPLEFT: nv3->pgraph.win95_gdi_text.clip_d.left = (param & 0xFFFF); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 204efaaa2..a365cab62 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -502,7 +502,7 @@ void nv3_recalc_timings(svga_t* svga) uint32_t pixel_mode = svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03; svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; - + // should these actually use separate values? // i don't we should force the top 2 bits to 1... @@ -532,8 +532,18 @@ void nv3_recalc_timings(svga_t* svga) svga->render = svga_render_8bpp_highres; break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: + /* This is some sketchy shit that is an attempt at an educated guess + at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, this is what's causing it */ + if ((svga->crtc[NV3_CRTC_REGISTER_VRETRACESTART] >> 1) & 0x01) + { + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; + } + else + { + svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; + } + /* sometimes it really renders in 15bpp, so you need to do this */ - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) { svga->bpp = 16; @@ -545,11 +555,14 @@ void nv3_recalc_timings(svga_t* svga) svga->bpp = 15; svga->lowres = 0; svga->render = svga_render_15bpp_highres; + + // fixes win2000, but breaks 9x?! } break; case NV3_CRTC_REGISTER_PIXELMODE_32BPP: svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; + svga->bpp = 32; svga->lowres = 0; svga->render = svga_render_32bpp_highres; From f1a231b6cced1b0d4586c0c8e6d68cfe4c68487c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 30 Mar 2025 23:56:42 +0100 Subject: [PATCH 156/274] Implement GDI-B. Rewrite text rendering code for perfect implementation of GDI-C, GDI-D and GDI-E text classes. Fucking hell that took forever.... --- .../86box/nv/classes/vid_nv3_classes.h | 9 +- src/include/86box/nv/render/vid_nv3_render.h | 7 +- src/include/86box/nv/vid_nv3.h | 4 +- .../classes/nv3_class_00c_win95_gdi_text.c | 132 ++++-- .../nv/nv3/render/nv3_render_primitives.c | 385 +++++++++++++----- 5 files changed, 392 insertions(+), 145 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 4f52bf789..e3a1bf7aa 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -165,6 +165,7 @@ typedef enum nv3_pgraph_class_e /* Type B: Clipped Rectangle */ #define NV3_W95TXT_B_CLIP_TOPLEFT 0x07F4 #define NV3_W95TXT_B_CLIP_BOTTOMRIGHT 0x07F8 +#define NV3_W95TXT_B_COLOR 0x07FC #define NV3_W95TXT_B_CLIP_CLIPRECT_START 0x0800 #define NV3_W95TXT_B_CLIP_CLIPRECT_SIZE 128 // Number of rects #define NV3_W95TXT_B_CLIP_CLIPRECT_END 0x09FF @@ -610,20 +611,20 @@ typedef struct nv3_object_class_00C uint8_t reserved3[0x1F0]; nv3_clip_16_t clip_b; uint32_t color_b; // Color for Clip B - nv3_clip_16_t rect_clip[64]; + nv3_clip_16_t clipped_rect[64]; uint8_t reserved4[0x1E8]; nv3_clip_16_t clip_c; uint32_t color1_c; nv3_size_16_t size_c; nv3_position_16_t point_c; - uint32_t color1_c_bitmap[128]; + uint32_t bitmap_c[128]; uint8_t reserved5[0x368]; nv3_clip_16_t clip_d; uint32_t color1_d; nv3_size_16_t size_in_d; nv3_size_16_t size_out_d; nv3_position_16_t point_d; - uint32_t mono_color1_d[128]; + uint32_t bitmap_d[128]; uint8_t reserved6[0x364]; nv3_clip_16_t clip_e; uint32_t color0_e; @@ -631,7 +632,7 @@ typedef struct nv3_object_class_00C nv3_size_16_t size_in_e; nv3_size_16_t size_out_e; nv3_position_16_t point_e; - uint32_t mono_color1_e[128]; + uint32_t bitmap_e[128]; uint8_t reserved7[0xB7F]; } nv3_win95_text_t; diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index fe429da74..9b5acd06d 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -31,7 +31,8 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); /* Primitives */ -void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); +void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); // Render an A (unclipped) GDI rect +void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj); // Render a B (clipped) GDI rect. /* Chroma */ bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj); @@ -41,5 +42,5 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj); void nv3_render_blit_screen2screen(nv3_grobj_t grobj); /* GDI */ -void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param); /* GDI Type-D: Clipped 1bpp text */ -void nv3_render_gdi_type_e(nv3_grobj_t grobj, uint32_t param); /* GDI Type-E: Clipped 1bpp two-colour text */ \ No newline at end of file +void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj); +void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj); /* GDI Type-E: Clipped 1bpp colour-expanded bitmap */ \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index ed55e050d..1957984d2 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1229,7 +1229,9 @@ typedef struct nv3_pgraph_s struct nv3_object_class_00A lin; struct nv3_object_class_00B triangle; struct nv3_object_class_00C win95_gdi_text; - nv3_position_16_t win95_gdi_text_current_position; /* This is here so we can hold the current state of the image draw */ + /* These are here so we can hold the current state of the image draw */ + uint32_t win95_gdi_text_bit_count; + nv3_position_16_t win95_gdi_text_current_position; struct nv3_object_class_00D m2mf; struct nv3_object_class_00E scaled_image_from_memory; struct nv3_object_class_010 blit; diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index a32358912..e2f640fac 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -39,6 +39,11 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.color_a = param; nv_log("Method Execution: GDI-A Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; + /* Type B: Clipped Rectangle */ + case NV3_W95TXT_B_COLOR: + nv3->pgraph.win95_gdi_text.color_b = param; + nv_log("Method Execution: GDI-B Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_b); + break; case NV3_W95TXT_B_CLIP_TOPLEFT: nv3->pgraph.win95_gdi_text.clip_b.left = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_b.top = ((param >> 16) & 0xFFFF); @@ -47,20 +52,53 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_W95TXT_B_CLIP_BOTTOMRIGHT: nv3->pgraph.win95_gdi_text.clip_b.bottom = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_b.right = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-B Clip Bottom,Right %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.left, nv3->pgraph.win95_gdi_text.clip_b.top); + nv_log("Method Execution: GDI-B Clip Bottom,Right %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.right, nv3->pgraph.win95_gdi_text.clip_b.bottom); + break; + /* Type C: Unclipped Bitmap */ + case NV3_W95TXT_C_CLIP_COLOR: + nv3->pgraph.win95_gdi_text.color1_c = param; + nv_log("Method Execution: GDI-C Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color1_c); + break; + case NV3_W95TXT_C_CLIP_SIZE: + nv3->pgraph.win95_gdi_text.size_c.w = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_c.h = ((param >> 16) & 0xFFFF); + + nv3->pgraph.win95_gdi_text_bit_count = 0; + nv_log("Method Execution: GDI-C Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_c.w, nv3->pgraph.win95_gdi_text.size_c.h); + break; + case NV3_W95TXT_C_CLIP_POSITION: + nv3->pgraph.win95_gdi_text.point_c.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.point_c.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-C Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_c.x, nv3->pgraph.win95_gdi_text.point_c.y); + + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_c.x ; + nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_c.y; + + break; + case NV3_W95TXT_C_CLIP_TOPLEFT: + nv3->pgraph.win95_gdi_text.clip_c.left = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_c.top = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-C Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.top); + break; + case NV3_W95TXT_C_CLIP_BOTTOMRIGHT: + nv3->pgraph.win95_gdi_text.clip_c.right = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clip_c.bottom = ((param >> 16) & 0xFFFF); + /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ + nv_log("Method Execution: GDI-C Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.top); break; /* Type B and C not implemented YET, as they are not used by NT GDI driver */ case NV3_W95TXT_D_CLIP_TOPLEFT: nv3->pgraph.win95_gdi_text.clip_d.left = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_d.top = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-A Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); + nv_log("Method Execution: GDI-D Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); break; case NV3_W95TXT_D_CLIP_BOTTOMRIGHT: nv3->pgraph.win95_gdi_text.clip_d.right = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_d.bottom = ((param >> 16) & 0xFFFF); /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ - nv_log("Method Execution: GDI-A Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); + nv_log("Method Execution: GDI-D Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); break; + case NV3_W95TXT_D_CLIP_COLOR: nv3->pgraph.win95_gdi_text.color1_d = param; nv_log("Method Execution: GDI-D Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); @@ -80,29 +118,21 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); nv_log("Method Execution: GDI-D Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y); - /* small case*/ - if (nv3->pgraph.win95_gdi_text.size_in_d.w < 0x0010) - { - nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_in_d.w - 1)); - nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); - } - /* large case: draw (7-0) (15-8)*/ - else - { - uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_d.w >> 1) - 1; - nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + large_start); - nv3->pgraph.win95_gdi_text_current_position.y = (nv3->pgraph.win95_gdi_text.point_d.y); - } + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; + nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_d.y; + nv3->pgraph.win95_gdi_text_bit_count = 0; break; /* Type E: Two-colour 1bpp */ case NV3_W95TXT_E_CLIP_TOPLEFT: nv3->pgraph.win95_gdi_text.clip_e.left = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_e.top = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Clip Left,Top 0x%08x\n", nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.top); break; case NV3_W95TXT_E_CLIP_BOTTOMRIGHT: nv3->pgraph.win95_gdi_text.clip_e.right = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.clip_e.bottom = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Clip Bottom,Right 0x%08x\n", nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.bottom); /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ break; case NV3_W95TXT_E_CLIP_COLOR_0: @@ -127,11 +157,9 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.point_e.x = (param & 0xFFFF); nv3->pgraph.win95_gdi_text.point_e.y = ((param >> 16) & 0xFFFF); - uint16_t large_start = (nv3->pgraph.win95_gdi_text.size_in_e.w >> 1) - 1; - - //nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_e.x + large_start); nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_e.y; + nv3->pgraph.win95_gdi_text_bit_count = 0; nv_log("Method Execution: GDI-E Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y); break; @@ -139,9 +167,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Type A submission: these are the same things as rectangles */ if (method_id >= NV3_W95TXT_A_RECT_START && method_id <= NV3_W95TXT_A_RECT_END) { - uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; + uint32_t index = (method_id - NV3_W95TXT_A_RECT_START) >> 3; - // IN THIS ONE SPECIFIC PLACE, ****AND ONLY THIS ONE SPECIFIC PLACE****, X AND Y ARE SWAPPED???? */ // If the size is submitted, render it. if (method_id & 0x04) @@ -149,8 +176,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.rect_a_size[index].w = (param >> 16) & 0xFFFF; nv3->pgraph.win95_gdi_text.rect_a_size[index].h = param & 0xFFFF; - nv_log("Method Execution: Rect GDI-A%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, - nv3->pgraph.win95_gdi_text.rect_a_size[index].h, nv3->pgraph.win95_gdi_text.color_a); + nv_log("Method Execution: Rect GDI-A%d Size=%d,%d", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, + nv3->pgraph.win95_gdi_text.rect_a_size[index].h); nv3_render_rect(nv3->pgraph.win95_gdi_text.rect_a_position[index], nv3->pgraph.win95_gdi_text.rect_a_size[index], nv3->pgraph.win95_gdi_text.color_a, grobj); @@ -163,14 +190,62 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: Rect GDI-A%d Position=%d,%d\n", index, nv3->pgraph.win95_gdi_text.rect_a_position[index].x, nv3->pgraph.win95_gdi_text.rect_a_position[index].y); } + return; } + /* Type B: Clipped Rectangle */ + else if (method_id >= NV3_W95TXT_B_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_B_CLIP_CLIPRECT_END) + { + uint32_t index = (method_id - NV3_W95TXT_B_CLIP_CLIPRECT_START) >> 3; + + /* Works slightly differently, we define the bounds of the rectangle instead. */ + if (method_id & 0x04) + { + nv3->pgraph.win95_gdi_text.clipped_rect[index].right = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clipped_rect[index].bottom = ((param >> 16) & 0xFFFF); + + nv_log("Method Execution: Rect GDI-B%d Right,Bottom=%d,%d\n", index, nv3->pgraph.win95_gdi_text.clipped_rect[index].right, + nv3->pgraph.win95_gdi_text.clipped_rect[index].bottom); + + nv3_render_rect_clipped(nv3->pgraph.win95_gdi_text.clipped_rect[index], + nv3->pgraph.win95_gdi_text.color_b, grobj); + } + else // left,top + { + nv3->pgraph.win95_gdi_text.clipped_rect[index].left = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.clipped_rect[index].top = ((param >> 16) & 0xFFFF); + + nv_log("Method Execution: Rect GDI-B%d Left,Top=%d,%d\n", index, + nv3->pgraph.win95_gdi_text.clipped_rect[index].left, nv3->pgraph.win95_gdi_text.clipped_rect[index].top); + } + + return; + } + /* Type C */ + else if (method_id >= NV3_W95TXT_C_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_C_CLIP_CLIPRECT_END) + { + /* lol */ + uint32_t index = (method_id - NV3_W95TXT_C_CLIP_CLIPRECT_START) >> 3; + + nv3->pgraph.win95_gdi_text.bitmap_c[index] = param; + + /* Mammoth logger! */ + nv_log("Method Execution: Rect GDI-C%d Data=%08x Size%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", + index, param, nv3->pgraph.win95_gdi_text.size_c.w, nv3->pgraph.win95_gdi_text.size_c.h, + nv3->pgraph.win95_gdi_text.point_c.x, nv3->pgraph.win95_gdi_text.point_c.y, + nv3->pgraph.win95_gdi_text.color1_c, + nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.right, + nv3->pgraph.win95_gdi_text.clip_c.top, nv3->pgraph.win95_gdi_text.clip_c.bottom); + + nv3_render_gdi_transparent_bitmap(false, nv3->pgraph.win95_gdi_text.color1_c, nv3->pgraph.win95_gdi_text.bitmap_c[index], grobj); + return; + } else if (method_id >= NV3_W95TXT_D_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_D_CLIP_CLIPRECT_END) { /* lol */ uint32_t index = (method_id - NV3_W95TXT_D_CLIP_CLIPRECT_START) >> 3; - nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; + nv3->pgraph.win95_gdi_text.bitmap_d[index] = param; /* Mammoth logger! */ nv_log("Method Execution: Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", @@ -178,9 +253,10 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h, nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y, nv3->pgraph.win95_gdi_text.color1_d, - nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, nv3->pgraph.win95_gdi_text.clip_d.top, nv3->pgraph.win95_gdi_text.clip_d.bottom); + nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, + nv3->pgraph.win95_gdi_text.clip_d.top, nv3->pgraph.win95_gdi_text.clip_d.bottom); - nv3_render_gdi_type_d(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); + nv3_render_gdi_transparent_bitmap(true, nv3->pgraph.win95_gdi_text.color1_d, nv3->pgraph.win95_gdi_text.bitmap_d[index], grobj); return; } else if (method_id >= NV3_W95TXT_E_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_E_CLIP_CLIPRECT_END) @@ -188,7 +264,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* lol */ uint32_t index = (method_id - NV3_W95TXT_E_CLIP_CLIPRECT_START) >> 3; - nv3->pgraph.win95_gdi_text.mono_color1_d[index] = param; + nv3->pgraph.win95_gdi_text.bitmap_e[index] = param; /* Mammoth logger! */ nv_log("Method Execution: Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", @@ -198,7 +274,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.win95_gdi_text.color1_e, nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.top, nv3->pgraph.win95_gdi_text.clip_e.bottom); - nv3_render_gdi_type_e(grobj, nv3->pgraph.win95_gdi_text.mono_color1_d[index]); + nv3_render_gdi_1bpp_bitmap(nv3->pgraph.win95_gdi_text.color0_e, nv3->pgraph.win95_gdi_text.color1_e, nv3->pgraph.win95_gdi_text.bitmap_e[index], grobj); return; } diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 4695d05b7..487f41731 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -46,140 +46,307 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co } } -void nv3_render_text_1bpp(bool bit, nv3_grobj_t grobj) +/* Render GDI-B clipped rectangle */ +void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj) { - uint16_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_out_d.w; - uint16_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + nv3->pgraph.win95_gdi_text.size_out_d.h; + nv3_position_16_t current_pos = {0}; - /* they send more data than they need */ - if (nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) - bit = false; - - uint32_t final_color; - - // if it's a 0 bit we don't need to do anything - if (bit) + for (int32_t y = clip.top; y < clip.bottom; y++) { - switch (nv3->nvbase.svga.bpp) + current_pos.y = y; + + for (int32_t x = clip.left; x < clip.right; x++) { - case 8: - final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFF); /* do we need to add anything? mul blend perhaps? */ - break; - case 15: - case 16: - final_color = (nv3->pgraph.win95_gdi_text.color1_d & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ - break; - case 32: - final_color = (nv3->pgraph.win95_gdi_text.color1_d); /* do we need to add anything? mul blend perhaps? */ - break; + current_pos.x = x; + + /* compare against the global clip too */ + if (current_pos.x >= nv3->pgraph.win95_gdi_text.clip_b.left + && current_pos.x <= nv3->pgraph.win95_gdi_text.clip_b.right + && current_pos.y >= nv3->pgraph.win95_gdi_text.clip_b.top + && current_pos.y <= nv3->pgraph.win95_gdi_text.clip_b.bottom) + { + nv3_render_write_pixel(current_pos, color, grobj); + } } } +} - /* in type d colour0 is always transparent */ - if (bit) - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color, grobj); - - /* increment the position - the bitmap is stored horizontally backward */ - nv3->pgraph.win95_gdi_text_current_position.x--; - - /* check if we need to go down a line */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.point_d.x) +void nv3_render_gdi_transparent_bitmap_blit(bool bit, bool clip, uint32_t color, nv3_grobj_t grobj) +{ + /* If the bit is set, and cliping is enabled (Type D) tru and lcip */ + if (bit && clip) { - nv3->pgraph.win95_gdi_text_current_position.x = (nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w - 1); - - nv3->pgraph.win95_gdi_text_current_position.y++; + /* Turn the bit off if we need to clip (Type D ) */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) + { + bit = false; + } + + /* + Also clip if we are outside of the SIZE_OUT range + We only need to do this in one direction just to get rid of the crud sent by NV + */ + uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_out_d.w); + uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + (nv3->pgraph.win95_gdi_text.size_out_d.h); + + if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x + || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) + bit = false; } - /* check if we are in the clipping rectangle */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) + /* We don't need to and it, because it seems the Riva only uses non-packed bpp formats for this class */ + if (bit) + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, color, grobj); + + /* + Check if we've reached the bottom + Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) + */ + + uint32_t end_x = (clip) ? nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w : nv3->pgraph.win95_gdi_text.point_c.x + nv3->pgraph.win95_gdi_text.size_c.w; + + nv3->pgraph.win95_gdi_text_current_position.x++; + + if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) { + nv3->pgraph.win95_gdi_text_current_position.y++; + + if (!clip) + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_c.x; + else + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; + } +} + +/* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ +void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj) +{ + /* + First, we need to figure out how many bits we have left. + If we have less than 32, don't process all of the bits. + + Bits are processed in the following order: [7-0] [15-8] [23-16] [31-24] + TODO: Store this somewhere, so it doesn't need to be recalculated. + + We store a global bit count for this purpose. + */ + + uint32_t bitmap_size = (clip) ? nv3->pgraph.win95_gdi_text.size_in_d.w * nv3->pgraph.win95_gdi_text.size_in_d.h : nv3->pgraph.win95_gdi_text.size_c.w * nv3->pgraph.win95_gdi_text.size_c.h; + uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; + + /* we have to interpret every bit in reverse bit order but in the right byte order */ + + bool current_bit = false; + + /* Start by rendering bits 7 through 0 */ + for (int32_t bit = 7; bit >= 0; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) return; - } -} -void nv3_render_gdi_type_d(nv3_grobj_t grobj, uint32_t param) -{ - // reset when a position is submitted - nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; - - /* Go through the bitmap that was sent, bit by bit. */ - for (int32_t bit_num = 0; bit_num <= 31; bit_num++) + /* Now for 15 through 8 */ + for (int32_t bit = 15; bit >= 8; bit--) { - bool bit = (param >> bit_num) & 0x01; + current_bit = (bitmap_data >> bit) & 0x01; - nv3_render_text_1bpp(bit, grobj); + nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; } -} -/* 2-colour 1bpp color-expanded text from [7-0] */ -void nv3_render_text_1bpp_2color(uint32_t byte, nv3_grobj_t grobj) -{ - for (int32_t bit_num = 0; bit_num <= 7; bit_num++) + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; + + /* Now for 23 through 16 */ + for (int32_t bit = 23; bit >= 16; bit--) { - bool bit = (byte >> bit_num) & 0x01; + current_bit = (bitmap_data >> bit) & 0x01; - uint16_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_out_e.w; - uint16_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + nv3->pgraph.win95_gdi_text.size_out_e.h; - - // if it's a 0 bit we don't need to do anything - - uint32_t final_color; - - switch (nv3->nvbase.svga.bpp) - { - case 8: - final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFF); /* do we need to add anything? mul blend perhaps? */ - break; - case 15: - case 16: - final_color = (bit) ? (nv3->pgraph.win95_gdi_text.color1_e & 0xFFFF) : (nv3->pgraph.win95_gdi_text.color0_e & 0xFFFF); /* do we need to add anything? mul blend perhaps? */ - break; - case 32: - final_color = (bit) ? nv3->pgraph.win95_gdi_text.color1_e : nv3->pgraph.win95_gdi_text.color0_e; /* do we need to add anything? mul blend perhaps? */ - break; - } + nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, final_color, grobj); - - /* increment the position - the bitmap is stored horizontally backward */ - nv3->pgraph.win95_gdi_text_current_position.x--; - - /* see if we need to go to the next line */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.point_e.x) - { - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.w - 1); - nv3->pgraph.win95_gdi_text_current_position.y++; - } - - /* check if we are in the clipping rectangle */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_e.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_e.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_e.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_e.bottom) - { - return; - } + if (!bits_remaining_in_bitmap) + break; } - + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; + + /* Now for 31 through 24 */ + for (int32_t bit = 31; bit >= 24; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; } -void nv3_render_gdi_type_e(nv3_grobj_t grobj, uint32_t param) +void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, nv3_grobj_t grobj) { - // reset when a position is submitted - nv3_position_16_t start_position = nv3->pgraph.win95_gdi_text_current_position; + /* We can't force the bit off because this is a 1bpp bitmap */ + bool skip = false; - /* we have to interpret every bit in reverse order but in the right bit order */ - uint8_t byte0 = ((param & 0xFF000000) >> 24); - uint8_t byte1 = ((param & 0xFF0000) >> 16); - uint8_t byte2 = ((param & 0xFF00) >> 8); - uint8_t byte3 = (param & 0xFF); + /* For Type E, always clip */ + /* Turn the bit off if we need to clip (Type D ) */ + if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_e.left + || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_e.right + || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_e.top + || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_e.bottom) + { + skip = true; + } - nv3_render_text_1bpp_2color(byte0, grobj); - nv3_render_text_1bpp_2color(byte1, grobj); - nv3_render_text_1bpp_2color(byte2, grobj); - nv3_render_text_1bpp_2color(byte3, grobj); + /* + Also clip if we are outside of the SIZE_OUT range + We only need to do this in one direction just to get rid of the crud sent by NV + */ + uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.w); + uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + (nv3->pgraph.win95_gdi_text.size_out_e.h); + + if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x + || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) + skip = true; + + /* We don't need to and it, because it seems the Riva only uses non-packed bpp formats for this class */ + if (!skip) + { + if (bit) + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, nv3->pgraph.win95_gdi_text.color1_e, grobj); + else + nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, nv3->pgraph.win95_gdi_text.color0_e, grobj); + } + + + /* + Check if we've reached the bottom, if so, advance to the next horizontal lin + Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) + */ + + uint32_t end_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_in_e.w; + + nv3->pgraph.win95_gdi_text_current_position.x++; + + if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) + { + nv3->pgraph.win95_gdi_text_current_position.y++; + nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; + } +} + +/* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ +void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj) +{ + /* + First, we need to figure out how many bits we have left. + If we have less than 32, don't process all of the bits. + + Bits are processed in the following order: [7-0] [15-8] [23-16] [31-24] + TODO: Store this somewhere, so it doesn't need to be recalculated. + + We store a global bit count for this purpose. + */ + + uint32_t bitmap_size = nv3->pgraph.win95_gdi_text.size_in_e.w * nv3->pgraph.win95_gdi_text.size_in_e.h; + uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; + + /* we have to interpret every bit in reverse bit order but in the right byte order */ + + bool current_bit = false; + + /* Start by rendering bits 7 through 0 */ + for (int32_t bit = 7; bit >= 0; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; + + /* Now for 15 through 8 */ + for (int32_t bit = 15; bit >= 8; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; + + /* Now for 23 through 16 */ + for (int32_t bit = 23; bit >= 16; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; + + /* Now for 31 through 24 */ + for (int32_t bit = 31; bit >= 24; bit--) + { + current_bit = (bitmap_data >> bit) & 0x01; + + nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); + nv3->pgraph.win95_gdi_text_bit_count++; + bits_remaining_in_bitmap--; + + if (!bits_remaining_in_bitmap) + break; + } + + /* IF we're done, let's return */ + if (!bits_remaining_in_bitmap) + return; } \ No newline at end of file From 7c4e2c82d41a3e5b198ac178b9a07c1fbf2bb505 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 3 Apr 2025 01:40:59 +0100 Subject: [PATCH 157/274] Split up logs into two verbosity levels (one is just init/invalid regs/object submission/pci, everything else is verbose_only) --- doc/nvidia_notes/status.xlsx | Bin 15983 -> 16043 bytes src/include/86box/nv/vid_nv.h | 3 + src/include/86box/nv/vid_nv3.h | 6 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 4 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 24 +++--- src/video/nv/nv3/nv3_core.c | 32 +++----- src/video/nv/nv3/render/nv3_render_core.c | 13 ++-- src/video/nv/nv3/subsystems/nv3_pbus.c | 16 ++-- src/video/nv/nv3/subsystems/nv3_pextdev.c | 16 ++-- src/video/nv/nv3/subsystems/nv3_pfb.c | 29 ++++--- src/video/nv/nv3/subsystems/nv3_pfifo.c | 73 +++++++++--------- src/video/nv/nv3/subsystems/nv3_pgraph.c | 24 +++--- src/video/nv/nv3/subsystems/nv3_pmc.c | 26 +++---- src/video/nv/nv3/subsystems/nv3_pme.c | 12 +-- src/video/nv/nv3/subsystems/nv3_pramdac.c | 18 +++-- src/video/nv/nv3/subsystems/nv3_pramin.c | 28 +++---- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 4 +- .../nv/nv3/subsystems/nv3_pramin_ramht.c | 6 +- .../nv/nv3/subsystems/nv3_pramin_ramro.c | 9 +-- src/video/nv/nv3/subsystems/nv3_ptimer.c | 14 ++-- src/video/nv/nv3/subsystems/nv3_pvideo.c | 16 ++-- src/video/nv/nv3/subsystems/nv3_user.c | 6 +- src/video/nv/nv_base.c | 51 ++++++++---- 23 files changed, 219 insertions(+), 211 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 6fc2aaa7411f99190fe376bd6114dfa499f86c48..1ddb8b34dbfc1e4e750f4992b5d7e696dbe20b39 100644 GIT binary patch delta 7829 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 delta 7747 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 YVEhk`3>Fm-fQkhB3Q)j#@cxDVALLT1hyVZp diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 79a420243..c26c51868 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -38,6 +38,9 @@ void nv_log_set_device(void* device); void nv_log(const char *fmt, ...); +// Verbose logging level. +void nv_log_verbose_only(const char *fmt, ...); + // Defines common to all NV chip architectural generations // PCI IDs diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 1957984d2..411ccdecf 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 26 March 2025 (STILL WORKING ON IT!!!) + * Last updated: 2 April 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * @@ -1605,7 +1605,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t value); void nv3_pfifo_interrupt(uint32_t id, bool fire_now); // NV3 PFIFO - Caches -void nv3_pfifo_cache0_push(); +//cache0_push not a thing void nv3_pfifo_cache0_pull(); void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val); void nv3_pfifo_cache1_pull(); @@ -1641,4 +1641,4 @@ void nv3_ptimer_tick(double real_time); void nv3_pvideo_init(); // NV3 PME (Mediaport) -void nv3_pmedia_init(); \ No newline at end of file +void nv3_pme_init(); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index b75a8e546..4a6d2e39c 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -64,8 +64,8 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* This is technically its own thing, but I don't know if it's ever a problem with how we've designed it */ if (nv3->pgraph.notify_pending) { - nv_log("M2MF notification with notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); - nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); + nv_log("WARNING: M2MF notification with notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); + nv_log("IF THIS BUILD WAS COMPILED WITH NV_LOG_ENABLE_ULTRA, YOU SHOULD SEE A CONTEXT BELOW"); nv3_debug_ramin_print_context_info(param, context); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 01f0e90c7..0ec7cdd2d 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -112,33 +112,33 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t // This code is temporary and will probably be moved somewhere else // Print torns of debug info #ifdef DEBUG - nv_log("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); + nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); - nv_log("Notification Information:\n"); - nv_log("Adjust Value: 0x%08x\n", info_adjust); - (info_pt_present) ? nv_log("Pagetable Present: True\n") : nv_log("Pagetable Present: False\n"); + nv_log_verbose_only("Notification Information:\n"); + nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); + (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); switch (info_notification_target) { case NV3_NOTIFICATION_TARGET_NVM: - nv_log("Notification Target: VRAM\n"); + nv_log_verbose_only("Notification Target: VRAM\n"); break; case NV3_NOTIFICATION_TARGET_CART: - nv_log("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); + nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); break; case NV3_NOTIFICATION_TARGET_PCI: - (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log("Notification Target: PCI Bus\n") : nv_log("Notification Target: PCI Bus (On AGP card?)\n"); + (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); break; case NV3_NOTIFICATION_TARGET_AGP: (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log("Notification Target: AGP Bus\n") : nv_log("Notification Target: AGP Bus (On PCI card?)\n"); + || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); break; } - nv_log("Limit: 0x%08x\n", notify_obj_limit); - (page_is_present) ? nv_log("Page is present\n") : nv_log("Page is not present\n"); - (page_is_readwrite) ? nv_log("Page is read-write\n") : nv_log("Page is read-only\n"); - nv_log("Pageframe Address: 0x%08x\n", frame_base); + nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); + (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); + (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); + nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); #endif // set up the dma transfer. we need to translate to a physical address. diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index a365cab62..610f8c500 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -72,9 +72,7 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3); - #ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - #endif + nv_log_verbose_only("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); return ret; } @@ -100,9 +98,8 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) ret = nv3_svga_in(real_address, nv3) | (nv3_svga_in(real_address + 1, nv3) << 8); - //#ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - //#endif + nv_log_verbose_only("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + return ret; } @@ -128,9 +125,7 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) | (nv3_svga_in(real_address + 2, nv3) << 16) | (nv3_svga_in(real_address + 3, nv3) << 24); - //#ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - //#endif + nv_log_verbose_only("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); return ret; } @@ -155,9 +150,7 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - //#ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - //#endif + nv_log_verbose_only("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); @@ -184,9 +177,8 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - //#ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - //#endif + nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -214,9 +206,7 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - //#ifdef ENABLE_NV_LOG_ULTRA - nv_log("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - //#endif + nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_out(real_address, val & 0xFF, nv3); nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -1073,9 +1063,9 @@ void* nv3_init(const device_t *info) #endif nv_log("Initialising core\n"); -#ifdef ENABLE_NV_LOG_ULTRA - nv_log("ULTRA LOGGING enabled"); -#endif + // this will only be logged if ENABLE_NV_LOG_ULTRA is defined + nv_log_verbose_only("ULTRA LOGGING enabled"); + // Figure out which vbios the user selected const char* vbios_id = device_get_config_bios("vbios"); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 15c652e5b..66ee170bc 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -44,9 +44,8 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) // set the pixel format color_final.pixel_format = format; - #ifdef ENABLE_NV_LOG - nv_log("Expanding Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d", color, format, alpha_enabled); - #endif + nv_log_verbose_only("Expanding Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); + // default to fully opaque in case alpha is disabled color_final.a = 0xFF; @@ -112,9 +111,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co uint8_t format = (grobj.grobj_0 & 0x07); bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; - #ifdef ENABLE_NV_LOG - nv_log("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); - #endif + nv_log_verbose_only("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); uint32_t packed_color = 0x00; @@ -141,10 +138,10 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co packed_color |= (color.b << 10); break; case nv3_pgraph_pixel_format_y8: - nv_log("nv3_render_downconvert: Y8 not implemented"); + warning("nv3_render_downconvert: Y8 not implemented"); break; case nv3_pgraph_pixel_format_y16: - nv_log("nv3_render_downconvert: Y16 not implemented"); + warning("nv3_render_downconvert: Y16 not implemented"); break; default: warning("nv3_render_downconvert_color unknown format %d", format); diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 46d829fb2..67a052d50 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -54,7 +54,7 @@ uint32_t nv3_pbus_read(uint32_t address) // todo: friendly logging - nv_log("PBUS Read from 0x%08x", address); + nv_log_verbose_only("PBUS Read from 0x%08x", address); // if the register actually exists if (reg) @@ -79,9 +79,9 @@ uint32_t nv3_pbus_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -95,15 +95,15 @@ 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("PBUS Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("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); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) @@ -174,7 +174,7 @@ uint8_t nv3_pbus_rma_read(uint16_t addr) } // log current location for vbios RE - nv_log("MMIO Real Mode Access read, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + nv_log_verbose_only("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; @@ -239,7 +239,7 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val) nv3->pbus.rma.data &= ~0xff000000; nv3->pbus.rma.data |= (val << 24); - nv_log("MMIO Real Mode Access write transaction complete, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + nv_log_verbose_only("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) diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index c26266a00..30ba68c03 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -89,11 +89,11 @@ uint32_t nv3_pextdev_read(uint32_t address) // special consideration for straps if (address == NV3_PSTRAPS) { - nv_log("Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); + nv_log_verbose_only("Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); } else { - nv_log("PEXTDEV Read from 0x%08x", address); + nv_log_verbose_only("PEXTDEV Read from 0x%08x", address); } // if the register actually exists @@ -113,9 +113,9 @@ uint32_t nv3_pextdev_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -129,12 +129,12 @@ 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("PEXTDEV Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("PEXTDEV Write 0x%08x -> 0x%08x\n", value, address); // special consideration for straps if (address == NV3_PSTRAPS) { - nv_log("Huh? Tried to write to the straps. Something is wrong...\n", nv3->pextdev.straps); + warning("Huh? Tried to write to the straps. Something is wrong...\n", nv3->pextdev.straps); return; } @@ -142,9 +142,9 @@ void nv3_pextdev_write(uint32_t address, uint32_t value) if (reg) { if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 0be3d14c8..0b36a3993 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -75,7 +75,7 @@ uint32_t nv3_pfb_read(uint32_t address) // todo: friendly logging - nv_log("PFB Read from 0x%08x", address); + nv_log_verbose_only("PFB Read from 0x%08x", address); // if the register actually exists if (reg) @@ -111,9 +111,9 @@ uint32_t nv3_pfb_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -127,15 +127,15 @@ 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("PFB Write 0x%08x -> 0x%08x", value, address); + nv_log_verbose_only("PFB 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); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) @@ -189,17 +189,16 @@ void nv3_pfb_config0_write(uint32_t val) // This doesn't actually seem very useful - #ifdef ENABLE_NV_LOG_ULTRA - nv_log("Framebuffer Config Change\n"); - nv_log("Horizontal Size=%d pixels\n", new_pfb_htotal); - nv_log("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); + + nv_log_verbose_only("Framebuffer Config Change\n"); + nv_log_verbose_only("Horizontal Size=%d pixels\n", new_pfb_htotal); + nv_log_verbose_only("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) - nv_log("Bit Depth=8bpp\n"); + nv_log_verbose_only("Bit Depth=8bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_16BPP) - nv_log("Bit Depth=16bpp\n"); + nv_log_verbose_only("Bit Depth=16bpp\n"); else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_32BPP) - nv_log("Bit Depth=32bpp\n"); - #endif - + nv_log_verbose_only("Bit Depth=32bpp\n"); + } \ 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 index 55ec21e19..6195c0639 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -101,7 +101,7 @@ uint32_t nv3_pfifo_read(uint32_t address) // todo: friendly logging - nv_log("PFIFO Read from 0x%08x", address); + nv_log_verbose_only("PFIFO Read from 0x%08x", address); // if the register actually exists if (reg) @@ -278,9 +278,9 @@ uint32_t nv3_pfifo_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } /* Handle some special memory areas */ else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) @@ -288,24 +288,26 @@ uint32_t nv3_pfifo_read(uint32_t address) uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; ret = nv3->pfifo.cache1_settings.context[ctx_entry_id]; - nv_log("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x\n", ctx_entry_id, ret); + nv_log_verbose_only("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x\n", ctx_entry_id, ret); } /* Direct cache read stuff */ else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { - nv_log("PFIFO Cache0 Read\n"); + nv_log_verbose_only("PFIFO Cache0 Read\n"); // See if we want the object name or the channel/subchannel information. if (address & 4) { - nv_log("Data=0x%08x\n", nv3->pfifo.cache0_entry.data); + nv_log_verbose_only("Data=0x%08x\n", nv3->pfifo.cache0_entry.data); + return nv3->pfifo.cache0_entry.data; } else { uint32_t final = nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - nv_log("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); + nv_log_verbose_only("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); + return final; } @@ -320,19 +322,19 @@ uint32_t nv3_pfifo_read(uint32_t address) slot = (address >> 3) & 0x3F; else slot = (address >> 3) & 0x1F; - - nv_log("PFIFO Cache1 Read slot=%d", slot); + + nv_log_verbose_only("PFIFO Cache1 Read slot=%d", slot); // See if we want the object name or the channel/subchannel information. if (address & 4) { - nv_log("Data=0x%08x\n", nv3->pfifo.cache1_entries[slot].data); + nv_log_verbose_only("Data=0x%08x\n", nv3->pfifo.cache1_entries[slot].data); return nv3->pfifo.cache1_entries[slot].data; } else { uint32_t final = nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - nv_log("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); + nv_log_verbose_only("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); return final; } @@ -392,7 +394,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); - nv_log("PFIFO Write 0x%08x -> 0x%08x", val, address); + nv_log_verbose_only("PFIFO Write 0x%08x -> 0x%08x", val, address); // if the register actually exists if (reg) @@ -571,18 +573,18 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) } if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) { - nv_log("PFIFO Cache0 Write\n"); + nv_log_verbose_only("PFIFO Cache0 Write\n"); // 3104 always written after 3100 if (address & 4) { - nv_log("Name = 0x%08x\n", val); + nv_log_verbose_only("Name = 0x%08x\n", val); nv3->pfifo.cache0_entry.data = val; nv3_pfifo_cache0_pull(); // immediately pull out } @@ -590,7 +592,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) { nv3->pfifo.cache0_entry.method = (val & 0x1FFC); nv3->pfifo.cache0_entry.subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; - nv_log("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache0_entry.subchannel, nv3->pfifo.cache0_entry.method); + nv_log_verbose_only("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache0_entry.subchannel, nv3->pfifo.cache0_entry.method); } } @@ -606,19 +608,19 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) uint32_t real_entry = nv3_pfifo_cache1_normal2gray(slot); - nv_log("Cache1 Write Slot %d (Gray code)", real_entry); + nv_log_verbose_only("Cache1 Write Slot %d (Gray code)", real_entry); // See if we want the object name or the channel/subchannel information. if (address & 4) { - nv_log("Name = 0x%08x\n", val); + nv_log_verbose_only("Name = 0x%08x\n", val); nv3->pfifo.cache1_entries[real_entry].data = val; } else { nv3->pfifo.cache1_entries[real_entry].method = (val & 0x1FFC); nv3->pfifo.cache1_entries[real_entry].subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; - nv_log("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache1_entries[real_entry].subchannel, nv3->pfifo.cache1_entries[real_entry].method); + nv_log_verbose_only("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache1_entries[real_entry].subchannel, nv3->pfifo.cache1_entries[real_entry].method); } } /* Handle some special memory areas */ @@ -627,7 +629,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; nv3->pfifo.cache1_settings.context[ctx_entry_id] = val; - nv_log("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, val); + nv_log_verbose_only("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, val); } else /* Completely unknown */ { @@ -689,11 +691,10 @@ uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) return nv3_pfifo_cache1_binary_code_table[val]; } -// Submits graphics objects INTO cache0 -void nv3_pfifo_cache0_push() -{ - -} +/* +You can't push into cache0 on the real hardware, but it's not practically done because Cache0 is meant to be reserved for software objects, +NV_USER writes always go to CACHE1 +*/ // Pulls graphics objects OUT of cache0 void nv3_pfifo_cache0_pull() @@ -740,9 +741,9 @@ void nv3_pfifo_cache0_pull() nv3->pfifo.cache0_settings.get_address ^= 0x04; #ifndef RELEASE_BUILD - #ifdef ENABLE_NV_LOG_ULTRA - nv_log("***** DEBUG: CACHE0 PULLED ****** Contextual information below\n"); - #endif + + nv_log_verbose_only("***** DEBUG: CACHE0 PULLED ****** Contextual information below\n"); + nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; @@ -757,7 +758,7 @@ void nv3_pfifo_context_switch(uint32_t new_channel) { /* Send our contexts to RAMFC. Load the new ones from RAMFC. */ if (new_channel >= NV3_DMA_CHANNELS) - fatal("Tried to switch to invalid dma channel"); + fatal("nv3_pfifo_context_switch: Tried to switch to invalid dma channel"); uint16_t ramfc_base = nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS & 0xF; @@ -832,7 +833,8 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) // Did we fuck up? if (oh_shit) { - nv_log("WE ARE FUCKED Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x IMPLEMENT THIS OR DIE!!!", oh_shit_reason, channel, subchannel, method_offset); + nv_log("OH CRAP: Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x", + oh_shit_reason, channel, subchannel, method_offset); nv3_ramro_write(nv3->pfifo.runout_put, new_address); nv3_ramro_write(nv3->pfifo.runout_put + 4, param); @@ -878,7 +880,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; - nv_log("Submitted object [PIO]: Channel %d.%d, Parameter 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", + nv_log_verbose_only("Submitted object [PIO]: Channel %d.%d, Parameter 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", channel, subchannel, param, method_offset, nv3->pfifo.cache1_settings.put_address); // Now we're done. Phew! @@ -918,7 +920,7 @@ void nv3_pfifo_cache1_pull() //bit23 set=hardware if (!(current_context & 0x800000)) { - nv_log("The object in CACHE1 is a software object\n"); + nv_log_verbose_only("The object in CACHE1 is a software object\n"); nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; @@ -939,9 +941,8 @@ void nv3_pfifo_cache1_pull() #ifndef RELEASE_BUILD - #ifdef ENABLE_NV_LOG_ULTRA - nv_log("***** DEBUG: CACHE1 PULLED ****** Contextual information below\n"); - #endif + nv_log_verbose_only("***** DEBUG: CACHE1 PULLED ****** Contextual information below\n"); + nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index ece125b0f..82ad95706 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -108,7 +108,7 @@ uint32_t nv3_pgraph_read(uint32_t address) // todo: friendly logging - nv_log("PGRAPH Read from 0x%08x", address); + nv_log_verbose_only("PGRAPH Read from 0x%08x", address); // if the register actually exists if (reg) @@ -262,9 +262,9 @@ uint32_t nv3_pgraph_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -275,7 +275,7 @@ uint32_t nv3_pgraph_read(uint32_t address) // Addresses should be aligned to 4 bytes. uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - nv_log("PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); + nv_log_verbose_only("PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); } else /* Completely unknown */ { @@ -297,15 +297,11 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) nv_register_t* reg = nv_get_register(address, pgraph_registers, sizeof(pgraph_registers)/sizeof(pgraph_registers[0])); - nv_log("PGRAPH Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("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) @@ -467,6 +463,12 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) } } + + if (reg->friendly_name) + nv_log_verbose_only(": %s\n", reg->friendly_name); + else + nv_log_verbose_only("\n"); + } else { @@ -477,7 +479,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // Addresses should be aligned to 4 bytes. uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - nv_log("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); + nv_log_verbose_only("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); nv3->pgraph.context_cache[entry] = value; } else /* Completely unknown */ @@ -524,7 +526,7 @@ void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channe grobj.grobj_2 = nv3_ramin_read32(real_ramin_base + 8, nv3); grobj.grobj_3 = nv3_ramin_read32(real_ramin_base + 12, nv3); - nv_log("**** About to execute method **** method=0x%04x param=0x%08x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", + nv_log_verbose_only("**** About to execute method **** method=0x%04x param=0x%08x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", method, param, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); /* Methods below 0x104 are shared across all classids, so call generic_method for that*/ diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index de1c21f24..050af07fa 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -60,7 +60,7 @@ nv_register_t pmc_registers[] = { uint32_t nv3_pmc_clear_interrupts() { - nv_log("Clearing IRQs\n"); + nv_log_verbose_only("Clearing IRQs\n"); pci_clear_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); } @@ -144,21 +144,21 @@ uint32_t nv3_pmc_handle_interrupts(bool send_now) { if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_HARDWARE) { - nv_log("Firing hardware-originated interrupt NV3_PMC_INTR_0=0x%08x\n", nv3->pmc.interrupt_status); + nv_log_verbose_only("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("NOT firing hardware-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE HARDWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); + nv_log_verbose_only("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("Firing software-originated interrupt NV3_PMC_INTR_0=0x%08x\n", nv3->pmc.interrupt_status); + nv_log_verbose_only("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("NOT firing software-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE SOFTWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); + nv_log_verbose_only("NOT firing software-originated interrupt NV3_PMC_INTR_0=0x%08x, BECAUSE SOFTWARE INTERRUPTS ARE DISABLED\n", nv3->pmc.interrupt_status); } } @@ -178,7 +178,7 @@ uint32_t nv3_pmc_read(uint32_t address) uint32_t ret = 0x00; // todo: friendly logging - nv_log("PMC Read from 0x%08x", address); + nv_log_verbose_only("PMC Read from 0x%08x", address); // if the register actually exists if (reg) @@ -194,7 +194,7 @@ uint32_t nv3_pmc_read(uint32_t address) ret = nv3->pmc.boot; break; case NV3_PMC_INTERRUPT_STATUS: - nv_log("\n"); // clear_interrupts logs + nv_log_verbose_only("\n"); // clear_interrupts logs nv3_pmc_clear_interrupts(); ret = nv3_pmc_handle_interrupts(false); @@ -211,9 +211,9 @@ uint32_t nv3_pmc_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -227,7 +227,7 @@ 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("PMC Write 0x%08x -> 0x%08x", value, address); + nv_log_verbose_only("PMC Write 0x%08x -> 0x%08x", value, address); // if the register actually exists... if (reg) @@ -245,7 +245,7 @@ void nv3_pmc_write(uint32_t address, uint32_t value) // This can only be done by software interrupts... if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) { - nv_log("Huh? This is a hardware interrupt...Please use the INTR_EN registers of the GPU subsystem you want to trigger " + warning("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; } @@ -264,9 +264,9 @@ void nv3_pmc_write(uint32_t address, uint32_t value) } if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else /* Completely unknown */ diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index d7f6f0cf9..b18797a65 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -49,7 +49,7 @@ uint32_t nv3_pme_read(uint32_t address) // todo: friendly logging - nv_log("PME Read from 0x%08x", address); + nv_log_verbose_only("PME Read from 0x%08x", address); // if the register actually exists if (reg) @@ -77,9 +77,9 @@ uint32_t nv3_pme_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -93,15 +93,15 @@ 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("PME Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("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); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index fb83ffaf1..0e41807a0 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -223,7 +223,7 @@ uint32_t nv3_pramdac_read(uint32_t address) // todo: friendly logging - nv_log("PRAMDAC Read from 0x%08x\n", address); + nv_log_verbose_only("PRAMDAC Read from 0x%08x\n", address); // if the register actually exists if (reg) @@ -288,9 +288,9 @@ uint32_t nv3_pramdac_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -304,15 +304,12 @@ 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("PRAMDAC Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("PRAMDAC 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) @@ -381,6 +378,11 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) break; } } + + if (reg->friendly_name) + nv_log_verbose_only(": %s\n", reg->friendly_name); + else + nv_log_verbose_only("\n"); } else /* Completely unknown */ { diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 2771d5d16..8556822f5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -61,7 +61,7 @@ uint8_t nv3_ramin_read8(uint32_t addr, void* priv) if (!nv3_ramin_arbitrate_read(addr, &val)) // Oh well { val = (uint8_t)nv3->nvbase.svga.vram[addr]; - nv_log("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log_verbose_only("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); } return (uint8_t)val; @@ -87,7 +87,7 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) if (!nv3_ramin_arbitrate_read(addr, &val)) { val = (uint16_t)vram_16bit[addr]; - nv_log("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + nv_log_verbose_only("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); } return val; @@ -114,7 +114,7 @@ uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { val = vram_32bit[addr]; - nv_log("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); + nv_log_verbose_only("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); } return val; @@ -139,7 +139,7 @@ void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val32)) { nv3->nvbase.svga.vram[addr] = val; - nv_log("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log_verbose_only("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); } @@ -165,7 +165,7 @@ void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val32)) { vram_16bit[addr] = val; - nv_log("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log_verbose_only("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); } @@ -189,7 +189,7 @@ void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) if (!nv3_ramin_arbitrate_write(addr, val)) { vram_32bit[addr] = val; - nv_log("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); + nv_log_verbose_only("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); } } @@ -378,7 +378,7 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u // Why does this work? uint32_t ramht_cur_address = ramht_base + (nv3_ramht_hash(name, channel) * bucket_entries * 8); - nv_log("Beginning search for graphics object at RAMHT base=0x%04x, name=0x%08x, Cache%d, channel=%d.%d)\n", + nv_log_verbose_only("Beginning search for graphics object at RAMHT base=0x%04x, name=0x%08x, Cache%d, channel=%d.%d)\n", ramht_cur_address, name, cache_num, channel, subchannel); bool found_object = false; @@ -503,13 +503,13 @@ bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, u void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) { #ifndef RELEASE_BUILD - nv_log("Found object:\n"); - nv_log("Param: 0x%04x\n", name); + nv_log_verbose_only("Found object:\n"); + nv_log_verbose_only("Param: 0x%04x\n", name); - nv_log("Context:\n"); - nv_log("DMA Channel %d (0-7 valid)\n", context.channel); - nv_log("Class ID: =0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); - nv_log("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); - nv_log("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); + nv_log_verbose_only("Context:\n"); + nv_log_verbose_only("DMA Channel %d (0-7 valid)\n", context.channel); + nv_log_verbose_only("Class ID: =0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); + nv_log_verbose_only("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); + nv_log_verbose_only("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); #endif } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index fa0d5899c..51d33c9a6 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -30,10 +30,10 @@ uint32_t nv3_ramfc_read(uint32_t address) { - nv_log("RAMFC (Unused DMA channel context) Read (0x%04x)), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); + nv_log_verbose_only("RAMFC (Unused DMA channel context) Read (0x%04x)\n", address); } void nv3_ramfc_write(uint32_t address, uint32_t value) { - nv_log("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x)), I DON'T BELIEVE THIS SHOULD EVER HAPPEN\n", value, address); + nv_log_verbose_only("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x)\n", value, address); } \ 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 index cce1d74a9..d2d17d321 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -39,17 +39,17 @@ uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) // is this the right endianness? - nv_log("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, (hash/8), name, channel); + nv_log_verbose_only("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, (hash/8), name, channel); return hash; } uint32_t nv3_ramht_read(uint32_t address) { - nv_log("RAMHT (Graphics object storage hashtable) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); + nv_log_verbose_only("RAMHT (Graphics object storage hashtable) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); } void nv3_ramht_write(uint32_t address, uint32_t value) { - nv_log("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - UNIMPLEMENTED\n", value, address); + nv_log_verbose_only("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - UNIMPLEMENTED\n", value, address); } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 1209faa2f..692a10e91 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -30,15 +30,10 @@ uint32_t nv3_ramro_read(uint32_t address) { - nv_log("RAM Runout (invalid dma object submission) Read (0x%04x), Probably shouldn't happenn\n", address); + nv_log("BIG Problem: RAM Runout (invalid dma object submission) Read (0x%04x)\n", address); } void nv3_ramro_write(uint32_t address, uint32_t value) { - nv_log("RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x), Probably shouldn't happenn", value, address); -} - -void nv3_ramro_send() -{ - + nv_log("BIG Problem: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x)", value, address); } \ 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 index c1b8a2576..3a882b5b8 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -82,7 +82,7 @@ void nv3_ptimer_tick(double real_time) // Only log on ptimer alarm. Otherwise, it's too much spam. if (nv3->ptimer.time >= nv3->ptimer.alarm) { - nv_log("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); + nv_log_verbose_only("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); } } @@ -97,7 +97,7 @@ uint32_t nv3_ptimer_read(uint32_t address) if (address != NV3_PTIMER_TIME_0_NSEC && address != NV3_PTIMER_TIME_1_NSEC) { - nv_log("PTIMER Read from 0x%08x", address); + nv_log_verbose_only("PTIMER Read from 0x%08x", address); } uint32_t ret = 0x00; @@ -147,9 +147,9 @@ uint32_t nv3_ptimer_read(uint32_t address) && reg->address != NV3_PTIMER_TIME_1_NSEC) { if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } } else @@ -165,15 +165,15 @@ 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("PTIMER Write 0x%08x -> 0x%08x", value, address); + nv_log_verbose_only("PTIMER Write 0x%08x -> 0x%08x", value, address); // if the register actually exists if (reg) { if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index 6f72da719..c883ee7e5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -55,15 +55,15 @@ uint32_t nv3_pvideo_read(uint32_t address) // todo: friendly logging - nv_log("PVIDEO Read from 0x%08x", address); + nv_log_verbose_only("PVIDEO Read from 0x%08x", address); // if the register actually exists if (reg) { if (reg->friendly_name) - nv_log(": %s\n", reg->friendly_name); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_read) @@ -95,9 +95,9 @@ uint32_t nv3_pvideo_read(uint32_t address) } if (reg->friendly_name) - nv_log(": 0x%08x <- %s\n", ret, reg->friendly_name); + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); } else { @@ -112,15 +112,15 @@ 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("PVIDEO Write 0x%08x -> 0x%08x\n", value, address); + nv_log_verbose_only("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); + nv_log_verbose_only(": %s\n", reg->friendly_name); else - nv_log("\n"); + nv_log_verbose_only("\n"); // on-read function if (reg->on_write) diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c index 99091b5b7..f10e28793 100644 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ b/src/video/nv/nv3/subsystems/nv3_user.c @@ -40,8 +40,7 @@ uint32_t nv3_user_read(uint32_t address) uint8_t channel = (address - NV3_USER_START) / 0x10000; uint8_t subchannel = ((address - NV3_USER_START)) / 0x2000 % NV3_DMA_SUBCHANNELS_PER_CHANNEL; - nv_log("User Submission Area PIO Channel %d.%d method_offset=0x%04x\n", channel, subchannel, method_offset); - + nv_log_verbose_only("User Submission Area PIO Channel %d.%d method_offset=0x%04x\n", channel, subchannel, method_offset); // 0x10 is free CACHE1 object // TODO: THERE ARE OTHER STUFF! @@ -54,8 +53,7 @@ uint32_t nv3_user_read(uint32_t address) } - nv_log("IT'S NOT IMPLEMENTED!!!! offset=0x%04x\n", method_offset); - + nv_log("NV_USER READ: Channel FIELD NOT IMPLEMENTED!!!! offset=0x%04x\n", method_offset); return 0x00; }; diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index 185c46183..bce7425c2 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -50,31 +50,52 @@ void nv_log_set_device(void* device) nv_log_device = device; } -void nv_log(const char *fmt, ...) +void nv_log_internal(const char* fmt, va_list arg) { if (!nv_log_device) return; - va_list ap; - - if (nv_do_log) { - va_start(ap, fmt); - // If our debug config option is configured, full log. Otherwise log with cyclical detection. - #ifndef RELEASE_BUILD - if (nv_log_full) - log_out(nv_log_device, fmt, ap); - else - #endif - log_out_cyclic(nv_log_device, fmt, ap); - - va_end(ap); - } + if (nv_log_full) + log_out(nv_log_device, fmt, arg); + else + log_out_cyclic(nv_log_device, fmt, arg); + } + +void nv_log(const char *fmt, ...) +{ + va_list arg; + + if (!nv_do_log) + return; + + va_start(arg, fmt); + nv_log_internal(fmt, arg); + va_end(arg); +} + +void nv_log_verbose_only(const char *fmt, ...) +{ + #ifdef ENABLE_NV_LOG_ULTRA + if (!nv_do_log) + return; + + va_start(arg, fmt); + nv_log_internal(fmt, arg); + va_end(arg); + #endif +} + #else void nv_log(const char *fmt, ...) { + +} +void nv_log_verbose_only(const char *fmt, ...) +{ + } void nv_log_set_device(void* device) From 041dc284929b1bf5431011920493f1d723dec68e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 5 Apr 2025 14:44:01 +0100 Subject: [PATCH 158/274] Split src/destination buffers, use grobj --- CMakeLists.txt | 2 +- src/include/86box/nv/render/vid_nv3_render.h | 6 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- .../classes/nv3_class_01c_image_in_memory.c | 2 +- src/video/nv/nv3/render/nv3_render_blit.c | 77 ++++++------------- src/video/nv/nv3/render/nv3_render_core.c | 36 +++++++-- 6 files changed, 58 insertions(+), 67 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1adcf4740..52f24f4b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ option(DEV_BRANCH "Development branch" option(DISCORD "Discord Rich Presence support" ON) option(DEBUGREGS486 "Enable debug register opeartion on 486+ CPUs" OFF) # Remove when merged, should just be -D -option(NV_LOG "NVidia RIVA 128 debug logging" OFF) +option(NV_LOG "NVidia RIVA 128 debug logging" ON) option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) if (NV_LOG) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 9b5acd06d..00591e342 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -19,9 +19,9 @@ /* Core */ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj); -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj); +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 7bae42184..57184054d 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -54,7 +54,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ && nv3->pgraph.blit.point_in.y == nv3->pgraph.blit.point_out.y) return; - //nv3_render_blit_screen2screen(grobj); + nv3_render_blit_screen2screen(grobj); break; default: diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index dd5768103..af488dc09 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -31,7 +31,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { /* We need this for a lot of methods, so may as well store it here. */ - uint32_t src_buffer_id = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + uint32_t src_buffer_id = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; switch (method_id) { diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 032b23b47..3059aeccd 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -113,66 +113,37 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { - //nv3_position_16_t old_position = nv3->pgraph.blit.point_in + nv3->pgraph.blit.size.w; - nv3_position_16_t old_position = {0}; - old_position.x = nv3->pgraph.blit.point_in.x + nv3->pgraph.blit.size.w; - old_position.y = nv3->pgraph.blit.point_in.y + nv3->pgraph.blit.size.h; + nv3_position_16_t old_position = nv3->pgraph.blit.point_in; nv3_position_16_t new_position = nv3->pgraph.blit.point_out; uint16_t end_x = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); uint16_t end_y = (nv3->pgraph.blit.point_out.y + nv3->pgraph.blit.size.h); - /* Read the old pixel */ - switch (nv3->nvbase.svga.bpp) + uint32_t pixel_to_copy = 0x00; + + /* Read the old pixel and rewrite it to the new position */ + for (int32_t y = nv3->pgraph.blit.point_out.y; y <= end_y; y++) { - case 8: //8bpp - for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) + old_position.y = new_position.y = y; + + for (int32_t x = nv3->pgraph.blit.point_out.x; x <= end_x; x++) + { + old_position.x = new_position.x = x; + + switch (nv3->nvbase.svga.bpp) { - old_position.y++; - new_position.y++; - - for (int32_t x = nv3->pgraph.blit.point_out.x; x < end_x; x++) - { - old_position.x++; - new_position.x++; - - uint32_t pixel_to_copy = nv3_render_read_pixel_8(old_position, grobj) & 0xFF; - nv3_render_write_pixel(new_position, pixel_to_copy, grobj); - } + case 8: + pixel_to_copy = nv3_render_read_pixel_8(old_position, grobj, false) & 0xFF; + break; + case 15 ... 16: //15bpp and 16bpp modes are considered as identical + pixel_to_copy = nv3_render_read_pixel_16(old_position, grobj, false) & 0xFFFF; + break; + case 32: + pixel_to_copy = nv3_render_read_pixel_32(old_position, grobj, false); + break; } - break; - case 15: - case 16: //16bpp - for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) - { - old_position.y++; - new_position.y++; - for (int32_t x = nv3->pgraph.blit.point_out.x; x >= end_x; x++) - { - old_position.x++; - new_position.x++; - - uint32_t pixel_to_copy = nv3_render_read_pixel_16(old_position, grobj) & 0xFFFF; - nv3_render_write_pixel(new_position, pixel_to_copy, grobj); - } - } - break; - case 32: //32bpp - for (int32_t y = nv3->pgraph.blit.point_out.y; y < end_y; y++) - { - old_position.y++; - new_position.y++; - - for (int32_t x = nv3->pgraph.blit.point_out.x; x >= end_x; x++) - { - old_position.x++; - new_position.x++; - - uint32_t pixel_to_copy = nv3_render_read_pixel_32(old_position, grobj); - nv3_render_write_pixel(new_position, pixel_to_copy, grobj); - } - } - break; - } + nv3_render_write_pixel(new_position, pixel_to_copy, grobj); + } + } } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 66ee170bc..9781259ad 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -213,11 +213,31 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool } /* /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ -uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj) +uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + + /* test DST_BUFFER code + I assume for 2d at least only one is allowed at a time + */ + + if (use_destination) + { + uint32_t destination_buffer = 5; // 5 = just use the source buffer + + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) destination_buffer = 0; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) destination_buffer = 1; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) destination_buffer = 2; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) destination_buffer = 3; + + if (destination_buffer != current_buffer + && destination_buffer != 5) + current_buffer = destination_buffer; + + } + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // we have to multiply the x position by the number of bytes per pixel @@ -242,19 +262,19 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro } /* Read an 8bpp pixel from the framebuffer. */ -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj) +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); return nv3->nvbase.svga.vram[vram_address]; } /* Read an 16bpp pixel from the framebuffer. */ -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); vram_address >>= 1; //convert to 16bit pointer @@ -263,10 +283,10 @@ uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) } /* Read an 16bpp pixel from the framebuffer. */ -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); vram_address >>= 1; //convert to 16bit pointer @@ -315,7 +335,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob if (!nv3_render_chroma_test(color, grobj)) return; - uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); + uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj, true); uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; uint8_t bit = 0x00; From c05098e9ea44fbef0603fa3ba9e97bfb83b386d6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 6 Apr 2025 02:53:02 +0100 Subject: [PATCH 159/274] The worst ever s2sb implementation in human history but at least it works. Why are the buffers set to the same place --- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- .../nv/nv3/classes/nv3_class_011_image.c | 4 +- src/video/nv/nv3/render/nv3_render_blit.c | 59 +++++++++++++++---- src/video/nv/nv3/render/nv3_render_core.c | 2 +- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 57184054d..7b752243f 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -47,7 +47,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* This is the last one*/ nv3->pgraph.blit.size.w = (param & 0xFFFF); nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB Size %04x,%04x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); + nv_log("Method Execution: S2SB Size %04x,%04x grobj_0=0x%08x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y, grobj.grobj_0); /* Some blits have (point_in == point_out) ???? */ if (nv3->pgraph.blit.point_in.x == nv3->pgraph.blit.point_out.x diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index c1d1200b9..bd5dfa6d3 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -28,8 +28,6 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> - - void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { switch (method_id) @@ -55,7 +53,7 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) { uint32_t pixel_slot = (method_id - NV3_IMAGE_COLOR_START) >> 2; - nv_log("Method Execution: Pixel%d Colour%08x Format%x\n", pixel_slot, param, (grobj.grobj_0) & 0x07); + nv_log("Method Execution: Image Pixel%d Colour%08x Format%x\n", pixel_slot, param, (grobj.grobj_0) & 0x07); nv3_render_blit_image(param, grobj); } else diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 3059aeccd..46556f928 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -108,27 +108,47 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) break; } - } + +#define NV3_MAX_HORIZONTAL_SIZE 1920 +#define NV3_MAX_VERTICAL_SIZE 1200 + +/* 1920 for margin. Holds a buffer of the old screen we want to hold so we don't overwrite things we already overwtote +We only need to clear it once per blit, because the blits are always the same size, and then only for the size of our new blit + +Extremely not crazy about this...Surely a better way to do it without buffering the ENTIRE SCREEN. I only update the parts that are needed, but still... + +This is LUDICROUSLY INEFFICIENT (2*O(n^2)) and COMPLETELY TERRIBLE code, but it's currently 2:48am so I can't think of a better approach... +*/ +uint32_t nv3_s2sb_line_buffer[NV3_MAX_HORIZONTAL_SIZE*NV3_MAX_VERTICAL_SIZE] = {0}; + void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { + if (nv3->pgraph.blit.size.w < NV3_MAX_HORIZONTAL_SIZE + && nv3->pgraph.blit.size.h < NV3_MAX_VERTICAL_SIZE) + memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.h) * (sizeof(uint32_t) * nv3->pgraph.blit.size.w)); + nv3_position_16_t old_position = nv3->pgraph.blit.point_in; nv3_position_16_t new_position = nv3->pgraph.blit.point_out; - uint16_t end_x = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); + uint16_t end_x_in = (nv3->pgraph.blit.point_in.x + nv3->pgraph.blit.size.w); /* needed for bounds checking */ + uint16_t end_x_out = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); uint16_t end_y = (nv3->pgraph.blit.point_out.y + nv3->pgraph.blit.size.h); uint32_t pixel_to_copy = 0x00; - /* Read the old pixel and rewrite it to the new position */ - for (int32_t y = nv3->pgraph.blit.point_out.y; y <= end_y; y++) - { - old_position.y = new_position.y = y; + /* Prevents overwriting pixels we've already modified*/ + uint32_t xdiff = 0, ydiff = 0; - for (int32_t x = nv3->pgraph.blit.point_out.x; x <= end_x; x++) + /* Read the old pixel into the line buffer */ + for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) + { + old_position.y = nv3->pgraph.blit.point_in.y + y; + + for (int32_t x = 0; x < nv3->pgraph.blit.size.w; x++) { - old_position.x = new_position.x = x; + old_position.x = nv3->pgraph.blit.point_in.x + x; switch (nv3->nvbase.svga.bpp) { @@ -143,7 +163,26 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) break; } - nv3_render_write_pixel(new_position, pixel_to_copy, grobj); + uint32_t buf_position = (y * nv3->pgraph.blit.size.w) + x; + nv3_s2sb_line_buffer[buf_position] = pixel_to_copy; } - } + + + } + /* simply write it all back to vram */ + for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) + { + new_position.y = nv3->pgraph.blit.point_out.y + y; + + for (int32_t x = 0; x < nv3->pgraph.blit.size.w; x++) + { + new_position.x = nv3->pgraph.blit.point_out.x + x; + + uint32_t buf_position = (y * nv3->pgraph.blit.size.w) + x; + + nv3_render_write_pixel(new_position, nv3_s2sb_line_buffer[buf_position], grobj); + } + } + + } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 9781259ad..e9ebeb013 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -289,7 +289,7 @@ uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); - vram_address >>= 1; //convert to 16bit pointer + vram_address >>= 2; //convert to 32bit pointer return vram_32[vram_address]; } From c9f6f87fccfad5fa95230576cf393c3854926fc1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 6 Apr 2025 14:03:09 +0100 Subject: [PATCH 160/274] much less idiotic... --- src/include/86box/nv/render/vid_nv3_render.h | 1 + src/include/86box/nv/vid_nv3.h | 2 +- .../classes/nv3_class_00c_win95_gdi_text.c | 2 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 5 ++ src/video/nv/nv3/render/nv3_render_blit.c | 61 ++++++++----------- src/video/nv/nv3/render/nv3_render_core.c | 3 +- 6 files changed, 33 insertions(+), 41 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 00591e342..048f3089a 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -22,6 +22,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); +uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 411ccdecf..dc6249346 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -14,7 +14,7 @@ * Also check the doc folder for some more notres * * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 2 April 2025 (STILL WORKING ON IT!!!) + * Last updated: 6 April 2025 (STILL WORKING ON IT!!!) * * Authors: Connor Hyde * diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index e2f640fac..c1085fe89 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -278,7 +278,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ return; } - nv_log("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); + warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; } diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 7b752243f..6582ada13 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -54,6 +54,11 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ && nv3->pgraph.blit.point_in.y == nv3->pgraph.blit.point_out.y) return; + /* Some of these seem to have sizes of 0, so skip */ + if (nv3->pgraph.blit.size.h == 0 + && nv3->pgraph.blit.size.w == 0) + return; + nv3_render_blit_screen2screen(grobj); break; diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 46556f928..ad8c0e3f9 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -138,51 +138,38 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) uint32_t pixel_to_copy = 0x00; - /* Prevents overwriting pixels we've already modified*/ - uint32_t xdiff = 0, ydiff = 0; + /* Coordinates for copying an entire line at a time */ + uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.w; + + /* Read the old pixel into the line buffer + Assumption: All data is sent in an unpacked format. In the case of an NVIDIA GPU this means that all data is sent 32 bits at a time regardless of if + the actual source data is 32 bits in size or not. For pixel data, the upper bits are left as 0 in 8bpp/16bpp mode. For 86box purposes, the data is written + 8/16 bits at a time. + + TODO: CHECK FOR PACKED FORMAT!!!!! + */ + + if (nv3->nvbase.svga.bpp == 15 + || nv3->nvbase.svga.bpp == 16) + size_x <<= 1; + else if (nv3->nvbase.svga.bpp == 32) + size_x <<= 2; - /* Read the old pixel into the line buffer */ for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { old_position.y = nv3->pgraph.blit.point_in.y + y; + /* 32bit buffer */ + buf_position = (nv3->pgraph.blit.size.w * y); + vram_position = nv3_render_get_vram_address(old_position, grobj, false); - for (int32_t x = 0; x < nv3->pgraph.blit.size.w; x++) - { - old_position.x = nv3->pgraph.blit.point_in.x + x; - - switch (nv3->nvbase.svga.bpp) - { - case 8: - pixel_to_copy = nv3_render_read_pixel_8(old_position, grobj, false) & 0xFF; - break; - case 15 ... 16: //15bpp and 16bpp modes are considered as identical - pixel_to_copy = nv3_render_read_pixel_16(old_position, grobj, false) & 0xFFFF; - break; - case 32: - pixel_to_copy = nv3_render_read_pixel_32(old_position, grobj, false); - break; - } - - uint32_t buf_position = (y * nv3->pgraph.blit.size.w) + x; - nv3_s2sb_line_buffer[buf_position] = pixel_to_copy; - } - - + memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); } /* simply write it all back to vram */ for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) - { + { + buf_position = (nv3->pgraph.blit.size.w * y); new_position.y = nv3->pgraph.blit.point_out.y + y; - - for (int32_t x = 0; x < nv3->pgraph.blit.size.w; x++) - { - new_position.x = nv3->pgraph.blit.point_out.x + x; - - uint32_t buf_position = (y * nv3->pgraph.blit.size.w) + x; - - nv3_render_write_pixel(new_position, nv3_s2sb_line_buffer[buf_position], grobj); - } + vram_position = nv3_render_get_vram_address(new_position, grobj, false); + memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); } - - } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index e9ebeb013..dca2a6a6c 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -212,7 +212,7 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool } -/* /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ +/* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { uint32_t vram_x = position.x; @@ -309,7 +309,6 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z - uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; /* doesn't seem*/ nv3_color_argb_t color_data = *(nv3_color_argb_t*)&color; From c1506772dec8299b5d4a3f64f32f3415b5bb2f83 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 6 Apr 2025 18:08:42 +0100 Subject: [PATCH 161/274] fix crossbuffer blit, use custom render functions --- .../86box/nv/classes/vid_nv3_classes.h | 4 +- src/include/86box/nv/render/vid_nv3_render.h | 3 + src/include/86box/nv/vid_nv.h | 1 + src/include/86box/nv/vid_nv3.h | 4 +- .../classes/nv3_class_01c_image_in_memory.c | 9 +- src/video/nv/nv3/nv3_core.c | 15 +- src/video/nv/nv3/render/nv3_render_blit.c | 3 +- src/video/nv/nv3/render/nv3_render_core.c | 161 ++++++++++++++++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 4 +- 9 files changed, 186 insertions(+), 18 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index e3a1bf7aa..3b6eb1411 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -270,8 +270,8 @@ typedef struct nv3_color_argb_s /* Generic 16-bit position*/ typedef struct nv3_position_16_s { - uint16_t y; uint16_t x; + uint16_t y; } nv3_position_16_t; /* A big position format with 30:16 = y, 15:11 = nothing, 10:0 = x */ @@ -287,8 +287,8 @@ typedef struct nv3_position_16_bigy_s /* Generic 16-bit size */ typedef struct nv3_size_16_s { - uint16_t h; uint16_t w; + uint16_t h; } nv3_size_16_t; /* Generic 32-bit colour + 16-bit position */ diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 048f3089a..567cf3cd4 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,6 +18,9 @@ #pragma once /* Core */ +void nv3_render_15bpp(svga_t *svga); +void nv3_render_16bpp(svga_t *svga); +void nv3_render_32bpp(svga_t *svga); void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index c26c51868..0b314d5f3 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -121,6 +121,7 @@ typedef struct nv_base_s bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID + uint32_t last_buffer_address; // Last buffer address. } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index dc6249346..482315e32 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1194,8 +1194,8 @@ typedef struct nv3_pgraph_s nv3_pgraph_dma_settings_t dma_settings; uint8_t rop; // Current GDI Ternary Render Operation // SURFACE STUFF - PGRAPH CAN OPERATE ON 4 SURFACES/BUFFERS AT A TIME - uint32_t boffset[NV3_PGRAPH_MAX_BUFFERS]; // 22-bit linear VRAM offset for the start of a surface. - uint16_t bpitch[NV3_PGRAPH_MAX_BUFFERS]; // 12-bit linear VRAM offset for the pitch of a surfac.e + uint32_t boffset[NV3_PGRAPH_MAX_BUFFERS]; // 22-bit linear VRAM offset for the start of a buffer. + uint16_t bpitch[NV3_PGRAPH_MAX_BUFFERS]; // 12-bit linear VRAM offset for the pitch of a buffer uint32_t bpixel[NV3_PGRAPH_MAX_BUFFERS]; // Pixel format for each possible surfaces. // CLIP nv3_pgraph_clip_misc_settings_t clip_misc_settings; diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index af488dc09..9e6a55310 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -77,8 +77,13 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; /* Byte offset in GPU VRAM of top left pixel (22:0) */ case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: - nv3->pgraph.boffset[src_buffer_id] = param & ((1 << NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END) - 0x10); - + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX + nv3->pgraph.boffset[src_buffer_id] = param & 0x7FFFFF; + else + nv3->pgraph.boffset[src_buffer_id] = param & 0x3FFFFF; + + nv3->nvbase.last_buffer_address = nv3->pgraph.boffset[src_buffer_id]; + nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; default: diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 610f8c500..78592160a 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -523,30 +523,27 @@ void nv3_recalc_timings(svga_t* svga) break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: /* This is some sketchy shit that is an attempt at an educated guess - at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, this is what's causing it */ + at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, + this is what's causing it. Possibly fucking *ReactOS* of all things */ if ((svga->crtc[NV3_CRTC_REGISTER_VRETRACESTART] >> 1) & 0x01) - { svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; - } else - { svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; - } + /* sometimes it really renders in 15bpp, so you need to do this */ if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) { svga->bpp = 16; svga->lowres = 0; - svga->render = svga_render_16bpp_highres; + svga->render = nv3_render_16bpp; } else { svga->bpp = 15; svga->lowres = 0; - svga->render = svga_render_15bpp_highres; + svga->render = nv3_render_15bpp; - // fixes win2000, but breaks 9x?! } break; @@ -555,7 +552,7 @@ void nv3_recalc_timings(svga_t* svga) svga->bpp = 32; svga->lowres = 0; - svga->render = svga_render_32bpp_highres; + svga->render = nv3_render_32bpp; break; } diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index ad8c0e3f9..666a93482 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -164,12 +164,13 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); } + /* simply write it all back to vram */ for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { buf_position = (nv3->pgraph.blit.size.w * y); new_position.y = nv3->pgraph.blit.point_out.y + y; - vram_position = nv3_render_get_vram_address(new_position, grobj, false); + vram_position = nv3_render_get_vram_address(new_position, grobj, true); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); } } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index dca2a6a6c..147ac8f6e 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -436,3 +436,164 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob break; } } + + +/* + Modified SVGA Core functions + Done, to facilitate the buffer changing around stuff. + + TODO: Do we need a custom 8bpp function? +*/ + +void nv3_render_15bpp(svga_t *svga) +{ + int x; + uint32_t *p; + uint32_t dat; + uint32_t changed_addr; + uint32_t addr; + + /* "force_old_addr" code path removed, since we don't need it */ + + if ((svga->displine + svga->y_add) < 0) + return; + + changed_addr = svga->remap_func(svga, svga->ma); + + if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { + p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + + if (svga->firstline_draw == 2000) + svga->firstline_draw = svga->displine; + svga->lastline_draw = svga->displine; + + if (!svga->remap_required) { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 8) { + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1)) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); + *p++ = svga->conv_16to32(svga, dat >> 16, 15); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 4) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); + *p++ = svga->conv_16to32(svga, dat >> 16, 15); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 8) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); + *p++ = svga->conv_16to32(svga, dat >> 16, 15); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 12) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); + *p++ = svga->conv_16to32(svga, dat >> 16, 15); + } + svga->ma += x << 1; + } else { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 2) { + addr = svga->remap_func(svga, svga->ma); + dat = *(uint32_t *) (&svga->vram[(addr) & svga->vram_display_mask]); + + *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); + *p++ = svga->conv_16to32(svga, dat >> 16, 15); + svga->ma += 4; + } + } + svga->ma &= svga->vram_display_mask; + } +} + + +void nv3_render_16bpp(svga_t *svga) +{ + int x; + uint32_t *p; + uint32_t dat; + uint32_t changed_addr; + uint32_t addr; + + if ((svga->displine + svga->y_add) < 0) + return; + + changed_addr = svga->remap_func(svga, svga->ma); + + /* "force_old_addr" code path removed, since we don't need it */ + if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { + p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + + if (svga->firstline_draw == 2000) + svga->firstline_draw = svga->displine; + svga->lastline_draw = svga->displine; + + if (!svga->remap_required) { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 8) { + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1)) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); + *p++ = svga->conv_16to32(svga, dat >> 16, 16); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 4) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); + *p++ = svga->conv_16to32(svga, dat >> 16, 16); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 8) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); + *p++ = svga->conv_16to32(svga, dat >> 16, 16); + + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 12) & svga->vram_display_mask]); + *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); + *p++ = svga->conv_16to32(svga, dat >> 16, 16); + } + svga->ma += x << 1; + } else { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 2) { + addr = svga->remap_func(svga, svga->ma); + dat = *(uint32_t *) (&svga->vram[(addr) & svga->vram_display_mask]); + + *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); + *p++ = svga->conv_16to32(svga, dat >> 16, 16); + + svga->ma += 4; + } + } + svga->ma &= svga->vram_display_mask; + } +} + + +void nv3_render_32bpp(svga_t *svga) +{ + int x; + uint32_t *p; + uint32_t dat; + uint32_t changed_addr; + uint32_t addr; + + if ((svga->displine + svga->y_add) < 0) + return; + + /* "force_old_addr" code path removed, since we don't need it */ + + changed_addr = svga->remap_func(svga, svga->ma); + + if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { + p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + + if (svga->firstline_draw == 2000) + svga->firstline_draw = svga->displine; + svga->lastline_draw = svga->displine; + + if (!svga->remap_required) { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x++) { + dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 2)) & svga->vram_display_mask]); + *p++ = svga_lookup_lut_ram(svga, dat & 0xffffff); + } + svga->ma += (x * 4); + } else { + for (x = 0; x <= (svga->hdisp + svga->scrollcache); x++) { + addr = svga->remap_func(svga, svga->ma); + dat = *(uint32_t *) (&svga->vram[addr & svga->vram_display_mask]); + *p++ = svga_lookup_lut_ram(svga, dat & 0xffffff); + + svga->ma += 4; + } + } + svga->ma &= svga->vram_display_mask; + } +} diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 6195c0639..ea39b9135 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -873,7 +873,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_index); next_put_address++; - if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# + if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX# next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); else next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); @@ -931,7 +931,7 @@ void nv3_pfifo_cache1_pull() // start by incrementing uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_index) + 1; - if (nv3->nvbase.gpu_revision >= NV3_BOOT_REG_REV_C00) // RIVA 128ZX# + if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); else next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); From 911a71c67fbe2b6209bae7d957563f7a51c8aeaf Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 9 Apr 2025 21:29:46 +0100 Subject: [PATCH 162/274] very large rewrite. use custom rendering code (for both DFB and GPU) to allow sub-scanline switches; implement a real pixel clock; only blit stuff that actually changed; add void to functions for actions (this may break some things) --- src/include/86box/nv/render/vid_nv3_render.h | 10 +- src/include/86box/nv/vid_nv.h | 9 +- src/include/86box/nv/vid_nv3.h | 34 +- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- src/video/nv/nv3/nv3_core.c | 32 +- src/video/nv/nv3/render/nv3_render_blit.c | 50 +++ src/video/nv/nv3/render/nv3_render_core.c | 373 ++++++++++++------ .../nv/nv3/render/nv3_render_primitives.c | 3 + src/video/nv/nv3/subsystems/nv3_pramdac.c | 17 + 9 files changed, 378 insertions(+), 152 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 567cf3cd4..11f4d2368 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,13 +18,17 @@ #pragma once /* Core */ -void nv3_render_15bpp(svga_t *svga); -void nv3_render_16bpp(svga_t *svga); -void nv3_render_32bpp(svga_t *svga); +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_current_bpp_dfb_8(uint32_t address); +void nv3_render_current_bpp_dfb_16(uint32_t address); +void nv3_render_current_bpp_dfb_32(uint32_t address); + void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); + + uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 0b314d5f3..ef5e80d71 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -72,6 +72,9 @@ void nv_log_verbose_only(const char *fmt, ...); #define NV_ARCHITECTURE_NV3 3 // Riva 128 #define NV_ARCHITECTURE_NV4 4 // Riva TNT and later +#define NV_MAX_BUF_SIZE_X 1920 // Maximum buffer size, X +#define NV_MAX_BUF_SIZE_Y 1200 // Maximum buffer size, Y + typedef enum nv_bus_generation_e { // NV1 - Prototype version @@ -113,7 +116,9 @@ typedef struct nv_base_s 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) uint32_t gpu_revision; // GPU Stepping - double pixel_clock_frequency; // Frequency used for pixel clock + double pixel_clock_frequency; // Frequency used for pixel clock# + double refresh_time; // Rough estimation of refresh rate, for when we can present the screen + double refresh_clock; // Time since the last refresh rivatimer_t* pixel_clock_timer; // Timer for measuring pixel clock bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_frequency; // Source Frequency for PTIMER @@ -139,7 +144,7 @@ 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 + uint32_t (*on_read)(void); // Optional on-read function void (*on_write)(uint32_t value); // Optional on-write fucntion } nv_register_t; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 482315e32..f11a4cdfc 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1457,7 +1457,7 @@ void nv3_recalc_timings(svga_t* svga); void nv3_force_redraw(void* priv); /* BAR0 GPU MMIO read */ -void nv3_update_mappings(); // Update memory mappings +void nv3_update_mappings(void); // Update memory 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 @@ -1556,12 +1556,12 @@ void nv3_user_write(uint32_t address, uint32_t value); // GPU subsystems // NV3 PMC -void nv3_pmc_init(); -uint32_t nv3_pmc_clear_interrupts(); +void nv3_pmc_init(void); +uint32_t nv3_pmc_clear_interrupts(void); uint32_t nv3_pmc_handle_interrupts(bool send_now); // NV3 PGRAPH -void nv3_pgraph_init(); +void nv3_pgraph_init(void); uint32_t nv3_pgraph_read(uint32_t address); void nv3_pgraph_write(uint32_t address, uint32_t value); void nv3_pgraph_vblank_start(svga_t* svga); @@ -1599,46 +1599,46 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_c void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context,nv3_grobj_t grobj); // NV3 PFIFO -void nv3_pfifo_init(); +void nv3_pfifo_init(void); uint32_t nv3_pfifo_read(uint32_t address); void nv3_pfifo_write(uint32_t address, uint32_t value); void nv3_pfifo_interrupt(uint32_t id, bool fire_now); // NV3 PFIFO - Caches //cache0_push not a thing -void nv3_pfifo_cache0_pull(); +void nv3_pfifo_cache0_pull(void); void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val); -void nv3_pfifo_cache1_pull(); +void nv3_pfifo_cache1_pull(void); uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); -uint32_t nv3_pfifo_cache1_num_free_spaces(); +uint32_t nv3_pfifo_cache1_num_free_spaces(void); // NV3 PFB -void nv3_pfb_init(); +void nv3_pfb_init(void); // NV3 PEXTDEV/PSTRAPS -void nv3_pextdev_init(); +void nv3_pextdev_init(void); // NV3 PBUS -void nv3_pbus_init(); +void nv3_pbus_init(void); // 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 (Final presentation) -void nv3_pramdac_init(); -void nv3_pramdac_set_vram_clock(); -void nv3_pramdac_set_pixel_clock(); +void nv3_pramdac_init(void); +void nv3_pramdac_set_vram_clock(void); +void nv3_pramdac_set_pixel_clock(void); void nv3_pramdac_pixel_clock_poll(double real_time); void nv3_pramdac_memory_clock_poll(double real_time); // NV3 PTIMER -void nv3_ptimer_init(); +void nv3_ptimer_init(void); void nv3_ptimer_tick(double real_time); // NV3 PVIDEO -void nv3_pvideo_init(); +void nv3_pvideo_init(void); // NV3 PME (Mediaport) -void nv3_pme_init(); \ No newline at end of file +void nv3_pme_init(void); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 6582ada13..45e5155a7 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -59,7 +59,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ && nv3->pgraph.blit.size.w == 0) return; - nv3_render_blit_screen2screen(grobj); + //nv3_render_blit_screen2screen(grobj); break; default: diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 78592160a..aa8865fdd 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -511,6 +511,21 @@ void nv3_recalc_timings(svga_t* svga) svga->hdisp += 0x100; // large screen bit } + /* Turn off override if we are in VGA mode */ + svga->override = !(pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA); + + /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. + + Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. + + This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. + In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. + + Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick + to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from + the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. + */ + // Set the pixel mode switch (pixel_mode) { @@ -519,7 +534,7 @@ void nv3_recalc_timings(svga_t* svga) svga->bpp = 8; svga->lowres = 0; svga->map8 = svga->pallook; - svga->render = svga_render_8bpp_highres; + //svga->render = nv3_render_8bpp; break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: /* This is some sketchy shit that is an attempt at an educated guess @@ -536,13 +551,13 @@ void nv3_recalc_timings(svga_t* svga) { svga->bpp = 16; svga->lowres = 0; - svga->render = nv3_render_16bpp; + //svga->render = nv3_render_16bpp; } else { svga->bpp = 15; svga->lowres = 0; - svga->render = nv3_render_15bpp; + //svga->render = nv3_render_15bpp; } @@ -552,7 +567,7 @@ void nv3_recalc_timings(svga_t* svga) svga->bpp = 32; svga->lowres = 0; - svga->render = nv3_render_32bpp; + //svga->render = nv3_render_32bpp; break; } @@ -784,6 +799,8 @@ void nv3_dfb_write8(uint32_t addr, uint8_t val, void* priv) { addr &= (nv3->nvbase.svga.vram_mask); nv3->nvbase.svga.vram[addr] = val; + nv3->nvbase.svga.changedvram[addr >> 12] = val; + nv3_render_current_bpp_dfb_8(addr); } void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv) @@ -791,6 +808,9 @@ void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv) addr &= (nv3->nvbase.svga.vram_mask); nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; nv3->nvbase.svga.vram[addr] = (val) & 0xFF; + nv3->nvbase.svga.changedvram[addr >> 12] = val; + nv3_render_current_bpp_dfb_16(addr); + } void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv) @@ -800,6 +820,10 @@ void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv) nv3->nvbase.svga.vram[addr + 2] = (val >> 16) & 0xFF; nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; nv3->nvbase.svga.vram[addr] = (val) & 0xFF; + nv3->nvbase.svga.changedvram[addr >> 12] = val; + + nv3_render_current_bpp_dfb_32(addr); + } /* Cursor shit */ diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 666a93482..09a099977 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -132,6 +132,20 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) nv3_position_16_t old_position = nv3->pgraph.blit.point_in; nv3_position_16_t new_position = nv3->pgraph.blit.point_out; + uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + + /* test DST_BUFFER code + I assume for 2d at least only one is allowed at a time + */ + + uint32_t dst_buffer = 0; // 5 = just use the source buffer + + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; + + uint16_t end_x_in = (nv3->pgraph.blit.point_in.x + nv3->pgraph.blit.size.w); /* needed for bounds checking */ uint16_t end_x_out = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); uint16_t end_y = (nv3->pgraph.blit.point_out.y + nv3->pgraph.blit.size.h); @@ -173,4 +187,40 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) vram_position = nv3_render_get_vram_address(new_position, grobj, true); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); } + + /* + We need to blit manually here because we don't go through nv3_render_write_pixel + We also need to update all of the areas of the screen that moved. + */ + + nv3_position_16_t blit_position = {0}; + nv3_size_16_t blit_size = {0}; + + /* Change the smallest area of the screen that moved */ + if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) + blit_size.w = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.w; + else if (nv3->pgraph.blit.point_out.x < nv3->pgraph.blit.point_in.x) + blit_size.w = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.w; + else + blit_size.w = nv3->pgraph.blit.size.w; + + if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) + blit_size.h = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.h; + else if (nv3->pgraph.blit.point_out.y < nv3->pgraph.blit.point_in.y) + blit_size.h = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.h; + else + blit_size.h = nv3->pgraph.blit.size.h; + + if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) + blit_position.x = nv3->pgraph.blit.point_in.x; + else if (nv3->pgraph.blit.point_out.x <= nv3->pgraph.blit.point_in.x) // equals case, just use out + blit_position.x = nv3->pgraph.blit.point_out.x; + + if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) + blit_position.y = nv3->pgraph.blit.point_in.y; + else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out + blit_position.y = nv3->pgraph.blit.point_out.y; + + + nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 147ac8f6e..4313ac2a6 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -30,6 +30,12 @@ #include <86box/nv/vid_nv3.h> #include <86box/utils/video_stdlib.h> +/* Functions only used in this translation unit */ +void nv3_render_8bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_15bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_16bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_32bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); + /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! */ @@ -261,6 +267,38 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro return pixel_addr_vram; } +/* Convert a dumb framebuffer address to a position. No buffer setup or anything, but just start at 0,0 for address 0. */ +nv3_position_16_t nv3_render_get_dfb_position(uint32_t vram_address) +{ + nv3_position_16_t pos = {0}; + + uint32_t pitch = nv3->nvbase.svga.hdisp; + + if (nv3->nvbase.svga.bpp == 15 + || nv3->nvbase.svga.bpp == 16) + pitch <<= 1; + else if (nv3->nvbase.svga.bpp == 32) + pitch <<= 2; + + //vram_address -= nv3->pgraph.boffset[0]; + + pos.y = (vram_address / pitch); + pos.x = (vram_address % pitch); + + /* Fixup our x position */ + if (nv3->nvbase.svga.bpp == 15 + || nv3->nvbase.svga.bpp == 16) + pos.x >>= 1; + else if (nv3->nvbase.svga.bpp == 32) + pos.x >>= 2; + + + /* there is some strange behaviour where it writes long past the end of the fb */ + if (pos.y >= nv3->nvbase.svga.monitor->target_buffer->h) pos.y = nv3->nvbase.svga.monitor->target_buffer->h - 1; + + return pos; +} + /* Read an 8bpp pixel from the framebuffer. */ uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) { @@ -435,165 +473,250 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob break; } + + /* Go write the pixel */ + nv3_size_16_t size = {0}; + size.w = size.h = 1; + nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj); } +/* Ensure the correct monitor size */ +void nv3_render_ensure_mode() +{ + bool changed = false; //doesn't check if the res is the same? + + if (nv3->nvbase.svga.hdisp != nv3->nvbase.svga.monitor->mon_xsize) + { + changed = true; + nv3->nvbase.svga.monitor->mon_xsize = nv3->nvbase.svga.hdisp; + } + + if (nv3->nvbase.svga.dispend != nv3->nvbase.svga.monitor->mon_ysize) + { + changed = true; + nv3->nvbase.svga.monitor->mon_ysize = nv3->nvbase.svga.dispend; + } + + if (changed) + { + /* set refresh rate - this is just a rough estimation right now. we need it as we only blit what changes */ + nv3->nvbase.refresh_time = 1 / (nv3->nvbase.pixel_clock_frequency / (double)ysize / (double)xsize); // rivatimers count in microseconds + set_screen_size(xsize, ysize); + } + +} + + +/* Blit to the monitor from DFB, 8bpp */ +void nv3_render_current_bpp_dfb_8(uint32_t address) +{ + +} + +/* Blit to the monitor from DFB, 15/16bpp */ +void nv3_render_current_bpp_dfb_16(uint32_t address) +{ + nv3_size_16_t size = {0}; + size.w = size.h = 1; + + nv3_position_16_t pos = nv3_render_get_dfb_position(address); + + //pos.x >>= 1; + + uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); + + if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) + /* should just "tip over" to the next line */ + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 16); + else + /* should just "tip over" to the next line */ + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); + + /*does 8bpp packed into 16 occur/ i would be surprised*/ +} + +/* Blit to the monitor from DFB, 32bpp */ +void nv3_render_current_bpp_dfb_32(uint32_t address) +{ + nv3_size_16_t size = {0}; + size.w = size.h = 1; + + nv3_position_16_t pos = nv3_render_get_dfb_position(address); + + uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); + + if (nv3->nvbase.svga.bpp == 32) + { + uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + *p = data; + } + /* Packed format */ + else if (nv3->nvbase.svga.bpp == 15 + || nv3->nvbase.svga.bpp == 16) + { + //pos.x >>= 1; + + uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, nv3->nvbase.svga.bpp); + *p++; + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, (data >> 16) & 0xFFFF, nv3->nvbase.svga.bpp); + } +} + + +/* Blit to the monitor from GPU, current bpp */ +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +{ + /* Ensure that we are in the correct mode. Modified SVGA core code */ + nv3_render_ensure_mode(); + + switch (nv3->nvbase.svga.bpp) + { + case 4: + /* Uh we should never be here because we're in the SVGA mode */ + fatal("NV3 - Tried to render 4bpp in NV mode"); + break; + case 8: + nv3_render_8bpp(pos, size, grobj); + break; + case 15: + nv3_render_15bpp(pos, size, grobj); + break; + case 16: + nv3_render_16bpp(pos, size, grobj); + break; + case 32: + nv3_render_32bpp(pos, size, grobj); + break; + } + +} /* - Modified SVGA Core functions - Done, to facilitate the buffer changing around stuff. - - TODO: Do we need a custom 8bpp function? + Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, indexed 8 bits per pixel format */ -void nv3_render_15bpp(svga_t *svga) +void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) { - int x; - uint32_t *p; - uint32_t dat; - uint32_t changed_addr; - uint32_t addr; + +} - /* "force_old_addr" code path removed, since we don't need it */ +/* + Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 15 bits per pixel format +*/ - if ((svga->displine + svga->y_add) < 0) - return; +void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +{ + if (!nv3) + return; - changed_addr = svga->remap_func(svga, svga->ma); + uint32_t vram_base; //acquired for the start of each line + uint32_t* p; + uint32_t data; + uint32_t start_x = pos.x; - if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { - p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - if (svga->firstline_draw == 2000) - svga->firstline_draw = svga->displine; - svga->lastline_draw = svga->displine; + for (uint32_t y = 0; y < size.h; y++) + { + /* re-set the vram address because we are basically "jumping" halfway across a line here */ + vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; - if (!svga->remap_required) { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 8) { - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1)) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); - *p++ = svga->conv_16to32(svga, dat >> 16, 15); + for (uint32_t x = 0; x < size.w; x++) + { + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 4) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); - *p++ = svga->conv_16to32(svga, dat >> 16, 15); - - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 8) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); - *p++ = svga->conv_16to32(svga, dat >> 16, 15); - - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 12) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); - *p++ = svga->conv_16to32(svga, dat >> 16, 15); - } - svga->ma += x << 1; - } else { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 2) { - addr = svga->remap_func(svga, svga->ma); - dat = *(uint32_t *) (&svga->vram[(addr) & svga->vram_display_mask]); - - *p++ = svga->conv_16to32(svga, dat & 0xffff, 15); - *p++ = svga->conv_16to32(svga, dat >> 16, 15); - svga->ma += 4; - } + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + + /* should just "tip over" to the next line */ + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); + + vram_base += 2; + pos.x++; } - svga->ma &= svga->vram_display_mask; + + pos.x = start_x; + pos.y++; } } +/* + Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 16 bits per pixel format +*/ -void nv3_render_16bpp(svga_t *svga) -{ - int x; - uint32_t *p; - uint32_t dat; - uint32_t changed_addr; - uint32_t addr; +void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +{ + if (!nv3) + return; - if ((svga->displine + svga->y_add) < 0) - return; + uint32_t vram_base; //acquired for the start of each line + uint32_t* p; + uint32_t data; + uint32_t start_x = pos.x; - changed_addr = svga->remap_func(svga, svga->ma); + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - /* "force_old_addr" code path removed, since we don't need it */ - if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { - p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + for (uint32_t y = 0; y < size.h; y++) + { + /* re-get the vram address because we are basically "jumping" halfway across a line here */ + vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; - if (svga->firstline_draw == 2000) - svga->firstline_draw = svga->displine; - svga->lastline_draw = svga->displine; + for (uint32_t x = 0; x < size.w; x++) + { + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - if (!svga->remap_required) { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 8) { - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1)) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); - *p++ = svga->conv_16to32(svga, dat >> 16, 16); - - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 4) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); - *p++ = svga->conv_16to32(svga, dat >> 16, 16); - - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 8) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); - *p++ = svga->conv_16to32(svga, dat >> 16, 16); - - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 1) + 12) & svga->vram_display_mask]); - *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); - *p++ = svga->conv_16to32(svga, dat >> 16, 16); - } - svga->ma += x << 1; - } else { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x += 2) { - addr = svga->remap_func(svga, svga->ma); - dat = *(uint32_t *) (&svga->vram[(addr) & svga->vram_display_mask]); - - *p++ = svga->conv_16to32(svga, dat & 0xffff, 16); - *p++ = svga->conv_16to32(svga, dat >> 16, 16); - - svga->ma += 4; - } + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + + /* should just "tip over" to the next line */ + *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); + + vram_base += 2; + pos.x++; } - svga->ma &= svga->vram_display_mask; - } + + pos.x = start_x; + pos.y++; + } } +/* + Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 32 bits per pixel format +*/ -void nv3_render_32bpp(svga_t *svga) +void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) { - int x; - uint32_t *p; - uint32_t dat; - uint32_t changed_addr; - uint32_t addr; + if (!nv3) + return; - if ((svga->displine + svga->y_add) < 0) - return; + uint32_t vram_base; + uint32_t* p; + uint32_t data; + uint32_t start_x = pos.x; - /* "force_old_addr" code path removed, since we don't need it */ + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - changed_addr = svga->remap_func(svga, svga->ma); + for (uint32_t y = 0; y < size.h; y++) + { + /* re-get the vram address because we are basically "jumping" halfway across a line here */ + vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; + - if (svga->changedvram[changed_addr >> 12] || svga->changedvram[(changed_addr >> 12) + 1] || svga->fullchange) { - p = &svga->monitor->target_buffer->line[svga->displine + svga->y_add][svga->x_add]; + for (uint32_t x = 0; x < size.w; x++) + { + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - if (svga->firstline_draw == 2000) - svga->firstline_draw = svga->displine; - svga->lastline_draw = svga->displine; - - if (!svga->remap_required) { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x++) { - dat = *(uint32_t *) (&svga->vram[(svga->ma + (x << 2)) & svga->vram_display_mask]); - *p++ = svga_lookup_lut_ram(svga, dat & 0xffffff); - } - svga->ma += (x * 4); - } else { - for (x = 0; x <= (svga->hdisp + svga->scrollcache); x++) { - addr = svga->remap_func(svga, svga->ma); - dat = *(uint32_t *) (&svga->vram[addr & svga->vram_display_mask]); - *p++ = svga_lookup_lut_ram(svga, dat & 0xffffff); - - svga->ma += 4; - } + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + + /* should just "tip over" to the next line */ + *p = data; + + vram_base += 4; + pos.x++; } - svga->ma &= svga->vram_display_mask; + + pos.y++; + pos.x = start_x; } } diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index 487f41731..c89fd2d6e 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -119,6 +119,7 @@ void nv3_render_gdi_transparent_bitmap_blit(bool bit, bool clip, uint32_t color, else nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; } + } /* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ @@ -208,6 +209,7 @@ void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitma /* IF we're done, let's return */ if (!bits_remaining_in_bitmap) return; + } void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, nv3_grobj_t grobj) @@ -262,6 +264,7 @@ void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, } } + /* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj) { diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 0e41807a0..d1fb9f1c5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -51,6 +51,23 @@ void nv3_pramdac_init() // Polls the pixel clock. void nv3_pramdac_pixel_clock_poll(double real_time) { + /* Ignore in VGA mode */ + if (!nv3->nvbase.svga.override) + return; + + /* Figure out our refresh time. */ + if (!nv3->nvbase.refresh_time) + nv3->nvbase.refresh_time = (1/60.0); // rivatimers count in microseconds but present the info as seconds + + nv3->nvbase.refresh_clock += real_time; + + if (nv3->nvbase.refresh_clock > nv3->nvbase.refresh_time) + { + /* Update the screen because something changed */ + video_blit_memtoscreen(0, 0, xsize, ysize); + nv3->nvbase.refresh_clock = 0; + } + // TODO: ???? } From c8f3a8ad6910b6287cb6ff2fe4014de176665334 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 9 Apr 2025 21:31:15 +0100 Subject: [PATCH 163/274] uncomment s2sb --- src/video/nv/nv3/classes/nv3_class_010_blit.c | 2 +- src/video/nv/nv3/nv3_core.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 45e5155a7..6582ada13 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -59,7 +59,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ && nv3->pgraph.blit.size.w == 0) return; - //nv3_render_blit_screen2screen(grobj); + nv3_render_blit_screen2screen(grobj); break; default: diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index aa8865fdd..babe7a056 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -534,7 +534,6 @@ void nv3_recalc_timings(svga_t* svga) svga->bpp = 8; svga->lowres = 0; svga->map8 = svga->pallook; - //svga->render = nv3_render_8bpp; break; case NV3_CRTC_REGISTER_PIXELMODE_16BPP: /* This is some sketchy shit that is an attempt at an educated guess @@ -545,19 +544,16 @@ void nv3_recalc_timings(svga_t* svga) else svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; - /* sometimes it really renders in 15bpp, so you need to do this */ if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) { svga->bpp = 16; svga->lowres = 0; - //svga->render = nv3_render_16bpp; } else { svga->bpp = 15; svga->lowres = 0; - //svga->render = nv3_render_15bpp; } From 22981f2d4b7d5ca28343ea48cc6d41d80a26d0e7 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 10 Apr 2025 10:56:07 +0100 Subject: [PATCH 164/274] remove some code that was stupid, add void to fucntions --- CMakeLists.txt | 2 +- src/include/86box/nv/render/vid_nv3_render.h | 8 +-- src/video/nv/nv3/classes/nv3_class_010_blit.c | 7 +-- .../nv/nv3/classes/nv3_class_shared_methods.c | 2 +- src/video/nv/nv3/nv3_core.c | 16 +++--- src/video/nv/nv3/render/nv3_render_blit.c | 10 ++-- src/video/nv/nv3/render/nv3_render_core.c | 55 +++++++++---------- src/video/nv/nv3/subsystems/nv3_pbus.c | 2 +- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 2 +- src/video/nv/nv3/subsystems/nv3_pextdev.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 10 ++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 +- src/video/nv/nv3/subsystems/nv3_pmc.c | 6 +- src/video/nv/nv3/subsystems/nv3_pme.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 10 ++-- src/video/nv/nv3/subsystems/nv3_ptimer.c | 2 +- src/video/nv/nv3/subsystems/nv3_pvideo.c | 2 +- 18 files changed, 68 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52f24f4b9..c9825b9ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ option(DISCORD "Discord Rich Presence support" option(DEBUGREGS486 "Enable debug register opeartion on 486+ CPUs" OFF) # Remove when merged, should just be -D option(NV_LOG "NVidia RIVA 128 debug logging" ON) -option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) +option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" ON) if (NV_LOG) add_compile_definitions(ENABLE_NV_LOG) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 11f4d2368..346396c54 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -24,12 +24,12 @@ void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj); +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj); +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination); +uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 6582ada13..b1dc30ea0 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -47,12 +47,7 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* This is the last one*/ nv3->pgraph.blit.size.w = (param & 0xFFFF); nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB Size %04x,%04x grobj_0=0x%08x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y, grobj.grobj_0); - - /* Some blits have (point_in == point_out) ???? */ - if (nv3->pgraph.blit.point_in.x == nv3->pgraph.blit.point_out.x - && nv3->pgraph.blit.point_in.y == nv3->pgraph.blit.point_out.y) - return; + nv_log("Method Execution: S2SB Size %04x,%04x grobj_0=0x%08x\n", nv3->pgraph.blit.size.w, nv3->pgraph.blit.size.h, grobj.grobj_0); /* Some of these seem to have sizes of 0, so skip */ if (nv3->pgraph.blit.size.h == 0 diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 0ec7cdd2d..80c635a21 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -35,7 +35,7 @@ void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t { /* mthdCreate in software(?)*/ case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - nv_log("mthdCreate obj_name=0x%08x\n", param); + //nv_log("mthdCreate obj_name=0x%08x\n", param); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); break; // set up the current notification request/object diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index babe7a056..785016a72 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -38,8 +38,8 @@ static video_timings_t timing_nv3t_pci = { .type = VIDEO_PCI, .write_b = 2, .wri static video_timings_t timing_nv3t_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; // Prototypes for functions only used in this translation unit -void nv3_init_mappings_mmio(); -void nv3_init_mappings_svga(); +void nv3_init_mappings_mmio(void); +void nv3_init_mappings_svga(void); bool nv3_is_svga_redirect_address(uint32_t addr); uint8_t nv3_svga_in(uint16_t addr, void* priv); @@ -873,7 +873,7 @@ void nv3_prom_write(uint32_t address, uint32_t value) } // Initialise the MMIO mappings -void nv3_init_mappings_mmio() +void nv3_init_mappings_mmio(void) { nv_log("Initialising MMIO mapping\n"); @@ -913,7 +913,7 @@ void nv3_init_mappings_mmio() } -void nv3_init_mappings_svga() +void nv3_init_mappings_svga(void) { nv_log("Initialising SVGA core memory mapping\n"); @@ -944,14 +944,14 @@ void nv3_init_mappings_svga() nv3); } -void nv3_init_mappings() +void nv3_init_mappings(void) { nv3_init_mappings_mmio(); nv3_init_mappings_svga(); } // Updates the mappings after initialisation. -void nv3_update_mappings() +void nv3_update_mappings(void) { // sanity check if (!nv3) @@ -1177,6 +1177,7 @@ 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); + return nv3; } // This function simply allocates ram and sets the bus to agp before initialising. @@ -1185,6 +1186,7 @@ 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); + return nv3; } void nv3_close(void* priv) @@ -1210,7 +1212,7 @@ void nv3_close(void* priv) } // See if the bios rom is available. -int32_t nv3_available() +int32_t nv3_available(void) { return rom_present(NV3_VBIOS_ASUS_V3000_V151) || rom_present(NV3_VBIOS_DIAMOND_V330_V162) diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 09a099977..fd32e4e61 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -30,7 +30,7 @@ #include <86box/nv/vid_nv3.h> /* Check the line bounds */ -void nv3_class_011_check_line_bounds() +void nv3_class_011_check_line_bounds(void) { uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; //uint32_t relative_y = nv3->pgraph.image_current_position.y - nv3->pgraph.image.point.y; @@ -174,7 +174,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) old_position.y = nv3->pgraph.blit.point_in.y + y; /* 32bit buffer */ buf_position = (nv3->pgraph.blit.size.w * y); - vram_position = nv3_render_get_vram_address(old_position, grobj, false); + vram_position = nv3_render_get_vram_address(old_position, grobj); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); } @@ -184,7 +184,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { buf_position = (nv3->pgraph.blit.size.w * y); new_position.y = nv3->pgraph.blit.point_out.y + y; - vram_position = nv3_render_get_vram_address(new_position, grobj, true); + vram_position = nv3_render_get_vram_address(new_position, grobj); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); } @@ -221,6 +221,8 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out blit_position.y = nv3->pgraph.blit.point_out.y; + uint32_t buf_end = ((nv3->nvbase.svga.bpp + 1) >> 3) * xsize * ysize; - nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj); + //if (nv3->pgraph.boffset[dst_buffer] <= buf_end) + //nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 4313ac2a6..7d27a2623 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -219,30 +219,23 @@ uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool } /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ -uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) +uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - /* test DST_BUFFER code - I assume for 2d at least only one is allowed at a time - */ + uint32_t destination_buffer = 5; // 5 = just use the source buffer - if (use_destination) - { - uint32_t destination_buffer = 5; // 5 = just use the source buffer + // src is hardcoded to 1, dst to 0. Hmm... + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) destination_buffer = 0; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) destination_buffer = 1; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) destination_buffer = 2; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) destination_buffer = 3; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) destination_buffer = 0; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) destination_buffer = 1; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) destination_buffer = 2; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) destination_buffer = 3; - - if (destination_buffer != current_buffer - && destination_buffer != 5) - current_buffer = destination_buffer; - - } + if (destination_buffer != current_buffer + && destination_buffer != 5) + current_buffer = destination_buffer; uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; @@ -300,19 +293,19 @@ nv3_position_16_t nv3_render_get_dfb_position(uint32_t vram_address) } /* Read an 8bpp pixel from the framebuffer. */ -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) +uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); return nv3->nvbase.svga.vram[vram_address]; } /* Read an 16bpp pixel from the framebuffer. */ -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) +uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); vram_address >>= 1; //convert to 16bit pointer @@ -321,10 +314,10 @@ uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj, } /* Read an 16bpp pixel from the framebuffer. */ -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj, bool use_destination) +uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj, use_destination); + uint32_t vram_address = nv3_render_get_vram_address(position, grobj); uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); vram_address >>= 2; //convert to 32bit pointer @@ -372,7 +365,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob if (!nv3_render_chroma_test(color, grobj)) return; - uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj, true); + uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; uint8_t bit = 0x00; @@ -481,7 +474,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob } /* Ensure the correct monitor size */ -void nv3_render_ensure_mode() +void nv3_render_ensure_screen_size(void) { bool changed = false; //doesn't check if the res is the same? @@ -570,7 +563,11 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) { /* Ensure that we are in the correct mode. Modified SVGA core code */ - nv3_render_ensure_mode(); + nv3_render_ensure_screen_size(); + + /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM */ + //if (nv3->nvbase.last_buffer_address > (((nv3->nvbase.svga.bpp + 1) >> 3) * xsize * ysize)) + //return; switch (nv3->nvbase.svga.bpp) { @@ -622,7 +619,7 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) { @@ -661,7 +658,7 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { /* re-get the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) { @@ -700,7 +697,7 @@ void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { /* re-get the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj, true) & nv3->nvbase.svga.vram_display_mask; + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 67a052d50..16934998c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -39,7 +39,7 @@ nv_register_t pbus_registers[] = { { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; -void nv3_pbus_init() +void nv3_pbus_init(void) { nv_log("Initialising PBUS..."); diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index 4ce686335..da068e07f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -30,7 +30,7 @@ /* Nvidia DMA Engine */ -void nv3_dma_translate_address() +void nv3_dma_translate_address(void) { } \ 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 index 30ba68c03..5d7448241 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_pextdev_init() +void nv3_pextdev_init(void) { nv_log("Initialising PEXTDEV....\n"); diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 0b36a3993..bf61f32af 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -43,7 +43,7 @@ nv_register_t pfb_registers[] = { { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; -void nv3_pfb_init() +void nv3_pfb_init(void) { nv_log("Initialising PFB..."); diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index ea39b9135..0a6a843a5 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -77,7 +77,7 @@ nv_register_t pfifo_registers[] = { }; // PFIFO init code -void nv3_pfifo_init() +void nv3_pfifo_init(void) { nv_log("Initialising PFIFO..."); @@ -347,7 +347,7 @@ uint32_t nv3_pfifo_read(uint32_t address) return ret; } -void nv3_pfifo_trigger_dma_if_required() +void nv3_pfifo_trigger_dma_if_required(void) { // Not a thing for cache0 @@ -697,7 +697,7 @@ NV_USER writes always go to CACHE1 */ // Pulls graphics objects OUT of cache0 -void nv3_pfifo_cache0_pull() +void nv3_pfifo_cache0_pull(void) { // Do nothing if PFIFO CACHE0 is disabled if (!nv3->pfifo.cache0_settings.pull0 & (1 >> NV3_PFIFO_CACHE0_PULL0_ENABLED)) @@ -887,7 +887,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) } // Pulls graphics objects OUT of cache1 -void nv3_pfifo_cache1_pull() +void nv3_pfifo_cache1_pull(void) { // Do nothing if PFIFO CACHE1 is disabled if (!nv3->pfifo.cache1_settings.pull0 & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) @@ -956,7 +956,7 @@ void nv3_pfifo_cache1_pull() } // THIS IS PER SUBCHANNEL! -uint32_t nv3_pfifo_cache1_num_free_spaces() +uint32_t nv3_pfifo_cache1_num_free_spaces(void) { // get the index diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 82ad95706..43befe49c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -30,7 +30,7 @@ #include <86box/nv/classes/vid_nv3_classes.h> // Initialise the PGRAPH subsystem. -void nv3_pgraph_init() +void nv3_pgraph_init(void) { nv_log("Initialising PGRAPH..."); // Set up the vblank interrupt diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 050af07fa..3ede8b691 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -28,9 +28,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> - - -void nv3_pmc_init() +void nv3_pmc_init(void) { nv_log("Initialising PMC....\n"); @@ -58,7 +56,7 @@ nv_register_t pmc_registers[] = { { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; -uint32_t nv3_pmc_clear_interrupts() +uint32_t nv3_pmc_clear_interrupts(void) { nv_log_verbose_only("Clearing IRQs\n"); pci_clear_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); diff --git a/src/video/nv/nv3/subsystems/nv3_pme.c b/src/video/nv/nv3/subsystems/nv3_pme.c index b18797a65..1f0da2aac 100644 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ b/src/video/nv/nv3/subsystems/nv3_pme.c @@ -34,7 +34,7 @@ nv_register_t pme_registers[] = { { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; -void nv3_pme_init() +void nv3_pme_init(void) { nv_log("Initialising PME..."); diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index d1fb9f1c5..8f16cb015 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -31,7 +31,7 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv3.h> -void nv3_pramdac_init() +void nv3_pramdac_init(void) { nv_log("Initialising PRAMDAC\n"); @@ -83,7 +83,7 @@ void nv3_pramdac_memory_clock_poll(double real_time) } // Gets the vram clock register. -uint32_t nv3_pramdac_get_vram_clock_register() +uint32_t nv3_pramdac_get_vram_clock_register(void) { // the clock format is packed into 19 bits // M divisor [7-0] @@ -94,7 +94,7 @@ uint32_t nv3_pramdac_get_vram_clock_register() + (nv3->pramdac.memory_clock_p << 16); // 0-3 } -uint32_t nv3_pramdac_get_pixel_clock_register() +uint32_t nv3_pramdac_get_pixel_clock_register(void) { return (nv3->pramdac.pixel_clock_m) + (nv3->pramdac.pixel_clock_n << 8) @@ -119,7 +119,7 @@ void nv3_pramdac_set_pixel_clock_register(uint32_t value) nv3_pramdac_set_pixel_clock(); } -void nv3_pramdac_set_vram_clock() +void nv3_pramdac_set_vram_clock(void) { // from driver and vbios source float frequency = 13500000.0f; @@ -150,7 +150,7 @@ void nv3_pramdac_set_vram_clock() rivatimer_set_period(nv3->nvbase.memory_clock_timer, time); } -void nv3_pramdac_set_pixel_clock() +void nv3_pramdac_set_pixel_clock(void) { // frequency divider algorithm from old varcem/86box/pcbox riva driver, // verified by reversing NT drivers v1.50e CalcMNP [symbols] function diff --git a/src/video/nv/nv3/subsystems/nv3_ptimer.c b/src/video/nv/nv3/subsystems/nv3_ptimer.c index 3a882b5b8..ab14be4f9 100644 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ b/src/video/nv/nv3/subsystems/nv3_ptimer.c @@ -41,7 +41,7 @@ nv_register_t ptimer_registers[] = { }; // ptimer init code -void nv3_ptimer_init() +void nv3_ptimer_init(void) { nv_log("Initialising PTIMER..."); diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c index c883ee7e5..60019b9c6 100644 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ b/src/video/nv/nv3/subsystems/nv3_pvideo.c @@ -39,7 +39,7 @@ nv_register_t pvideo_registers[] = { }; // ptimer init code -void nv3_pvideo_init() +void nv3_pvideo_init(void) { nv_log("Initialising PVIDEO..."); From d93b3de9aae7344d095dbd2d83078be35c43b9fe Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 10 Apr 2025 19:30:50 +0100 Subject: [PATCH 165/274] Fix compilation of nv_log_verbose_only --- src/video/nv/nv_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c index bce7425c2..c7a83d5ad 100644 --- a/src/video/nv/nv_base.c +++ b/src/video/nv/nv_base.c @@ -78,6 +78,8 @@ void nv_log(const char *fmt, ...) void nv_log_verbose_only(const char *fmt, ...) { #ifdef ENABLE_NV_LOG_ULTRA + va_list arg; + if (!nv_do_log) return; From e1ad1d39eb707ab102f71a107c50d4387eda1a52 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 12 Apr 2025 00:12:57 +0100 Subject: [PATCH 166/274] hopefully fix build with GHA cfg --- src/include/86box/nv/vid_nv3.h | 2 +- src/video/nv/nv3/nv3_core.c | 6 +++--- src/video/nv/nv3/subsystems/nv3_pextdev.c | 2 +- src/video/nv/nv3/subsystems/nv3_pfb.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 5 ++--- src/video/nv/nv3/subsystems/nv3_pmc.c | 2 +- src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c | 3 ++- src/video/nv/nv3/subsystems/nv3_pramin_ramht.c | 1 + src/video/nv/nv3/subsystems/nv3_pramin_ramro.c | 1 + 9 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index f11a4cdfc..43abf63df 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1557,7 +1557,7 @@ void nv3_user_write(uint32_t address, uint32_t value); // NV3 PMC void nv3_pmc_init(void); -uint32_t nv3_pmc_clear_interrupts(void); +void nv3_pmc_clear_interrupts(void); uint32_t nv3_pmc_handle_interrupts(bool send_now); // NV3 PGRAPH diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 785016a72..ff684ba47 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -616,7 +616,7 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) 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); + 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; } @@ -869,7 +869,7 @@ uint8_t nv3_prom_read(uint32_t address) void nv3_prom_write(uint32_t address, uint32_t value) { uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", address, value); + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", real_addr, value); } // Initialise the MMIO mappings @@ -974,7 +974,7 @@ void nv3_update_mappings(void) nv3_svga_out, NULL, NULL, nv3); - if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND]) & PCI_COMMAND_MEM) + if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) { nv_log("The memory was turned off, not much is going to happen.\n"); return; diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 5d7448241..9aa17698f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -134,7 +134,7 @@ void nv3_pextdev_write(uint32_t address, uint32_t value) // special consideration for straps if (address == NV3_PSTRAPS) { - warning("Huh? Tried to write to the straps. Something is wrong...\n", nv3->pextdev.straps); + warning("Huh? Tried to write to the straps (value=%d). Something is wrong...\n", nv3->pextdev.straps); return; } diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index bf61f32af..3e6cd4271 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -29,7 +29,7 @@ #include <86box/nv/vid_nv3.h> // Functions only used in this translation unit -uint32_t nv3_pfb_config0_read(); +uint32_t nv3_pfb_config0_read(void); void nv3_pfb_config0_write(uint32_t val); nv_register_t pfb_registers[] = { @@ -170,7 +170,7 @@ void nv3_pfb_write(uint32_t address, uint32_t value) } } -uint32_t nv3_pfb_config0_read() +uint32_t nv3_pfb_config0_read(void) { return nv3->pfb.config_0; } diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 0a6a843a5..69b834df9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -760,8 +760,7 @@ void nv3_pfifo_context_switch(uint32_t new_channel) if (new_channel >= NV3_DMA_CHANNELS) fatal("nv3_pfifo_context_switch: Tried to switch to invalid dma channel"); - uint16_t ramfc_base = nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS & 0xF; - + //uint16_t ramfc_base = nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS & 0xF; } // NV_USER writes go here! @@ -820,7 +819,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) { // Cache reassignment required if (!nv3->pfifo.cache_reassignment - || (nv3->pfifo.cache1_settings.get_address != nv3->pfifo.cache1_settings.get_address)) + || (nv3->pfifo.cache1_settings.get_address != nv3->pfifo.cache1_settings.put_address)) { oh_shit = true; oh_shit_reason = nv3_runout_reason_no_cache_available; diff --git a/src/video/nv/nv3/subsystems/nv3_pmc.c b/src/video/nv/nv3/subsystems/nv3_pmc.c index 3ede8b691..b6528fe0a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ b/src/video/nv/nv3/subsystems/nv3_pmc.c @@ -56,7 +56,7 @@ nv_register_t pmc_registers[] = { { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; -uint32_t nv3_pmc_clear_interrupts(void) +void nv3_pmc_clear_interrupts(void) { nv_log_verbose_only("Clearing IRQs\n"); pci_clear_irq(nv3->nvbase.pci_slot, PCI_INTA, &nv3->nvbase.pci_irq_state); diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c index 51d33c9a6..59fa41e8d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c @@ -30,7 +30,8 @@ uint32_t nv3_ramfc_read(uint32_t address) { - nv_log_verbose_only("RAMFC (Unused DMA channel context) Read (0x%04x)\n", address); + nv_log_verbose_only("RAMFC (Unused DMA channel context) Read (0x%04x) (UNIMPLEMENTED returning 0x00)\n", address); + return 0x00; //temp } void nv3_ramfc_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c index d2d17d321..9f90b3434 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c @@ -47,6 +47,7 @@ uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) uint32_t nv3_ramht_read(uint32_t address) { nv_log_verbose_only("RAMHT (Graphics object storage hashtable) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); + return 0x00; } void nv3_ramht_write(uint32_t address, uint32_t value) diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c index 692a10e91..142d746d2 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c @@ -31,6 +31,7 @@ uint32_t nv3_ramro_read(uint32_t address) { nv_log("BIG Problem: RAM Runout (invalid dma object submission) Read (0x%04x)\n", address); + return 0x00; } void nv3_ramro_write(uint32_t address, uint32_t value) From b606129b780f9b36358f0400c87cf331ee727ae5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 12 Apr 2025 00:23:17 +0100 Subject: [PATCH 167/274] Add string.h to nv3_render_blit --- src/video/nv/nv3/render/nv3_render_blit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index fd32e4e61..dc6c74aa3 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -19,6 +19,7 @@ #include #include #include +#include #include <86box/86box.h> #include <86box/device.h> #include <86box/mem.h> From 15f49638e03d61ceeb2bfd0af2a12dfb4bc3f8b8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 12 Apr 2025 03:02:55 +0100 Subject: [PATCH 168/274] Big improvement to S2SB, but it doesn't fully work yet. Sometimes it copies in the other direction, what ? --- .../86box/nv/classes/vid_nv3_classes.h | 4 + src/include/86box/nv/render/vid_nv3_render.h | 3 +- src/include/86box/nv/vid_nv3.h | 4 +- .../classes/nv3_class_01c_image_in_memory.c | 4 + src/video/nv/nv3/nv3_core.c | 52 ++++++------- src/video/nv/nv3/render/nv3_render_blit.c | 76 ++++++++++++++----- src/video/nv/nv3/render/nv3_render_core.c | 63 +++++++++++++-- 7 files changed, 151 insertions(+), 55 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 3b6eb1411..5647a3c81 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -77,6 +77,10 @@ typedef enum nv3_pgraph_class_e #define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x0100 // Set object ctx for dma...see nv3_dma_context_t structure #define NV3_SET_NOTIFY 0x0104 +// Crap e.g. "OS name", that sometimes gets submitted, for some reason. So we just suppress the warning messages for them +#define NV3_NVCLASS_CRAP_START 0x0310 +#define NV3_NVCLASS_CRAP_END 0x0324 + // Render OPeration #define NV3_ROP_SET_ROP 0x0300 // Set GDI standard rop diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 346396c54..68bc7d557 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,7 +18,7 @@ #pragma once /* Core */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check); void nv3_render_current_bpp_dfb_8(uint32_t address); void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); @@ -30,6 +30,7 @@ uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj); +uint32_t nv3_render_get_vram_address_for_buffer(nv3_position_16_t position, nv3_grobj_t grobj, uint32_t buffer); uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 43abf63df..6d5c8cfca 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1473,8 +1473,8 @@ void nv3_dfb_write8(uint32_t addr, uint8_t val, void* priv); void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv); // Write 16-bit DFB void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv); // Write 32-bit DFB -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_svga_read(uint16_t addr, void* priv); // Read SVGA compatibility registers +void nv3_svga_write(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 diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 9e6a55310..87cf01812 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -86,6 +86,10 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; + case NV3_NVCLASS_CRAP_START ... NV3_NVCLASS_CRAP_END: + /* Suppress but don't do anything */ + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + break; default: warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ff684ba47..1f786fc47 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -42,8 +42,8 @@ void nv3_init_mappings_mmio(void); void nv3_init_mappings_svga(void); bool nv3_is_svga_redirect_address(uint32_t addr); -uint8_t nv3_svga_in(uint16_t addr, void* priv); -void nv3_svga_out(uint16_t addr, uint8_t val, void* priv); +uint8_t nv3_svga_read(uint16_t addr, void* priv); +void nv3_svga_write(uint16_t addr, uint8_t val, void* priv); // Determine if this address needs to be redirected to the SVGA subsystem. @@ -70,7 +70,7 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - ret = nv3_svga_in(real_address, nv3); + ret = nv3_svga_read(real_address, nv3); nv_log_verbose_only("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); @@ -95,8 +95,8 @@ uint16_t nv3_mmio_read16(uint32_t addr, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - ret = nv3_svga_in(real_address, nv3) - | (nv3_svga_in(real_address + 1, nv3) << 8); + ret = nv3_svga_read(real_address, nv3) + | (nv3_svga_read(real_address + 1, nv3) << 8); nv_log_verbose_only("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); @@ -120,10 +120,10 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) // svga writes are not logged anyway rn uint32_t real_address = addr & 0x3FF; - ret = nv3_svga_in(real_address, nv3) - | (nv3_svga_in(real_address + 1, nv3) << 8) - | (nv3_svga_in(real_address + 2, nv3) << 16) - | (nv3_svga_in(real_address + 3, nv3) << 24); + ret = nv3_svga_read(real_address, nv3) + | (nv3_svga_read(real_address + 1, nv3) << 8) + | (nv3_svga_read(real_address + 2, nv3) << 16) + | (nv3_svga_read(real_address + 3, nv3) << 24); nv_log_verbose_only("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); @@ -152,7 +152,7 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) nv_log_verbose_only("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - nv3_svga_out(real_address, val & 0xFF, nv3); + nv3_svga_write(real_address, val & 0xFF, nv3); return; } @@ -180,8 +180,8 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - nv3_svga_out(real_address, val & 0xFF, nv3); - nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); + nv3_svga_write(real_address, val & 0xFF, nv3); + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); return; } @@ -208,10 +208,10 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - nv3_svga_out(real_address, val & 0xFF, nv3); - nv3_svga_out(real_address + 1, (val >> 8) & 0xFF, nv3); - nv3_svga_out(real_address + 2, (val >> 16) & 0xFF, nv3); - nv3_svga_out(real_address + 3, (val >> 24) & 0xFF, nv3); + nv3_svga_write(real_address, val & 0xFF, nv3); + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); + nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); + nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); return; } @@ -597,7 +597,7 @@ void nv3_force_redraw(void* priv) } // Read from SVGA core memory -uint8_t nv3_svga_in(uint16_t addr, void* priv) +uint8_t nv3_svga_read(uint16_t addr, void* priv) { nv3_t* nv3 = (nv3_t*)priv; @@ -662,7 +662,7 @@ uint8_t nv3_svga_in(uint16_t addr, void* priv) } // Write to SVGA core memory -void nv3_svga_out(uint16_t addr, uint8_t val, void* priv) +void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) { // sanity check if (!nv3) @@ -939,8 +939,8 @@ void nv3_init_mappings_svga(void) nv3->nvbase.svga.vram, 0, &nv3->nvbase.svga); io_sethandler(0x03c0, 0x0020, - nv3_svga_in, NULL, NULL, - nv3_svga_out, NULL, NULL, + nv3_svga_read, NULL, NULL, + nv3_svga_write, NULL, NULL, nv3); } @@ -964,14 +964,14 @@ void nv3_update_mappings(void) (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_svga_read, NULL, NULL, + nv3_svga_write, 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_svga_read, NULL, NULL, + nv3_svga_write, NULL, NULL, nv3); if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) @@ -1117,7 +1117,7 @@ void* nv3_init(const device_t *info) 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, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_pci); @@ -1132,7 +1132,7 @@ void* nv3_init(const device_t *info) 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, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_in, nv3_svga_out, nv3_draw_cursor, NULL); + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_agp); diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index dc6c74aa3..788cb4ca2 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -130,14 +130,10 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) && nv3->pgraph.blit.size.h < NV3_MAX_VERTICAL_SIZE) memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.h) * (sizeof(uint32_t) * nv3->pgraph.blit.size.w)); - nv3_position_16_t old_position = nv3->pgraph.blit.point_in; - nv3_position_16_t new_position = nv3->pgraph.blit.point_out; - + /* First calculate our source and destination buffer */ uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - /* test DST_BUFFER code - I assume for 2d at least only one is allowed at a time - */ + bool wtf_nvidia = false; uint32_t dst_buffer = 0; // 5 = just use the source buffer @@ -145,13 +141,23 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; + + nv3_position_16_t old_position; + nv3_position_16_t new_position; - - uint16_t end_x_in = (nv3->pgraph.blit.point_in.x + nv3->pgraph.blit.size.w); /* needed for bounds checking */ - uint16_t end_x_out = (nv3->pgraph.blit.point_out.x + nv3->pgraph.blit.size.w); - uint16_t end_y = (nv3->pgraph.blit.point_out.y + nv3->pgraph.blit.size.h); - - uint32_t pixel_to_copy = 0x00; + /* If src_buffer != dst_buffer, the positions and src/dst buffer seem to be swapped. + Some kind of hardware errata (?), otherwise, I have no explanation for this behaviour. */ + if (nv3->pgraph.boffset[src_buffer] == nv3->pgraph.boffset[dst_buffer]) + { + old_position = nv3->pgraph.blit.point_in; + new_position = nv3->pgraph.blit.point_out; + } + else + { + old_position = nv3->pgraph.blit.point_out; + new_position = nv3->pgraph.blit.point_in; + wtf_nvidia = true; + } /* Coordinates for copying an entire line at a time */ uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.w; @@ -170,23 +176,35 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) else if (nv3->nvbase.svga.bpp == 32) size_x <<= 2; + for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { - old_position.y = nv3->pgraph.blit.point_in.y + y; - /* 32bit buffer */ buf_position = (nv3->pgraph.blit.size.w * y); - vram_position = nv3_render_get_vram_address(old_position, grobj); + /* shouldn't matter in non-wtf mode */ + vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, dst_buffer); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); + old_position.y++; + /* 32bit buffer */ } /* simply write it all back to vram */ for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { buf_position = (nv3->pgraph.blit.size.w * y); - new_position.y = nv3->pgraph.blit.point_out.y + y; - vram_position = nv3_render_get_vram_address(new_position, grobj); + + /* Trying to avoid making the above function more complex. It seems, src is used most of th etime...But this is bad... */ + if (wtf_nvidia) + { + /* Use the parameters of our dst buffer with the position of our source buffer, seriously, who was thinking of this */ + vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, src_buffer); + //vram_position = vram_position - nv3->pgraph.boffset[dst_buffer] + nv3->pgraph.boffset[src_buffer]; + } + else + vram_position = nv3_render_get_vram_address(new_position, grobj); + memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); + new_position.y++; } /* @@ -222,8 +240,26 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out blit_position.y = nv3->pgraph.blit.point_out.y; - uint32_t buf_end = ((nv3->nvbase.svga.bpp + 1) >> 3) * xsize * ysize; - //if (nv3->pgraph.boffset[dst_buffer] <= buf_end) - //nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj); + /* Figure out the Display Buffer Address from the CRTCs */ + uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) + + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) + + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; + + /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not + Apply stupid hack */ + + if (wtf_nvidia) + { + if (nv3->pgraph.boffset[src_buffer] != dba) + return; + } + else + { + if (nv3->pgraph.boffset[dst_buffer] != dba) + return; + } + + + nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, false); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 7d27a2623..6a6a8e587 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -225,6 +225,7 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro uint32_t vram_y = position.y; uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + /* uint32_t destination_buffer = 5; // 5 = just use the source buffer // src is hardcoded to 1, dst to 0. Hmm... @@ -236,7 +237,7 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro if (destination_buffer != current_buffer && destination_buffer != 5) current_buffer = destination_buffer; - +*/ uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // we have to multiply the x position by the number of bytes per pixel @@ -260,6 +261,36 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro return pixel_addr_vram; } + +/* Combine the current buffer with the pitch to get the address in the video ram for a specific position relative to a specific framebuffer */ +uint32_t nv3_render_get_vram_address_for_buffer(nv3_position_16_t position, nv3_grobj_t grobj, uint32_t buffer) +{ + uint32_t vram_x = position.x; + uint32_t vram_y = position.y; + + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; + + // we have to multiply the x position by the number of bytes per pixel + switch (framebuffer_bpp) + { + case 8: + break; + case 15: + case 16: + vram_x = position.x << 1; + break; + case 32: + vram_x = position.x << 2; + break; + } + + uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[buffer] * vram_y) + nv3->pgraph.boffset[buffer]; + + pixel_addr_vram &= nv3->nvbase.svga.vram_mask; + + return pixel_addr_vram; +} + /* Convert a dumb framebuffer address to a position. No buffer setup or anything, but just start at 0,0 for address 0. */ nv3_position_16_t nv3_render_get_dfb_position(uint32_t vram_address) { @@ -470,7 +501,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob /* Go write the pixel */ nv3_size_16_t size = {0}; size.w = size.h = 1; - nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj); + nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, true); } /* Ensure the correct monitor size */ @@ -560,14 +591,34 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) /* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check) { /* Ensure that we are in the correct mode. Modified SVGA core code */ nv3_render_ensure_screen_size(); - /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM */ - //if (nv3->nvbase.last_buffer_address > (((nv3->nvbase.svga.bpp + 1) >> 3) * xsize * ysize)) - //return; + /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM, so it can be used for s2sb's etc */ + + /* Not needed for s2sb*/ + if (run_render_check) + { + /* Figure out the Display Buffer Address from the CRTCs */ + uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) + + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) + + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; + + /* Check our destination(?) buffer */ + uint32_t dst_buffer = 0; // 5 = just use the source buffer + + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; + + /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not */ + if (nv3->pgraph.boffset[dst_buffer] != dba) + return; + } + switch (nv3->nvbase.svga.bpp) { From 371a2d269b15710d884ba74410a50f5ce2286a56 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 12 Apr 2025 14:53:42 +0100 Subject: [PATCH 169/274] Turn off ultra logging --- CMakeLists.txt | 2 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c9825b9ef..52f24f4b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ option(DISCORD "Discord Rich Presence support" option(DEBUGREGS486 "Enable debug register opeartion on 486+ CPUs" OFF) # Remove when merged, should just be -D option(NV_LOG "NVidia RIVA 128 debug logging" ON) -option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" ON) +option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) if (NV_LOG) add_compile_definitions(ENABLE_NV_LOG) diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 8f16cb015..5597e2f73 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -405,4 +405,9 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) { nv_log(": Unknown register write (address=0x%08x)\n", address); } +} + +uint32_t nv3_pramdac_read_clut(void) //4bpp/8bpp +{ + } \ No newline at end of file From 801aa4a97dd85c602be7d85bf4fa02f407d9efe1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 12 Apr 2025 17:28:04 +0100 Subject: [PATCH 170/274] Fix framebuffer size with vertical resolution > 1024 by applying large screen bit even in NV mode --- doc/nvidia_notes/status.xlsx | Bin 16043 -> 15992 bytes src/video/nv/nv3/nv3_core.c | 15 +++++++++++---- src/video/nv/nv3/render/nv3_render_core.c | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 1ddb8b34dbfc1e4e750f4992b5d7e696dbe20b39..4e02b672cb00475d41b8a9e3411d5ca4a9365291 100644 GIT binary patch delta 6573 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 delta 6645 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 vAQ0|*6EMZ}|A*hNl1i6v_Z`1z)%k_(; diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 1f786fc47..14788dcb3 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -499,18 +499,25 @@ void nv3_recalc_timings(svga_t* svga) // required for VESA resolutions, force parameters higher // only fuck around with any of this in VGAmode? + 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_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_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDE10)) svga->dispend += 0x400; + + if (svga->crtc[NV3_CRTC_REGISTER_HEB] & 0x01) + svga->hdisp += 0x100; // large screen bit + + /* if (pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA) { 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 } - + */ /* Turn off override if we are in VGA mode */ svga->override = !(pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 6a6a8e587..fc4bce4d9 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -147,7 +147,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co warning("nv3_render_downconvert: Y8 not implemented"); break; case nv3_pgraph_pixel_format_y16: - warning("nv3_render_downconvert: Y16 not implemented"); + //warning("nv3_render_downconvert: Y16 not implemented"); break; default: warning("nv3_render_downconvert_color unknown format %d", format); From afcb3392240f6502cd75ee9cdfbc09bafedf113c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 13 Apr 2025 18:36:48 +0100 Subject: [PATCH 171/274] Get rid of useless "reserved" stuff and also implement 8bpp indexed mode using CLUT. --- doc/nvidia_notes/status.xlsx | Bin 15992 -> 15953 bytes .../86box/nv/classes/vid_nv3_classes.h | 92 ++--------------- src/include/86box/nv/render/vid_nv3_render.h | 9 +- src/include/86box/nv/vid_nv3.h | 21 +++- src/video/nv/nv3/nv3_core.c | 75 ++++++++------ src/video/nv/nv3/nv3_core_arbiter.c | 22 ++-- src/video/nv/nv3/render/nv3_render_core.c | 97 ++++++++++++------ src/video/nv/nv3/subsystems/nv3_pramdac.c | 48 ++++++++- 8 files changed, 203 insertions(+), 161 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index 4e02b672cb00475d41b8a9e3411d5ca4a9365291..b73941d31dae703054ddc6f338acef9f0a12bcd4 100644 GIT binary patch delta 5487 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 * @@ -727,8 +727,16 @@ extern const device_config_t nv3_config[]; #define NV3_PRAMDAC_END 0x680FFF #define NV3_PDAC_END 0x680FFF // OPTIONAL external DAC -#define NV3_VGA_DAC_START 0x6813C6 -#define NV3_VGA_DAC_END 0x6813C9 + +#define NV3_USER_DAC_START 0x681200 +#define NV3_USER_DAC_PALETTE_START 0x6813C6 +#define NV3_USER_DAC_PIXEL_MASK 0x6813C6 +#define NV3_USER_DAC_READ_MODE_ADDRESS 0x6813C7 //bit0=read/write mode? +#define NV3_USER_DAC_WRITE_MODE_ADDRESS 0x6813C8 +#define NV3_USER_DAC_PALETTE_DATA 0x6813C9 +#define NV3_USER_DAC_PALETTE_SIZE 768 +#define NV3_USER_DAC_PALETTE_END 0x6813C9 +#define NV3_USER_DAC_END 0x681FFF #define NV3_USER_START 0x800000 // Mapping for the area where objects are submitted into the FIFO (up to 0x880000?) #define NV3_USER_END 0xFFFFFF @@ -1065,6 +1073,11 @@ typedef struct nv3_pramdac_s 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 + + uint8_t user_pixel_mask; // pixel mask for DAC lookup + uint32_t user_read_mode_address; // user read mode address + uint32_t user_write_mode_address; // user write mode address + uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entriesxr,g,b = 768 bytes } nv3_pramdac_t; /* Holds DMA channel context information */ @@ -1133,7 +1146,7 @@ typedef struct nv3_pgraph_status_s } nv3_pgraph_status_t; /* All of this B* stuff is registers at 400630..40065c and 4006a8 in reality, easier to implement it like this - BPixel = Internal Binary Representation of the pixel within the architecture + BPixel = Buffer Pixel Format */ #define NV3_BPIXEL_FORMAT 0 #define NV3_BPIXEL_FORMAT_IS_VALID 2 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 14788dcb3..e8456814f 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -49,9 +49,9 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv); bool nv3_is_svga_redirect_address(uint32_t addr) { - return (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) // VGA - || (addr >= NV3_PRMCIO_START && addr <= NV3_PRMCIO_END) // CRTC - || (addr >= NV3_VGA_DAC_START && addr <= NV3_VGA_DAC_END); // Legacy RAMDAC support(?) + return (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) // VGA + || (addr >= NV3_PRMCIO_START && addr <= NV3_PRMCIO_END) // CRTC + || (addr >= NV3_USER_DAC_START && addr <= NV3_USER_DAC_END); // Note: 6813c6-6813c9 are ignored somewhere else } // All MMIO regs are 32-bit i believe internally @@ -65,6 +65,14 @@ uint8_t nv3_mmio_read8(uint32_t addr, void* priv) // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. addr &= 0xFFFFFF; + // We need to specifically exclude this particular set of registers + // so we can write the 4/8bpp CLUT + if (addr >= NV3_USER_DAC_PALETTE_START && addr <= NV3_USER_DAC_PALETTE_END) + { + // Throw directly into PRAMDAC + return nv3_mmio_arbitrate_read(addr); + } + if (nv3_is_svga_redirect_address(addr)) { // svga writes are not logged anyway rn @@ -143,6 +151,15 @@ void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) { addr &= 0xFFFFFF; + // We need to specifically exclude this particular set of registers + // so we can write the 4/8bpp CLUT + if (addr >= NV3_USER_DAC_PALETTE_START && addr <= NV3_USER_DAC_PALETTE_END) + { + // Throw directly into PRAMDAC + nv3_mmio_arbitrate_write(addr, val); + return; + } + // This is weitek vga stuff // If we need to add more of these we can convert these to a switch statement if (nv3_is_svga_redirect_address(addr)) @@ -474,7 +491,7 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) break; default: - + break; } } @@ -493,31 +510,6 @@ void nv3_recalc_timings(svga_t* svga) svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; - // 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 - // only fuck around with any of this in VGAmode? - - 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_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_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDE10)) svga->dispend += 0x400; - - if (svga->crtc[NV3_CRTC_REGISTER_HEB] & 0x01) - svga->hdisp += 0x100; // large screen bit - - /* - if (pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA) - { - 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_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; - - } - */ /* Turn off override if we are in VGA mode */ svga->override = !(pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA); @@ -698,7 +690,7 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) addr ^= 0x60; uint8_t crtcreg = nv3->nvbase.svga.crtcreg; - uint8_t old_value; + uint8_t old_value = 0x00; // todo: // Pixel formats (8bit vs 555 vs 565) @@ -747,12 +739,33 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) case NV3_CRTC_REGISTER_RMA: nv3->pbus.rma.mode = val & NV3_CRTC_REGISTER_RMA_MODE_MAX; break; + /* Handle some large screen stuff */ + case NV3_CRTC_REGISTER_PIXELMODE: + if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VDT10)) + nv3->nvbase.svga.vtotal += 0x400; + if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VRS10)) + nv3->nvbase.svga.vblankstart += 0x400; + if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VBS10)) + nv3->nvbase.svga.vsyncstart += 0x400; + if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_HBE6)) + nv3->nvbase.svga.hdisp += 0x400; + + /* Make sure dispend and vblankstart are right if we are displaying above 1024 vert */ + if (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDE10)) + nv3->nvbase.svga.dispend += 0x400; + + break; + case NV3_CRTC_REGISTER_HEB: + if (val & 0x01) + nv3->nvbase.svga.hdisp += 0x100; + break; case NV3_CRTC_REGISTER_I2C_GPIO: uint8_t scl = !!(val & 0x20); uint8_t sda = !!(val & 0x10); // Set an I2C GPIO register i2c_gpio_set(nv3->nvbase.i2c, scl, sda); break; + } /* Recalculate the timings if we actually changed them @@ -763,7 +776,7 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" if (nv3->nvbase.svga.crtcreg < 0xE - && nv3->nvbase.svga.crtcreg > 0x10) + || nv3->nvbase.svga.crtcreg > 0x10) { nv3->nvbase.svga.fullchange = changeframecount; nv3_recalc_timings(&nv3->nvbase.svga); diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 2bb3ef8b4..99aebb5e7 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -58,9 +58,10 @@ 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; + // Ensure the addresses are dword aligned. + // I don't know why this is needed because writepriv32 is always to dword align, but it crashes if you don't do this. + if (!(address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) + address &= 0xFFFFFC; // gigantic set of if statements to send the write to the right subsystem if (address >= NV3_PMC_START && address <= NV3_PMC_END) @@ -97,7 +98,8 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) 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) + else if ((address >= NV3_PRAMDAC_START && address <= NV3_PRAMDAC_END) + || (address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) //clut ret = nv3_pramdac_read(address); else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) ret = nv3_dfb_read32(address & nv3->nvbase.svga.vram_mask, &nv3->nvbase.svga); @@ -121,9 +123,12 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. address &= 0xFFFFFF; - // 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; + + // Ensure the addresses are dword aligned. + // I don't know why this is needed because writepriv32 is always to dword align, but it crashes if you don't do this. + // Exclude the 4bpp/8bpp CLUT for this purpose + if (!(address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) + address &= 0xFFFFFC; // gigantic set of if statements to send the write to the right subsystem if (address >= NV3_PMC_START && address <= NV3_PMC_END) @@ -158,7 +163,8 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) 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) + else if ((address >= NV3_PRAMDAC_START && address <= NV3_PRAMDAC_END) + || (address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) //clut nv3_pramdac_write(address, value); else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) nv3_dfb_write32(address, value, &nv3->nvbase.svga); diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index fc4bce4d9..6eb1f50c1 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -88,6 +88,7 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) break; case nv3_pgraph_pixel_format_y8: + /* Indexed mode */ color_final.a = (color >> 8) & 0xFF; // yuv @@ -143,11 +144,11 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co packed_color |= (color.g << 20); packed_color |= (color.b << 10); break; - case nv3_pgraph_pixel_format_y8: - warning("nv3_render_downconvert: Y8 not implemented"); + case nv3_pgraph_pixel_format_y8: /* i think this is just indexed mode. since r=g=b we can just take the indexed from the r */ + packed_color = nv3_render_get_palette_index((color.r >> 2) & 0xFF); break; case nv3_pgraph_pixel_format_y16: - //warning("nv3_render_downconvert: Y16 not implemented"); + warning("nv3_render_downconvert: Y16 not implemented"); break; default: warning("nv3_render_downconvert_color unknown format %d", format); @@ -189,15 +190,24 @@ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.a << 10); } -/* Convert a rgb10 colour to a pattern colour */ -uint32_t nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1) +/* Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) */ +uint32_t nv3_render_get_palette_index(uint8_t index) +{ + uint32_t red_index = index * 3; + uint32_t green_index = red_index + 1; + uint32_t blue_index = red_index + 2; + + uint8_t red_colour = nv3->pramdac.palette[red_index]; + uint8_t green_colour = nv3->pramdac.palette[green_index]; + uint8_t blue_colour = nv3->pramdac.palette[blue_index]; + + /* Alpha is always 0xFF */ + return (0xFF << 24) | ((red_colour) << 16) | ((green_colour) << 8) | blue_colour; +} + +/* Convert a rgb10 colour to a pattern colour */ +void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1) { - /* reset the colour */ - if (!use_color1) - nv3->pgraph.pattern_color_0_rgb.r = nv3->pgraph.pattern_color_0_rgb.g = nv3->pgraph.pattern_color_0_rgb.b = 0x00; - else - nv3->pgraph.pattern_color_1_rgb.r = nv3->pgraph.pattern_color_1_rgb.g = nv3->pgraph.pattern_color_1_rgb.b = 0x00; - /* select the right pattern colour, _rgb is already in RGB10 format, so we don't need to do any conversion */ if (!use_color1) @@ -359,7 +369,6 @@ uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) /* Plots a pixel. */ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) { - uint8_t alpha = 0xFF; // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. // It seems, you are meant to @@ -372,12 +381,6 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z - /* doesn't seem*/ - nv3_color_argb_t color_data = *(nv3_color_argb_t*)&color; - - if (framebuffer_bpp == 32) - alpha = color_data.a; - int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; int32_t clip_end_y = nv3->pgraph.clip_start.y + nv3->pgraph.clip_size.y; @@ -507,7 +510,8 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob /* Ensure the correct monitor size */ void nv3_render_ensure_screen_size(void) { - bool changed = false; //doesn't check if the res is the same? + /* First check if hdisp == xsize and dispend == ysize. */ + bool changed = false; if (nv3->nvbase.svga.hdisp != nv3->nvbase.svga.monitor->mon_xsize) { @@ -521,9 +525,13 @@ void nv3_render_ensure_screen_size(void) nv3->nvbase.svga.monitor->mon_ysize = nv3->nvbase.svga.dispend; } + /* + if either changed: + -> set resolution + -> set refresh rate - this is just a rough estimation right now. we need it as we only blit what changes + */ if (changed) { - /* set refresh rate - this is just a rough estimation right now. we need it as we only blit what changes */ nv3->nvbase.refresh_time = 1 / (nv3->nvbase.pixel_clock_frequency / (double)ysize / (double)xsize); // rivatimers count in microseconds set_screen_size(xsize, ysize); } @@ -534,7 +542,15 @@ void nv3_render_ensure_screen_size(void) /* Blit to the monitor from DFB, 8bpp */ void nv3_render_current_bpp_dfb_8(uint32_t address) { + nv3_size_16_t size = {0}; + size.w = size.h = 1; + nv3_position_16_t pos = nv3_render_get_dfb_position(address); + + uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); + + *p = nv3_render_get_palette_index(data & 0xFF); } /* Blit to the monitor from DFB, 15/16bpp */ @@ -545,8 +561,6 @@ void nv3_render_current_bpp_dfb_16(uint32_t address) nv3_position_16_t pos = nv3_render_get_dfb_position(address); - //pos.x >>= 1; - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); @@ -623,8 +637,8 @@ void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t s switch (nv3->nvbase.svga.bpp) { case 4: - /* Uh we should never be here because we're in the SVGA mode */ - fatal("NV3 - Tried to render 4bpp in NV mode"); + /* Uh we should never be here because we're in the SVGA mode(?) */ + fatal("NV3 - 4bpp not implemented (not even sure if it's SVGA only)"); break; case 8: nv3_render_8bpp(pos, size, grobj); @@ -648,7 +662,36 @@ void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t s void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) { - + if (!nv3) + return; + + uint32_t vram_base; //acquired for the start of each line + uint32_t* p; + uint32_t data; + uint32_t start_x = pos.x; + + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + + for (uint32_t y = 0; y < size.h; y++) + { + /* re-set the vram address because we are basically "jumping" halfway across a line here */ + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; + + for (uint32_t x = 0; x < size.w; x++) + { + p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + + /* should just "tip over" to the next line */ + *p = nv3_render_get_palette_index(data & 0xFF); + + vram_base++; + pos.x++; + } + + pos.x = start_x; + pos.y++; + } } /* @@ -675,7 +718,6 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t x = 0; x < size.w; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; /* should just "tip over" to the next line */ @@ -714,7 +756,6 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t x = 0; x < size.w; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; /* should just "tip over" to the next line */ @@ -750,11 +791,9 @@ void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro /* re-get the vram address because we are basically "jumping" halfway across a line here */ vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - for (uint32_t x = 0; x < size.w; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; /* should just "tip over" to the next line */ diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 5597e2f73..f9f8b670c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -225,6 +225,10 @@ nv_register_t pramdac_registers[] = { 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}, + { NV3_USER_DAC_PIXEL_MASK, "PRAMDAC - User DAC Pixel Mask", NULL, NULL}, + { NV3_USER_DAC_READ_MODE_ADDRESS, "PRAMDAC - User DAC Read Mode Address", NULL, NULL}, + { NV3_USER_DAC_WRITE_MODE_ADDRESS, "PRAMDAC - User DAC Write Mode Address", NULL, NULL}, + { NV3_USER_DAC_PALETTE_DATA, "PRAMDAC - User DAC Palette Data", NULL, NULL}, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; @@ -301,6 +305,21 @@ uint32_t nv3_pramdac_read(uint32_t address) case NV3_PRAMDAC_HSERR_WIDTH: ret = nv3->pramdac.hserr_width; break; + case NV3_USER_DAC_PIXEL_MASK: + ret = nv3->pramdac.user_pixel_mask; + break; + case NV3_USER_DAC_READ_MODE_ADDRESS: + ret = nv3->pramdac.user_read_mode_address; + break; + case NV3_USER_DAC_WRITE_MODE_ADDRESS: + ret = nv3->pramdac.user_write_mode_address; + break; + case NV3_USER_DAC_PALETTE_DATA: + /* I doubt NV actually read this in their drivers, but it's worth doing anyway */ + /* Bit 1 is listed as "read or write mode" and 7:0 as "Write-only address", but NV only ever set this to 0 too, so i think this should be fine for now */ + ret = nv3->pramdac.palette[nv3->pramdac.user_read_mode_address]; + nv3->pramdac.user_read_mode_address++; + break; } } @@ -390,8 +409,31 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) nv3->pramdac.hequ_width = value; break; case NV3_PRAMDAC_HSERR_WIDTH: - nv3->pramdac.hserr_width = value; + break; + case NV3_USER_DAC_PIXEL_MASK: + nv3->pramdac.user_pixel_mask = value; + break; + case NV3_USER_DAC_READ_MODE_ADDRESS: + nv3->pramdac.user_read_mode_address = value; + break; + case NV3_USER_DAC_WRITE_MODE_ADDRESS: + + /* + This seems to get reset to 0 after 256 writes, but, the palette is 768 bytes in size. + Clearly there's some mechanism here, but I'm not sure what it is. So let's just reset if we reach 768. + */ + if (nv3->pramdac.user_write_mode_address >= NV3_USER_DAC_PALETTE_SIZE) + nv3->pramdac.user_write_mode_address = value; + + break; + case NV3_USER_DAC_PALETTE_DATA: + /* I doubt NV actually read this in their drivers, but it's worth doing anyway */ + /* Bit 1 is listed as "read or write mode" and 7:0 as "Write-only address", but NV only ever set this to 0 too, so i think this should be fine for now */ + nv3->pramdac.palette[nv3->pramdac.user_write_mode_address] = value; + + nv3->pramdac.user_write_mode_address++; + break; } } @@ -407,7 +449,3 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) } } -uint32_t nv3_pramdac_read_clut(void) //4bpp/8bpp -{ - -} \ No newline at end of file From f170901c5563d093b14180dd6c06ba1d063c1fdd Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 15 Apr 2025 01:07:41 +0100 Subject: [PATCH 172/274] some logging fixes --- src/video/nv/nv3/classes/nv3_class_010_blit.c | 6 +++--- src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c | 1 + src/video/nv/nv3/render/nv3_render_blit.c | 4 ++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 1 + src/video/nv/nv3/subsystems/nv3_pramdac.c | 3 +-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index b1dc30ea0..5f58ce6f9 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -35,19 +35,19 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_BLIT_POSITION_IN: nv3->pgraph.blit.point_in.x = (param & 0xFFFF); nv3->pgraph.blit.point_in.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB POINT_IN %04x,%04x\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); + nv_log("Method Execution: S2SB POINT_IN %d,%d\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); break; case NV3_BLIT_POSITION_OUT: nv3->pgraph.blit.point_out.x = (param & 0xFFFF); nv3->pgraph.blit.point_out.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB POINT_OUT %04x,%04x\n", nv3->pgraph.blit.point_out.x, nv3->pgraph.blit.point_out.y); + nv_log("Method Execution: S2SB POINT_OUT %d,%d\n", nv3->pgraph.blit.point_out.x, nv3->pgraph.blit.point_out.y); break; case NV3_BLIT_SIZE: /* This is the last one*/ nv3->pgraph.blit.size.w = (param & 0xFFFF); nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB Size %04x,%04x grobj_0=0x%08x\n", nv3->pgraph.blit.size.w, nv3->pgraph.blit.size.h, grobj.grobj_0); + nv_log("Method Execution: S2SB Size %d,%d grobj_0=0x%08x\n", nv3->pgraph.blit.size.w, nv3->pgraph.blit.size.h, grobj.grobj_0); /* Some of these seem to have sizes of 0, so skip */ if (nv3->pgraph.blit.size.h == 0 diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 87cf01812..5a1d08733 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -32,6 +32,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { /* We need this for a lot of methods, so may as well store it here. */ uint32_t src_buffer_id = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + switch (method_id) { diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 788cb4ca2..ed8858ee3 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -142,8 +142,8 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - nv3_position_16_t old_position; - nv3_position_16_t new_position; + nv3_position_16_t old_position = {0}; + nv3_position_16_t new_position = {0}; /* If src_buffer != dst_buffer, the positions and src/dst buffer seem to be swapped. Some kind of hardware errata (?), otherwise, I have no explanation for this behaviour. */ diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 43befe49c..20cb97076 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -85,6 +85,7 @@ nv_register_t pgraph_registers[] = { { NV3_PGRAPH_STATUS, "PGRAPH Status", NULL, NULL }, { NV3_PGRAPH_TRAPPED_ADDRESS, "PGRAPH Trapped Address", NULL, NULL }, { NV3_PGRAPH_TRAPPED_DATA, "PGRAPH Trapped Data", NULL, NULL }, + { NV3_PGRAPH_INSTANCE, "PGRAPH Object Instance", NULL, NULL}, { NV3_PGRAPH_TRAPPED_INSTANCE, "PGRAPH Trapped Object Instance", NULL, NULL }, { NV3_PGRAPH_DMA_INTR_0, "PGRAPH DMA Interrupt Status (unimplemented)", NULL, NULL }, { NV3_PGRAPH_DMA_INTR_EN_0, "PGRAPH DMA Interrupt Enable (unimplemented)", NULL, NULL }, diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index f9f8b670c..b24d3d201 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -418,14 +418,13 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) nv3->pramdac.user_read_mode_address = value; break; case NV3_USER_DAC_WRITE_MODE_ADDRESS: - /* This seems to get reset to 0 after 256 writes, but, the palette is 768 bytes in size. Clearly there's some mechanism here, but I'm not sure what it is. So let's just reset if we reach 768. */ if (nv3->pramdac.user_write_mode_address >= NV3_USER_DAC_PALETTE_SIZE) nv3->pramdac.user_write_mode_address = value; - + break; case NV3_USER_DAC_PALETTE_DATA: /* I doubt NV actually read this in their drivers, but it's worth doing anyway */ From 0a579c0775ef94d966f3aba4d96905cf310ec3c6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 17 Apr 2025 21:40:41 +0100 Subject: [PATCH 173/274] Split RIVA 128 aand RIVA 128 ZX --- src/include/86box/nv/vid_nv3.h | 3 +- src/include/86box/video.h | 2 + src/video/nv/nv3/nv3_core.c | 151 ++++++++++++++---- src/video/nv/nv3/nv3_core_config.c | 182 +++++++++++----------- src/video/nv/nv3/subsystems/nv3_pextdev.c | 8 +- src/video/vid_table.c | 2 + 6 files changed, 223 insertions(+), 125 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 5c21e1918..e7bab44ac 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -26,7 +26,8 @@ #include <86box/nv/render/vid_nv3_render.h> // The GPU base structure -extern const device_config_t nv3_config[]; +extern const device_config_t nv3_config[]; // Config for RIVA 128 (revision A/B) +extern const device_config_t nv3t_config[]; // Config for RIVA 128 ZX (revision C) #define NV3_MMIO_SIZE 0x1000000 // Max MMIO size diff --git a/src/include/86box/video.h b/src/include/86box/video.h index cd48134c2..1b6821557 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -599,6 +599,8 @@ 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; +extern const device_t nv3t_device_pci; +extern const device_t nv3t_device_agp; /* Wyse 700 */ extern const device_t wy700_device; diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e8456814f..deef4f093 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -1089,10 +1089,22 @@ void nv3_update_mappings(void) // void* nv3_init(const device_t *info) { + // set the vram amount and gpu revision + + /* We don't bother looking these up if they are nonzero. On Riva 128 ZX, they are already set by the init function (always 8 MB VRAM + Revision C0) */ + if (!nv3->nvbase.vram_amount) + nv3->nvbase.vram_amount = device_get_config_int("vram_size"); + + if (!nv3->nvbase.gpu_revision) + nv3->nvbase.gpu_revision = device_get_config_int("chip_revision"); + + /* Set log device name based on card model */ + const char* log_device_name = (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) ? "NV3T" : "NV3"; + if (device_get_config_int("nv_debug_fulllog")) - nv3->nvbase.log = log_open("NV3"); + nv3->nvbase.log = log_open(log_device_name); else - nv3->nvbase.log = log_open_cyclic("NV3"); + nv3->nvbase.log = log_open_cyclic(log_device_name); #ifdef ENABLE_NV_LOG // Allows nv_log to be used for multiple nvidia devices @@ -1103,16 +1115,26 @@ void* nv3_init(const device_t *info) // this will only be logged if ENABLE_NV_LOG_ULTRA is defined nv_log_verbose_only("ULTRA LOGGING enabled"); - // Figure out which vbios the user selected + // This depends on the bus we are using and if the gpu is rev a/b or rev c const char* vbios_id = device_get_config_bios("vbios"); const char* vbios_file = ""; - // depends on the bus we are using - if (nv3->nvbase.bus_generation == nv_bus_pci) - vbios_file = device_get_bios_file(&nv3_device_pci, vbios_id, 0); - else - vbios_file = device_get_bios_file(&nv3_device_agp, vbios_id, 0); + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + { + if (nv3->nvbase.bus_generation == nv_bus_pci) + vbios_file = device_get_bios_file(&nv3t_device_pci, vbios_id, 0); + else + vbios_file = device_get_bios_file(&nv3t_device_agp, vbios_id, 0); + } + else + { + if (nv3->nvbase.bus_generation == nv_bus_pci) + vbios_file = device_get_bios_file(&nv3_device_pci, vbios_id, 0); + else + vbios_file = device_get_bios_file(&nv3_device_agp, vbios_id, 0); + } + int32_t err = rom_init(&nv3->nvbase.vbios, vbios_file, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); @@ -1125,39 +1147,52 @@ void* nv3_init(const device_t *info) else nv_log("Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); - // set the vram amount and gpu revision - nv3->nvbase.vram_amount = device_get_config_int("vram_size"); - nv3->nvbase.gpu_revision = device_get_config_int("chip_revision"); - // set up the bus and start setting up SVGA core if (nv3->nvbase.bus_generation == nv_bus_pci) { - nv_log("using PCI bus\n"); + nv_log("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, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - + /* Initialise the right revision of the card */ if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + { + svga_init(&nv3t_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_pci); - else + } + else + { + svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_pci); + } } - else if (nv3->nvbase.bus_generation == nv_bus_agp_1x) + else if (nv3->nvbase.bus_generation == nv_bus_agp_1x + || nv3->nvbase.bus_generation == nv_bus_agp_2x) { - nv_log("using AGP 1X bus\n"); + nv_log("Using AGP 1X/2X 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, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - + /* Initialise the right revision of the card */ if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + { + svga_init(&nv3t_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_agp); - else + } + else + { + svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, + nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_agp); + } } // set vram @@ -1191,7 +1226,7 @@ void* nv3_init(const device_t *info) return nv3; } -// This function simply allocates ram and sets the bus to pci before initialising. +// RIVA 128 PCI initialisation function: This function simply allocates the device struct, and sets the bus to PCI before initialising. void* nv3_init_pci(const device_t* info) { nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); @@ -1200,7 +1235,7 @@ void* nv3_init_pci(const device_t* info) return nv3; } -// This function simply allocates ram and sets the bus to agp before initialising. +// RIVA 128 AGP initialisation function: This function simply allocates the device struct, and sets the bus to AGP before initialising. void* nv3_init_agp(const device_t* info) { nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); @@ -1209,6 +1244,32 @@ void* nv3_init_agp(const device_t* info) return nv3; } +// RIVA 128 ZX PCI initialisation function: This function simply allocates the device struct, and sets the bus to PCI before initialising. +// It also sets the GPU revision to C0 because NV3T config doesn't let you configure the rev (there were multiple steppings, but it's basically irrelevant), +// and sets RAM to 8 MB (the only supported config on ZX cards) +void* nv3t_init_pci(const device_t* info) +{ + nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); + nv3->nvbase.bus_generation = nv_bus_pci; + nv3->nvbase.gpu_revision = NV3_PCI_CFG_REVISION_C00; + nv3->nvbase.vram_amount = NV3_VRAM_SIZE_8MB; + nv3_init(info); + return nv3; +} + +// RIVA 128 ZX AGP initialisation function: This function simply allocates the device struct, and sets the bus to AGP before initialising. +// It also sets the GPU revision to C0 because NV3T config doesn't let you configure the rev (there were multiple steppings, but it's basically irrelevant) +// and sets RAM to 8 MB (the only supported config on ZX cards) +void* nv3t_init_agp(const device_t* info) +{ + nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); + nv3->nvbase.bus_generation = nv_bus_agp_2x; // Riva 128 ZX is AGP2X + nv3->nvbase.gpu_revision = NV3_PCI_CFG_REVISION_C00; + nv3->nvbase.vram_amount = NV3_VRAM_SIZE_8MB; + nv3_init(info); + return nv3; +} + void nv3_close(void* priv) { // Shut down logging @@ -1252,8 +1313,8 @@ int32_t nv3_available(void) // 2MB or 4MB VRAM const device_t nv3_device_pci = { - .name = "NVidia RIVA 128 (NV3) PCI", - .internal_name = "nv3", + .name = "nVIDIA RIVA 128 (NV3) PCI", + .internal_name = "nv3_pci", .flags = DEVICE_PCI, .local = 0, .init = nv3_init_pci, @@ -1269,7 +1330,7 @@ const device_t nv3_device_pci = // 2MB or 4MB VRAM const device_t nv3_device_agp = { - .name = "NVidia RIVA 128 (NV3) AGP", + .name = "nVIDIA RIVA 128 (NV3) AGP", .internal_name = "nv3_agp", .flags = DEVICE_AGP, .local = 0, @@ -1279,4 +1340,38 @@ const device_t nv3_device_agp = .force_redraw = nv3_force_redraw, .available = nv3_available, .config = nv3_config, +}; + +// NV3T (RIVA 128 ZX) +// PCI +// 8MB VRAM +const device_t nv3t_device_pci = +{ + .name = "nVIDIA RIVA 128 ZX (NV3T) PCI", + .internal_name = "nv3t_pci", + .flags = DEVICE_PCI, + .local = 0, + .init = nv3t_init_pci, + .close = nv3_close, + .speed_changed = nv3_speed_changed, + .force_redraw = nv3_force_redraw, + .available = nv3_available, + .config = nv3t_config, +}; + +// NV3T (RIVA 128) +// AGP +// 2MB or 4MB VRAM +const device_t nv3t_device_agp = +{ + .name = "nVIDIA RIVA 128 ZX (NV3T) AGP", + .internal_name = "nv3t_agp", + .flags = DEVICE_AGP, + .local = 0, + .init = nv3t_init_agp, + .close = nv3_close, + .speed_changed = nv3_speed_changed, + .force_redraw = nv3_force_redraw, + .available = nv3_available, + .config = nv3t_config, }; \ No newline at end of file diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index a229ca3c7..ec7d46e30 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -32,107 +32,42 @@ const device_config_t nv3_config[] = // VBIOS type configuration { .name = "vbios", - #ifndef RELEASE_BUILD - .description = "VBIOS", - #else .description = "Model", - #endif .type = CONFIG_BIOS, .default_string = "NV3_VBIOS_ERAZOR_V15403", .default_int = 0, .bios = { { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1997-09-30] ELSA VICTORY Erazor VBE 3.0 DDC2B DPMS Video BIOS Ver. 1.47.01 (ZZ/ A/00)", .files_no = 1, - #else - .name = "[RIVA 128] ELSA Victory Erazor v1.47.01", .files_no = 1, - #endif + .name = "ELSA VICTORY Erazor - Version 1.47.00", .files_no = 1, .internal_name = "NV3_VBIOS_ERAZOR_V14700", .files = {NV3_VBIOS_ERAZOR_V14700, ""} }, { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1998-02-06] ELSA VICTORY Erazor Ver. 1.54.03 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, - #else - .name = "[RIVA 128] ELSA Victory Erazor v1.54.03", .files_no = 1, - #endif + .name = "ELSA VICTORY Erazor - Version 1.54.03", .files_no = 1, .internal_name = "NV3_VBIOS_ERAZOR_V15403", .files = {NV3_VBIOS_ERAZOR_V15403, ""} }, { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1998-05-04] ELSA VICTORY Erazor Ver. 1.55.00 [WD/VBE30/DDC2B/DPMS]", .files_no = 1, - #else - .name = "[RIVA 128] ELSA Victory Erazor v1.55.00", .files_no = 1, - #endif + .name = "ELSA VICTORY Erazor - Version 1.55.00", .files_no = 1, .internal_name = "NV3_VBIOS_ERAZOR_V15500", .files = {NV3_VBIOS_ERAZOR_V15500, ""} }, { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1998-01-14] Diamond Multimedia Systems, Inc. Viper V330 Version 1.62-CO", .files_no = 1, - #else - .name = "[RIVA 128] Diamond Viper V330", .files_no = 1, - #endif + .name = "Diamond Viper V330 - Version 1.62-CO", .files_no = 1, .internal_name = "NV3_VBIOS_DIAMOND_V330_V162", .files = {NV3_VBIOS_DIAMOND_V330_V162, ""}, }, { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1997-09-06] ASUS AGP/3DP-V3000 BIOS 1.51B", .files_no = 1, - #else - .name = "[RIVA 128] ASUS AGP/3DP-V3000", .files_no = 1, - #endif + .name = "ASUS AGP/3DP-V3000 - Version 1.51B", .files_no = 1, .internal_name = "NV3_VBIOS_ASUS_V3000_V151", .files = {NV3_VBIOS_ASUS_V3000_V151, ""}, }, { - #ifndef RELEASE_BUILD - .name = "[NV3 - 1997-12-17] STB Velocity 128 (RIVA 128) Ver.1.82", .files_no = 1, - #else - .name = "[RIVA 128] STB Velocity 128", .files_no = 1, - #endif + .name = "STB Velocity 128 - Version 1.82", .files_no = 1, .internal_name = "NV3_VBIOS_STB_V128_V182", .files = {NV3_VBIOS_STB_V128_V182, ""}, }, - { - #ifndef RELEASE_BUILD - .name = "[NV3T - 1998-09-15] Diamond Multimedia Viper V330 8M BIOS - Version 1.82B", .files_no = 1, - #else - .name = "[RIVA 128 ZX] Diamond Multimedia Viper V330 8MB", .files_no = 1, - #endif - .internal_name = "NV3T_VBIOS_DIAMOND_V330_V182B", - .files = {NV3T_VBIOS_DIAMOND_V330_V182B, ""}, - }, - { - #ifndef RELEASE_BUILD - .name = "[NV3T - 1998-08-04] ASUS AGP-V3000 ZXTV BIOS - V1.70D.03", .files_no = 1, - #else - .name = "[RIVA 128 ZX] ASUS AGP-V3000 ZXTV", .files_no = 1, - #endif - .internal_name = "NV3T_VBIOS_ASUS_V170", - .files = {NV3T_VBIOS_ASUS_V170, ""}, - }, - { - #ifndef RELEASE_BUILD - .name = "[NV3T - 1998-07-30] RIVA 128 ZX BIOS - V1.71B-N", .files_no = 1, - #else - .name = "[RIVA 128 ZX] Nvidia Reference BIOS v1.71", .files_no = 1, - #endif - .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V171", - .files = {NV3T_VBIOS_REFERENCE_CEK_V171, ""}, - }, - - { - #ifndef RELEASE_BUILD - .name = "[NV3T+SGRAM - 1998-08-15] RIVA 128 ZX BIOS - V1.72B", .files_no = 1, - #else - .name = "[RIVA 128 ZX] Nvidia Reference BIOS v1.72", .files_no = 1, - #endif - .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V172", - .files = {NV3T_VBIOS_REFERENCE_CEK_V172, ""}, - }, } }, // Memory configuration @@ -143,22 +78,17 @@ const device_config_t nv3_config[] = .default_int = NV3_VRAM_SIZE_4MB, .selection = { -#ifndef RELEASE_BUILD // I thought this was never released, but it seems that at least one was released: // The card was called the "NEC G7AGK" { .description = "2 MB", .value = NV3_VRAM_SIZE_2MB, }, -#endif + { .description = "4 MB", .value = NV3_VRAM_SIZE_4MB, }, - { - .description = "8 MB", - .value = NV3_VRAM_SIZE_8MB, - }, } }, @@ -169,30 +99,94 @@ const device_config_t nv3_config[] = .default_int = NV3_PCI_CFG_REVISION_B00, .selection = { -#ifndef RELEASE_BUILD { - .description = "NV3/STG3000 Engineering Sample / Stepping A0 (January 1997) with integrated PAUDIO sound card", -#else - .description = "RIVA 128 Prototype (Revision A)", -#endif + .description = "RIVA 128 Prototype (Revision A; January 1997)", .value = NV3_PCI_CFG_REVISION_A00, }, -#ifndef RELEASE_BUILD { - .description = "RIVA 128 / Stepping B0 (October 1997)", -#else .description = "RIVA 128 (Revision B)", -#endif .value = NV3_PCI_CFG_REVISION_B00, }, -#ifndef RELEASE_BUILD - { - .description = "NV3T - RIVA 128 ZX / Stepping C0 (March 1998)", -#else - .description = "RIVA 128 ZX (Revision C)", -#endif - .value = NV3_PCI_CFG_REVISION_C00, - }, + } + }, + // Multithreading configuration + { + + .name = "pgraph_threads", +#ifndef RELEASE_BUILD + .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", +#else + .description = "Render threads", +#endif + .type = CONFIG_SELECTION, + .default_int = 1, // todo: change later + .selection = + { + { + .description = "1 thread (Only use if issues appear with more threads)", + .value = 1, + }, + { + .description = "2 threads", + .value = 2, + }, + { + .description = "4 threads", + .value = 4, + }, + { + .description = "8 threads", + .value = 8, + }, + }, + }, +#ifndef RELEASE_BUILD + { + .name = "nv_debug_fulllog", + .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", + .type = CONFIG_BINARY, + .default_int = 0, + }, +#endif + { + .type = CONFIG_END + } +}; + +const device_config_t nv3t_config[] = +{ + // VBIOS type configuration + { + .name = "vbios", + .description = "Model", + .type = CONFIG_BIOS, + .default_string = "NV3T_VBIOS_DIAMOND_V330_V182B", + .default_int = 0, + .bios = + { + { + + .name = "Diamond Multimedia Viper V330 8M BIOS - Version 1.82B", .files_no = 1, + .internal_name = "NV3T_VBIOS_DIAMOND_V330_V182B", + .files = {NV3T_VBIOS_DIAMOND_V330_V182B, ""}, + }, + { + .name = "ASUS AGP-V3000 ZXTV BIOS - V1.70D.03", .files_no = 1, + .internal_name = "NV3T_VBIOS_ASUS_V170", + .files = {NV3T_VBIOS_ASUS_V170, ""}, + }, + { + .name = "NVidia Reference BIOS - V1.71B-N", .files_no = 1, + + .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V171", + .files = {NV3T_VBIOS_REFERENCE_CEK_V171, ""}, + }, + + { + .name = "NVidia Reference BIOS - V1.72B", .files_no = 1, + .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V172", + .files = {NV3T_VBIOS_REFERENCE_CEK_V172, ""}, + }, } }, // Multithreading configuration diff --git a/src/video/nv/nv3/subsystems/nv3_pextdev.c b/src/video/nv/nv3/subsystems/nv3_pextdev.c index 9aa17698f..c89d540c3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ b/src/video/nv/nv3/subsystems/nv3_pextdev.c @@ -134,8 +134,12 @@ void nv3_pextdev_write(uint32_t address, uint32_t value) // special consideration for straps if (address == NV3_PSTRAPS) { - warning("Huh? Tried to write to the straps (value=%d). Something is wrong...\n", nv3->pextdev.straps); - return; + /* For some reason, all RIVA 128 ZX VBIOSes try to write to the straps. So only indicate this as a problem and return on Rev A/B */ + if (nv3->nvbase.gpu_revision != NV3_PCI_CFG_REVISION_C00) + { + warning("Huh? Tried to write to the straps (value=%d). Something is wrong...\n", nv3->pextdev.straps); + return; + } } // if the register actually exists diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 997c79304..202c2dd22 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -253,6 +253,8 @@ video_cards[] = { { .device = &voodoo_3_3500_si_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &nv3t_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &nv3t_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = NULL, .flags = VIDEO_FLAG_TYPE_NONE } // clang-format on }; From 52c36d5644fe41cea0ee247c4be2ec8029025d71 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 18 Apr 2025 19:05:28 +0100 Subject: [PATCH 174/274] Implement a bizarre register that nvidia did not even internally document. WTF? --- src/include/86box/nv/render/vid_nv3_render.h | 2 +- src/include/86box/nv/vid_nv3.h | 20 ++-- src/video/nv/nv3/nv3_core.c | 4 +- src/video/nv/nv3/render/nv3_render_blit.c | 104 +++++++------------ src/video/nv/nv3/render/nv3_render_core.c | 67 ++++++------ 5 files changed, 84 insertions(+), 113 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 369230347..4b4f966ea 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,7 +18,7 @@ #pragma once /* Core */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check); +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer); void nv3_render_current_bpp_dfb_8(uint32_t address); void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index e7bab44ac..fcfd2de72 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -79,9 +79,9 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_VBIOS_DEFAULT NV3_VBIOS_ERAZOR_V15403 // Temporary, will be loaded from settings -#define NV3_VRAM_SIZE_2MB 0x200000 // 2MB -#define NV3_VRAM_SIZE_4MB 0x400000 // 4MB -#define NV3_VRAM_SIZE_8MB 0x800000 // NV3T only +#define NV3_VRAM_SIZE_2MB 0x200000 // 2MB +#define NV3_VRAM_SIZE_4MB 0x400000 // 4MB +#define NV3_VRAM_SIZE_8MB 0x800000 // NV3T only // There is also 1mb supported by the card but it was never used // PCI config @@ -676,10 +676,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PRMCIO_START 0x601000 -#define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_MONO 0x6013B4 // Current CRTC Register Index - Monochrome -#define NV3_PRMCIO_CRTC_REGISTER_CUR_MONO 0x6013B5 // Currently Selected CRTC Register - Monochrome -#define NV3_PRMCIO_CRTC_REGISTER_CUR_INDEX_COLOR 0x6013D4 // Current CRTC Register Index - Colour -#define NV3_PRMCIO_CRTC_REGISTER_CUR_COLOR 0x6013D5 + #define NV3_PRMCIO_END 0x601FFF #define NV3_PDAC_START 0x680000 // OPTIONAL external DAC @@ -787,6 +784,9 @@ extern const device_config_t nv3t_config[]; // Confi // CRTC/CIO (0x3b0-0x3df) +#define NV3_CRTC_REGISTER_INDEX_MONO 0x3B4 +#define NV3_CRTC_REGISTER_MONO 0x3B5 // Currently Selected CRTC Register - Monochrome + #define NV3_CRTC_DATA_OUT 0x3C0 #define NV3_CRTC_MISCOUT 0x3C2 @@ -796,6 +796,8 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_INDEX 0x3D4 #define NV3_CRTC_REGISTER_CURRENT 0x3D5 +#define NV3_CRTC_REGISTER_WTF 0x3D8 + // These are standard (0-18h) #define NV3_CRTC_REGISTER_HTOTAL 0x00 #define NV3_CRTC_REGISTER_HDISPEND 0x01 @@ -1076,8 +1078,8 @@ typedef struct nv3_pramdac_s uint32_t hserr_width; // horizontal sync error width uint8_t user_pixel_mask; // pixel mask for DAC lookup - uint32_t user_read_mode_address; // user read mode address - uint32_t user_write_mode_address; // user write mode address + uint32_t user_read_mode_address; // user read mode address + uint32_t user_write_mode_address; // user write mode address uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entriesxr,g,b = 768 bytes } nv3_pramdac_t; diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index deef4f093..2875f3cbc 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -196,7 +196,6 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - nv3_svga_write(real_address, val & 0xFF, nv3); nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); @@ -632,6 +631,9 @@ uint8_t nv3_svga_read(uint16_t addr, void* priv) case NV3_CRTC_REGISTER_INDEX: ret = nv3->nvbase.svga.crtcreg; break; + case NV3_CRTC_REGISTER_WTF: + ret = 0x08; // Required to not freeze in certain situations on v3.xx drivers + break; case NV3_CRTC_REGISTER_CURRENT: // Support the extended NVIDIA CRTC register range switch (nv3->nvbase.svga.crtcreg) diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index ed8858ee3..8b7d9cbf8 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -131,10 +131,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.h) * (sizeof(uint32_t) * nv3->pgraph.blit.size.w)); /* First calculate our source and destination buffer */ - uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - - bool wtf_nvidia = false; - + uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; uint32_t dst_buffer = 0; // 5 = just use the source buffer if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; @@ -142,22 +139,10 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - nv3_position_16_t old_position = {0}; - nv3_position_16_t new_position = {0}; + bool cross_buffer_blit = (nv3->pgraph.boffset[src_buffer] != nv3->pgraph.boffset[dst_buffer]); - /* If src_buffer != dst_buffer, the positions and src/dst buffer seem to be swapped. - Some kind of hardware errata (?), otherwise, I have no explanation for this behaviour. */ - if (nv3->pgraph.boffset[src_buffer] == nv3->pgraph.boffset[dst_buffer]) - { - old_position = nv3->pgraph.blit.point_in; - new_position = nv3->pgraph.blit.point_out; - } - else - { - old_position = nv3->pgraph.blit.point_out; - new_position = nv3->pgraph.blit.point_in; - wtf_nvidia = true; - } + nv3_position_16_t old_position = nv3->pgraph.blit.point_in; + nv3_position_16_t new_position = nv3->pgraph.blit.point_out; /* Coordinates for copying an entire line at a time */ uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.w; @@ -175,13 +160,12 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) size_x <<= 1; else if (nv3->nvbase.svga.bpp == 32) size_x <<= 2; - for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { buf_position = (nv3->pgraph.blit.size.w * y); /* shouldn't matter in non-wtf mode */ - vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, dst_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, src_buffer); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); old_position.y++; @@ -192,16 +176,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) { buf_position = (nv3->pgraph.blit.size.w * y); - - /* Trying to avoid making the above function more complex. It seems, src is used most of th etime...But this is bad... */ - if (wtf_nvidia) - { - /* Use the parameters of our dst buffer with the position of our source buffer, seriously, who was thinking of this */ - vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, src_buffer); - //vram_position = vram_position - nv3->pgraph.boffset[dst_buffer] + nv3->pgraph.boffset[src_buffer]; - } - else - vram_position = nv3_render_get_vram_address(new_position, grobj); + vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, dst_buffer); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); new_position.y++; @@ -216,50 +191,43 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) nv3_size_16_t blit_size = {0}; /* Change the smallest area of the screen that moved */ - if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) - blit_size.w = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.w; - else if (nv3->pgraph.blit.point_out.x < nv3->pgraph.blit.point_in.x) - blit_size.w = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.w; + + if (cross_buffer_blit) + { + blit_position = nv3->pgraph.blit.point_out; + blit_size = nv3->pgraph.blit.size; + } else - blit_size.w = nv3->pgraph.blit.size.w; + { + if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) + blit_size.w = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.w; + else if (nv3->pgraph.blit.point_out.x < nv3->pgraph.blit.point_in.x) + blit_size.w = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.w; + else + blit_size.w = nv3->pgraph.blit.size.w; - if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) - blit_size.h = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.h; - else if (nv3->pgraph.blit.point_out.y < nv3->pgraph.blit.point_in.y) - blit_size.h = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.h; - else - blit_size.h = nv3->pgraph.blit.size.h; + if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) + blit_size.h = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.h; + else if (nv3->pgraph.blit.point_out.y < nv3->pgraph.blit.point_in.y) + blit_size.h = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.h; + else + blit_size.h = nv3->pgraph.blit.size.h; - if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) - blit_position.x = nv3->pgraph.blit.point_in.x; - else if (nv3->pgraph.blit.point_out.x <= nv3->pgraph.blit.point_in.x) // equals case, just use out - blit_position.x = nv3->pgraph.blit.point_out.x; + if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) + blit_position.x = nv3->pgraph.blit.point_in.x; + else if (nv3->pgraph.blit.point_out.x <= nv3->pgraph.blit.point_in.x) // equals case, just use out + blit_position.x = nv3->pgraph.blit.point_out.x; - if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) - blit_position.y = nv3->pgraph.blit.point_in.y; - else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out - blit_position.y = nv3->pgraph.blit.point_out.y; + if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) + blit_position.y = nv3->pgraph.blit.point_in.y; + else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out + blit_position.y = nv3->pgraph.blit.point_out.y; - - /* Figure out the Display Buffer Address from the CRTCs */ - uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) - + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) - + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; + } /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not Apply stupid hack */ + - if (wtf_nvidia) - { - if (nv3->pgraph.boffset[src_buffer] != dba) - return; - } - else - { - if (nv3->pgraph.boffset[dst_buffer] != dba) - return; - } - - - nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, false); + nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, false, true); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 6eb1f50c1..916717984 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -31,10 +31,10 @@ #include <86box/utils/video_stdlib.h> /* Functions only used in this translation unit */ -void nv3_render_8bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); -void nv3_render_15bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); -void nv3_render_16bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); -void nv3_render_32bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj); +void nv3_render_8bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_15bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_16bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_32bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! @@ -235,19 +235,6 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro uint32_t vram_y = position.y; uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - /* - uint32_t destination_buffer = 5; // 5 = just use the source buffer - - // src is hardcoded to 1, dst to 0. Hmm... - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) destination_buffer = 0; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) destination_buffer = 1; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) destination_buffer = 2; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) destination_buffer = 3; - - if (destination_buffer != current_buffer - && destination_buffer != 5) - current_buffer = destination_buffer; -*/ uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // we have to multiply the x position by the number of bytes per pixel @@ -504,7 +491,7 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob /* Go write the pixel */ nv3_size_16_t size = {0}; size.w = size.h = 1; - nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, true); + nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, true, false); } /* Ensure the correct monitor size */ @@ -605,7 +592,7 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) /* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check) +void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer) { /* Ensure that we are in the correct mode. Modified SVGA core code */ nv3_render_ensure_screen_size(); @@ -641,16 +628,16 @@ void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t s fatal("NV3 - 4bpp not implemented (not even sure if it's SVGA only)"); break; case 8: - nv3_render_8bpp(pos, size, grobj); + nv3_render_8bpp(pos, size, grobj, use_destination_buffer); break; case 15: - nv3_render_15bpp(pos, size, grobj); + nv3_render_15bpp(pos, size, grobj, use_destination_buffer); break; case 16: - nv3_render_16bpp(pos, size, grobj); + nv3_render_16bpp(pos, size, grobj, use_destination_buffer); break; case 32: - nv3_render_32bpp(pos, size, grobj); + nv3_render_32bpp(pos, size, grobj, use_destination_buffer); break; } @@ -660,7 +647,7 @@ void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t s Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, indexed 8 bits per pixel format */ -void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -675,7 +662,10 @@ void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grob for (uint32_t y = 0; y < size.h; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; + if (use_destination_buffer) + vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + else + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) { @@ -698,7 +688,7 @@ void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grob Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 15 bits per pixel format */ -void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -713,7 +703,10 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; + if (use_destination_buffer) + vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + else + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) { @@ -736,7 +729,7 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 16 bits per pixel format */ -void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -750,8 +743,11 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { - /* re-get the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; + /* re-set the vram address because we are basically "jumping" halfway across a line here */ + if (use_destination_buffer) + vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + else + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; for (uint32_t x = 0; x < size.w; x++) { @@ -774,7 +770,7 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 32 bits per pixel format */ -void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj) +void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -788,9 +784,12 @@ void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro for (uint32_t y = 0; y < size.h; y++) { - /* re-get the vram address because we are basically "jumping" halfway across a line here */ - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - + /* re-set the vram address because we are basically "jumping" halfway across a line here */ + if (use_destination_buffer) + vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + else + vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; + for (uint32_t x = 0; x < size.w; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; From 1e5bdfbd160789f7eaaff568a4c233f97661091b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 21 Apr 2025 13:50:06 +0100 Subject: [PATCH 175/274] Fix OOB write in PGRAPH_CONTEXT_CACHE --- CMakePresets.json | 3 ++- src/video/nv/nv3/subsystems/nv3_pgraph.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 30feab8ab..b2ed64e97 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -36,7 +36,8 @@ "name": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON" + "NV_LOG": "ON", + "NV_LOG_ULTRA": "ON" }, "inherits": "base" }, diff --git a/src/video/nv/nv3/subsystems/nv3_pgraph.c b/src/video/nv/nv3/subsystems/nv3_pgraph.c index 20cb97076..dbca46216 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -478,7 +478,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) && address <= NV3_PGRAPH_CONTEXT_CACHE(NV3_PGRAPH_CONTEXT_CACHE_SIZE)) { // Addresses should be aligned to 4 bytes. - uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); + uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)) >> 2; nv_log_verbose_only("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); nv3->pgraph.context_cache[entry] = value; From 2a511b8dc7a53a5deca1da872f5e79c7e5e96f42 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 22 Apr 2025 00:16:03 +0100 Subject: [PATCH 176/274] various fixes --- src/include/86box/nv/vid_nv3.h | 8 +++- src/video/nv/nv3/subsystems/nv3_pfifo.c | 57 +++++++++++++++---------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index fcfd2de72..00eeb0df1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -299,6 +299,9 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PFIFO_CACHE1_STATUS_EMPTY 4 // 1 if ramro is empty #define NV3_PFIFO_CACHE1_STATUS_FULL 8 #define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 +#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE 0 +#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE_IDLE 0x00 +#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING 0x01 #define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 #define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 #define NV3_PFIFO_CACHE1_DMA_CONFIG_2 0x3228 @@ -308,8 +311,11 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_AGP 0x03 // The type of bus we are sending over // Why does a gpu need its own translation lookaside buffer and pagetable format. Are they crazy +// Seems to be the same format as the notifier engine #define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 -#define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // Base of pagetableor DMA +#define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // pagetable entry for dma +#define NV3_PFIFO_CACHE1_DMA_TLB_PTE_IS_PRESENT 1 +#define NV3_PFIFO_CACHE1_DMA_TLB_FRAME_ADDRESS 12 // 31:12 #define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA #define NV3_PFIFO_CACHE1_PULL0 0x3240 //todo: merge stuff diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 69b834df9..c5f13ae1b 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -61,14 +61,14 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put", NULL, NULL }, { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put", NULL, NULL }, //Cache1 exclusive stuff - { NV3_PFIFO_CACHE1_DMA_CONFIG_0, "PFIFO - Cache1 DMA Config0"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_1, "PFIFO - Cache1 DMA Config1"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_2, "PFIFO - Cache1 DMA Config2"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_3, "PFIFO - Cache1 DMA Config3"}, - { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status - PROBABLY TRIGGERING DMA"}, - { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA Translation Lookaside Buffer - Pagetable Base"}, - { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA Status"}, - { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA Status"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_0, "PFIFO - Cache1 DMA Access (bit 0: is running, bit 4: is busy)"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_1, "PFIFO - Cache1 DMA Length"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_2, "PFIFO - Cache1 DMA Address"}, + { NV3_PFIFO_CACHE1_DMA_CONFIG_3, "PFIFO - Cache1 DMA Target Node"}, + { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status"}, + { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA TLB - Pagetable Base"}, + { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA TLB - Pagetable Entry (31:12 - Frame Address; bit 0 - Is Present)"}, + { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA TLB - Tag"}, //Runout { NV3_PFIFO_RUNOUT_GET, "PFIFO Runout Get Address [8:3 if 512b, otherwise 12:3]"}, { NV3_PFIFO_RUNOUT_PUT, "PFIFO Runout Put Address [8:3 if 512b, otherwise 12:3]"}, @@ -354,31 +354,42 @@ void nv3_pfifo_trigger_dma_if_required(void) bool cache1_dma = false; /* Check that DMA is enabled */ - if (nv3->pfifo.cache1_settings.dma_state + if ((nv3->pfifo.cache1_settings.dma_state & NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING) && nv3->pfifo.cache1_settings.dma_enabled) { uint32_t bytes_to_send = nv3->pfifo.cache1_settings.dma_length; uint32_t where_to_send = nv3->pfifo.cache1_settings.dma_address; - uint32_t target_node = nv3->pfifo.cache1_settings.dma_target_node; //2=pci, 3=agp. What does this even do + uint32_t target_node = nv3->pfifo.cache1_settings.dma_target_node; //2=pci, 3=agp. /* Pagetable information */ - uint32_t tlb_pt_base = nv3->pfifo.cache1_settings.dma_tlb_pt_base; - uint32_t tlb_pt_entry = nv3->pfifo.cache1_settings.dma_tlb_pte; + uint32_t tlb_pt_base = nv3->pfifo.cache1_settings.dma_tlb_pt_base; + uint32_t tlb_pt_entry = nv3->pfifo.cache1_settings.dma_tlb_pte; // notify_obj_page uint32_t tlb_pt_tag = nv3->pfifo.cache1_settings.dma_tlb_tag; // 0xFFFFFFFF usually? + + /* + going to treat the format the same as notifiers + */ + if (!(tlb_pt_entry & NV3_PFIFO_CACHE1_DMA_TLB_PTE_IS_PRESENT)) + { + warning("NV3: Tried to DMA to a non-existent page! Big Problem!"); + return; + } - /* PUSH - System to GPU (?) */ - if (nv3->pfifo.cache1_settings.push0) - { - /* PULL - GPU to System */ - nv_log("Initiating System to NV DMA - Probably we are trying to notify\n"); - } - else if (nv3->pfifo.cache1_settings.pull0) - { - /* PULL - GPU to System */ - nv_log("Initiating NV to System DMA - Probably we are trying to notify\n"); - } + uint32_t final_page_base = tlb_pt_entry & 0xFFFFF000; /* pull out 31:12 */ + + /* + page size is 0x1000 + */ + uint32_t final_address = final_page_base + (tlb_pt_entry << 10); //x86 page size is 0x1000 + + nv_log_verbose_only("DMA Engine: DMA to %08x length=%08x", final_address, bytes_to_send); + + dma_bm_write() } + + //we're done + nv3->pfifo.cache1_settings.dma_state &= ~NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING; } void nv3_pfifo_write(uint32_t address, uint32_t val) From 00910aa6217cf3ef47499c7cff5bf63cb7deb7c1 Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Tue, 22 Apr 2025 08:44:10 -0500 Subject: [PATCH 177/274] Fix build --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index c5f13ae1b..32b6230ea 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -384,7 +384,7 @@ void nv3_pfifo_trigger_dma_if_required(void) nv_log_verbose_only("DMA Engine: DMA to %08x length=%08x", final_address, bytes_to_send); - dma_bm_write() + //dma_bm_write() } From 26ee11e8686d68a29fb8b50c52076e70cb1371f8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 23 Apr 2025 01:28:54 +0100 Subject: [PATCH 178/274] Refactor: Merge nv3_position_16 and nv3_size_16 into nv3_coord_16, same for bigy and combined: remove dead code --- .../86box/nv/classes/vid_nv3_classes.h | 114 ++++++++---------- src/include/86box/nv/render/vid_nv3_render.h | 16 +-- src/include/86box/nv/vid_nv3.h | 60 ++++----- .../nv/nv3/classes/nv3_class_007_rectangle.c | 6 +- .../classes/nv3_class_00c_win95_gdi_text.c | 48 ++++---- src/video/nv/nv3/classes/nv3_class_010_blit.c | 11 +- .../nv/nv3/classes/nv3_class_011_image.c | 12 +- src/video/nv/nv3/nv3_core.c | 6 +- src/video/nv/nv3/render/nv3_render_blit.c | 46 ++++--- src/video/nv/nv3/render/nv3_render_core.c | 76 ++++++------ .../nv/nv3/render/nv3_render_primitives.c | 26 ++-- src/video/nv/nv3/subsystems/nv3_pfifo.c | 7 +- 12 files changed, 203 insertions(+), 225 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 3b4f3b4a3..163b6464e 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -271,36 +271,29 @@ typedef struct nv3_color_argb_s uint8_t b; } nv3_color_argb_t; -/* Generic 16-bit position*/ -typedef struct nv3_position_16_s +/* Generic 16-bit coordinate*/ +typedef struct nv3_coord_16_s { uint16_t x; uint16_t y; -} nv3_position_16_t; +} nv3_coord_16_t; /* A big position format with 30:16 = y, 15:11 = nothing, 10:0 = x */ -typedef struct nv3_position_16_bigy_s +typedef struct nv3_coord_16_bigy_s { // WHOSE IDEA WAS THIS? uint16_t x : 11; uint8_t reserved : 5; uint16_t y : 15; bool reserved2 : 1; -} nv3_position_16_bigy_t; - -/* Generic 16-bit size */ -typedef struct nv3_size_16_s -{ - uint16_t w; - uint16_t h; -} nv3_size_16_t; +} nv3_coord_16_bigy_t; /* Generic 32-bit colour + 16-bit position */ -typedef struct nv3_color_and_position_16_s +typedef struct nv3_color_and_coord_16_s { nv3_color_expanded_t color; - nv3_position_16_t points; -} nv3_color_and_position_16_t; + nv3_coord_16_t points; +} nv3_color_and_coord_16_t; /* Generic 16-bit clip region */ typedef struct nv3_clip_16_s @@ -410,8 +403,8 @@ typedef struct nv3_object_class_005 uint32_t set_notify; // Set notifier /* 16-bit precision */ - nv3_position_16_t position; - nv3_size_16_t size; + nv3_coord_16_t position; + nv3_coord_16_t size; } nv3_clipping_rectangle_t; @@ -442,8 +435,8 @@ typedef struct nv3_object_class_007 nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint32_t set_notify; // Set notifier uint32_t color; // The colour of the object. - nv3_position_16_t position[16]; // The positions of up to 16 rectangles. - nv3_size_16_t size[16]; // The sizes of up to 16 rectangles + nv3_coord_16_t position[16]; // The positions of up to 16 rectangles. + nv3_coord_16_t size[16]; // The sizes of up to 16 rectangles } nv3_rectangle_t; @@ -451,7 +444,7 @@ typedef struct nv3_object_class_007 typedef struct nv3_object_class_008_cpoint_s { nv3_color_expanded_t color; // argb-format 32-bit color - nv3_position_16_t position; // position + nv3_coord_16_t position; // position } nv3_object_class_008_cpoint_t; /* @@ -465,7 +458,7 @@ typedef struct nv3_object_class_008 nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint32_t set_notify; // Set notifier nv3_color_expanded_t color; // argb? - nv3_position_16_t point[16]; // Boring points + nv3_coord_16_t point[16]; // Boring points nv3_position_32_t point32[16]; // Allows you to have points with full 32-bit precision nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points! } nv3_point_t; @@ -473,8 +466,8 @@ typedef struct nv3_object_class_008 /* Normal line... */ typedef struct nv3_object_class_009_line_s { - nv3_position_16_t start; // presumably unless it's in reverse order...TODO: check the order - nv3_position_16_t end; + nv3_coord_16_t start; // presumably unless it's in reverse order...TODO: check the order + nv3_coord_16_t end; } nv3_object_class_009_line_t; @@ -506,7 +499,7 @@ typedef struct nv3_object_class_009 nv3_object_class_009_line32_t line32[8]; nv3_object_class_009_line_t polyline[32]; nv3_object_class_009_line32_t polyline32[16]; - nv3_color_and_position_16_t cpolyline[16]; // List of line points and colours. + nv3_color_and_coord_16_t cpolyline[16]; // List of line points and colours. } nv3_line_t; /* @@ -528,7 +521,7 @@ typedef struct nv3_object_class_00A nv3_object_class_009_line32_t line32[8]; nv3_object_class_009_line_t polyline[32]; nv3_object_class_009_line32_t polyline32[16]; - nv3_color_and_position_16_t cpolyline[16]; // List of line points and colours. + nv3_color_and_coord_16_t cpolyline[16]; // List of line points and colours. } nv3_lin_t; @@ -545,7 +538,7 @@ typedef struct nv3_object_class_00B uint32_t set_notify; // Set notifier nv3_color_expanded_t color; // argb? // The points of the triangle. - nv3_position_16_t points[3]; + nv3_coord_16_t points[3]; // Another way of filling out the points of the triangle uint32_t x0; @@ -555,10 +548,10 @@ typedef struct nv3_object_class_00B uint32_t y2; uint32_t x2; - nv3_position_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? + nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? nv3_position_32_t mesh32[16]; - nv3_color_and_position_16_t ctriangle[3]; // Triangle with colour - nv3_color_and_position_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours + nv3_color_and_coord_16_t ctriangle[3]; // Triangle with colour + nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours } nv3_triangle_t; /* @@ -567,7 +560,6 @@ typedef struct nv3_object_class_00B Also 0x4C in context IDs. GDI text acceleration for Windows 95. - How the fuck does this even work? */ typedef struct nv3_object_class_00C { @@ -575,8 +567,8 @@ typedef struct nv3_object_class_00C nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint32_t set_notify; // Set notifier uint32_t color_a; // Color for Clip A - nv3_position_16_t rect_a_position[64]; - nv3_size_16_t rect_a_size[64]; + nv3_coord_16_t rect_a_position[64]; + nv3_coord_16_t rect_a_size[64]; /* Clipped rect */ nv3_clip_16_t clip_b; uint32_t color_b; // Color for Clip B @@ -584,23 +576,23 @@ typedef struct nv3_object_class_00C /* Unclipped transparent bitmap */ nv3_clip_16_t clip_c; uint32_t color1_c; - nv3_size_16_t size_c; - nv3_position_16_t point_c; + nv3_coord_16_t size_c; + nv3_coord_16_t point_c; uint32_t bitmap_c[128]; /* Clipped transparent bitmap */ nv3_clip_16_t clip_d; uint32_t color1_d; - nv3_size_16_t size_in_d; - nv3_size_16_t size_out_d; - nv3_position_16_t point_d; + nv3_coord_16_t size_in_d; + nv3_coord_16_t size_out_d; + nv3_coord_16_t point_d; uint32_t bitmap_d[128]; /* Clipped 1bpp bitmap */ nv3_clip_16_t clip_e; uint32_t color0_e; uint32_t color1_e; - nv3_size_16_t size_in_e; - nv3_size_16_t size_out_e; - nv3_position_16_t point_e; + nv3_coord_16_t size_in_e; + nv3_coord_16_t size_out_e; + nv3_coord_16_t point_e; uint32_t bitmap_e[128]; } nv3_win95_text_t; @@ -637,14 +629,14 @@ typedef struct nv3_object_class_00E { nv3_class_ctx_switch_method_t set_notify_ctx_dma; uint32_t set_notify; - nv3_position_16_t clip_0; - nv3_size_16_t clip_1; - nv3_position_16_t rectangle_out_0; - nv3_size_16_t rectangle_out_1; + nv3_coord_16_t clip_0; + nv3_coord_16_t clip_1; + nv3_coord_16_t rectangle_out_0; + nv3_coord_16_t rectangle_out_1; // Calculus in a graphics card uint32_t delta_du_dx; uint32_t delta_dv_dy; - nv3_size_16_t size; // can be size_y if YUV420 + nv3_coord_16_t size; // can be size_y if YUV420 uint32_t pitch; uint32_t offset; uint32_t point; @@ -663,16 +655,16 @@ typedef struct nv3_object_class_00E 0x?? (drivers) Also 0x50 in context IDs. - Represents a blit. + Represents a screen to screen blit. I'm still figuring it out. */ typedef struct nv3_object_class_010 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; uint32_t set_notify; - nv3_position_16_t point_in; - nv3_position_16_t point_out; - nv3_size_16_t size; + nv3_coord_16_t point_in; + nv3_coord_16_t point_out; + nv3_coord_16_t size; } nv3_blit_t; /* @@ -686,9 +678,9 @@ typedef struct nv3_object_class_011 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; uint32_t set_notify; - nv3_position_16_t point; - nv3_size_16_t size; - nv3_size_16_t size_in; + nv3_coord_16_t point; + nv3_coord_16_t size; + nv3_coord_16_t size_in; nv3_color_expanded_t color[32]; // The colour to use } nv3_image_t; @@ -706,9 +698,9 @@ typedef struct nv3_object_class_012 uint32_t set_notify; nv3_color_expanded_t color_0; nv3_color_expanded_t color_1; - nv3_position_16_t point; // Top left(?) of the bitmap - nv3_size_16_t size; - nv3_size_16_t size_in; + nv3_coord_16_t point; // Top left(?) of the bitmap + nv3_coord_16_t size; + nv3_coord_16_t size_in; uint32_t monochrome_bitmap[32]; } nv3_bitmap_t; @@ -726,8 +718,8 @@ typedef struct nv3_object_class_014 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; uint32_t set_notify; - nv3_position_16_t point; - nv3_size_16_t size; + nv3_coord_16_t point; + nv3_coord_16_t size; uint32_t image_pitch; // bytes per row uint32_t image_start; } nv3_image_to_memory_t; @@ -744,11 +736,11 @@ typedef struct nv3_object_class_015 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; uint32_t set_notify; - nv3_size_16_t size_in; + nv3_coord_16_t size_in; uint32_t delta_dx_du; uint32_t delta_dy_dv; - nv3_position_16_t clip_0; - nv3_size_16_t clip_1; + nv3_coord_16_t clip_0; + nv3_coord_16_t clip_1; uint32_t point12d4; /* todo: fraction struct */ // no reserve needed } nv3_stretched_image_from_cpu_t; @@ -1154,7 +1146,7 @@ typedef struct nv3_object_class_018 uint32_t set_notify; nv3_d3d5_control_out_t control_out; nv3_d3d5_alpha_control_t alpha_control; - nv3_position_16_t point; + nv3_coord_16_t point; nv3_zeta_buffer_t zeta[8]; } nv3_point_with_zeta_buffer_t; diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 4b4f966ea..795bae979 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,20 +18,20 @@ #pragma once /* Core */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer); +void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer); void nv3_render_current_bpp_dfb_8(uint32_t address); void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); /* Pixel */ -void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj); -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj); -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj); +void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj); +uint8_t nv3_render_read_pixel_8(nv3_coord_16_t position, nv3_grobj_t grobj); +uint16_t nv3_render_read_pixel_16(nv3_coord_16_t position, nv3_grobj_t grobj); +uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj); /* Address */ -uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_get_vram_address_for_buffer(nv3_position_16_t position, nv3_grobj_t grobj, uint32_t buffer); +uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj); +uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer); /* Colour Conversion */ uint32_t nv3_render_get_palette_index(uint8_t index); // Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) @@ -43,7 +43,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); /* Primitives */ -void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj); // Render an A (unclipped) GDI rect +void nv3_render_rect(nv3_coord_16_t position, nv3_coord_16_t size, uint32_t color, nv3_grobj_t grobj); // Render an A (unclipped) GDI rect void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj); // Render a B (clipped) GDI rect. /* Chroma */ diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 00eeb0df1..c27af6a1d 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -834,7 +834,7 @@ extern const device_config_t nv3t_config[]; // Confi // These are nvidia, licensed from weitek (25-63) -#define NV3_CRTC_REGISTER_RPC0 0x19 // What does this mean? +#define NV3_CRTC_REGISTER_RPC0 0x19 // 7:5 - [10:8] of CRTC. 4:0 - [20:16] of 21-bit display buffer address #define NV3_CRTC_REGISTER_RPC1 0x1A // What does this mean? #define NV3_CRTC_REGISTER_READ_BANK 0x1D #define NV3_CRTC_REGISTER_WRITE_BANK 0x1E @@ -1199,10 +1199,10 @@ typedef struct nv3_pgraph_s uint32_t abs_uclip_ymin; uint32_t abs_uclip_ymax; // Canvas stuff - nv3_position_16_bigy_t src_canvas_min; - nv3_position_16_bigy_t src_canvas_max; - nv3_position_16_bigy_t dst_canvas_min; - nv3_position_16_bigy_t dst_canvas_max; + nv3_coord_16_bigy_t src_canvas_min; + nv3_coord_16_bigy_t src_canvas_max; + nv3_coord_16_bigy_t dst_canvas_min; + nv3_coord_16_bigy_t dst_canvas_max; // Pattern stuff nv3_color_expanded_t pattern_color_0_rgb; // ignore alpha uint32_t pattern_color_0_alpha; // only 7:0 relevant @@ -1224,13 +1224,13 @@ typedef struct nv3_pgraph_s uint32_t notifier; bool notify_pending; // Determines if a notification is pending. /* Are these even used */ - nv3_position_16_bigy_t clip0_min; - nv3_position_16_bigy_t clip0_max; - nv3_position_16_bigy_t clip1_min; - nv3_position_16_bigy_t clip1_max; + nv3_coord_16_bigy_t clip0_min; + nv3_coord_16_bigy_t clip0_max; + nv3_coord_16_bigy_t clip1_min; + nv3_coord_16_bigy_t clip1_max; /* idk */ - nv3_position_16_t clip_start; // Start of the clipping region - nv3_position_16_t clip_size; // Size of the clipping region. + nv3_coord_16_t clip_start; // Start of the clipping region + nv3_coord_16_t clip_size; // Size of the clipping region. bool fifo_access; // Determines if PGRAPH can access PFIFO. nv3_pgraph_status_t status; // Current status of the 3D engine. uint32_t trapped_address; @@ -1253,12 +1253,12 @@ typedef struct nv3_pgraph_s struct nv3_object_class_00C win95_gdi_text; /* These are here so we can hold the current state of the image draw */ uint32_t win95_gdi_text_bit_count; - nv3_position_16_t win95_gdi_text_current_position; + nv3_coord_16_t win95_gdi_text_current_position; struct nv3_object_class_00D m2mf; struct nv3_object_class_00E scaled_image_from_memory; struct nv3_object_class_010 blit; struct nv3_object_class_011 image; - nv3_position_16_t image_current_position; /* This is here so we can hold the current state of the image */ + nv3_coord_16_t image_current_position; /* This is here so we can hold the current state of the image */ struct nv3_object_class_012 bitmap; struct nv3_object_class_014 transfer2memory; struct nv3_object_class_015 stretched_image_from_cpu; @@ -1439,26 +1439,26 @@ typedef struct nv3_s nv_base_t nvbase; // Base Nvidia structure // Config - nv3_straps_t straps; - nv3_pci_config_t pci_config; + nv3_straps_t straps; // OEM Configuration + nv3_pci_config_t pci_config; // PCI Configuration // 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 submission + 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 submission - 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_ramin_ramht_t ramht; // hashtable for PGRAPH objects - nv3_ramin_ramro_t ramro; // anti-fuckup mechanism for idiots who fucked up the FIFO submission - nv3_ramin_ramfc_t ramfc; // context for unused channels - nv3_ramin_ramau_t ramau; // auxillary weirdnes - nv3_ramin_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 + 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_ramin_ramht_t ramht; // hashtable for PGRAPH objects + nv3_ramin_ramro_t ramro; // anti-fuckup mechanism for idiots who fucked up the FIFO submission + nv3_ramin_ramfc_t ramfc; // context for unused channels + nv3_ramin_ramau_t ramau; // auxillary weirdnes + nv3_ramin_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; diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c index 6dfdf281e..d1273a84a 100644 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c @@ -44,10 +44,10 @@ void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // If the size is submitted, render it. if (method_id & 0x04) { - nv3->pgraph.rectangle.size[index].w = param & 0xFFFF; - nv3->pgraph.rectangle.size[index].h = (param >> 16) & 0xFFFF; + nv3->pgraph.rectangle.size[index].x = param & 0xFFFF; + nv3->pgraph.rectangle.size[index].y = (param >> 16) & 0xFFFF; - nv_log("Method Execution: Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].w, nv3->pgraph.rectangle.size[index].h, nv3->pgraph.rectangle.color); + nv_log("Method Execution: Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].x, nv3->pgraph.rectangle.size[index].y, nv3->pgraph.rectangle.color); nv3_render_rect(nv3->pgraph.rectangle.position[index], nv3->pgraph.rectangle.size[index], nv3->pgraph.rectangle.color, grobj); } diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c index c1085fe89..64d76c278 100644 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c @@ -60,11 +60,11 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: GDI-C Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color1_c); break; case NV3_W95TXT_C_CLIP_SIZE: - nv3->pgraph.win95_gdi_text.size_c.w = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_c.h = ((param >> 16) & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_c.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_c.y = ((param >> 16) & 0xFFFF); nv3->pgraph.win95_gdi_text_bit_count = 0; - nv_log("Method Execution: GDI-C Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_c.w, nv3->pgraph.win95_gdi_text.size_c.h); + nv_log("Method Execution: GDI-C Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_c.x, nv3->pgraph.win95_gdi_text.size_c.y); break; case NV3_W95TXT_C_CLIP_POSITION: nv3->pgraph.win95_gdi_text.point_c.x = (param & 0xFFFF); @@ -104,14 +104,14 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: GDI-D Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; case NV3_W95TXT_D_CLIP_SIZE_IN: - nv3->pgraph.win95_gdi_text.size_in_d.w = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_in_d.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_d.w, nv3->pgraph.win95_gdi_text.size_in_d.h); + nv3->pgraph.win95_gdi_text.size_in_d.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_in_d.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-D Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_d.x, nv3->pgraph.win95_gdi_text.size_in_d.y); break; case NV3_W95TXT_D_CLIP_SIZE_OUT: - nv3->pgraph.win95_gdi_text.size_out_d.w = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_out_d.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h); + nv3->pgraph.win95_gdi_text.size_out_d.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_out_d.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-D Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_d.x, nv3->pgraph.win95_gdi_text.size_out_d.y); break; case NV3_W95TXT_D_CLIP_POSITION: nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); @@ -144,14 +144,14 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: GDI-E Color1 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); break; case NV3_W95TXT_E_CLIP_SIZE_IN: - nv3->pgraph.win95_gdi_text.size_in_e.w = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_in_e.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_e.w, nv3->pgraph.win95_gdi_text.size_in_e.h); + nv3->pgraph.win95_gdi_text.size_in_e.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_in_e.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_e.x, nv3->pgraph.win95_gdi_text.size_in_e.y); break; case NV3_W95TXT_E_CLIP_SIZE_OUT: - nv3->pgraph.win95_gdi_text.size_out_e.w = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_out_e.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_e.w, nv3->pgraph.win95_gdi_text.size_out_e.h); + nv3->pgraph.win95_gdi_text.size_out_e.x = (param & 0xFFFF); + nv3->pgraph.win95_gdi_text.size_out_e.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: GDI-E Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_e.x, nv3->pgraph.win95_gdi_text.size_out_e.y); break; case NV3_W95TXT_E_CLIP_POSITION: nv3->pgraph.win95_gdi_text.point_e.x = (param & 0xFFFF); @@ -173,11 +173,11 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ // If the size is submitted, render it. if (method_id & 0x04) { - nv3->pgraph.win95_gdi_text.rect_a_size[index].w = (param >> 16) & 0xFFFF; - nv3->pgraph.win95_gdi_text.rect_a_size[index].h = param & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_size[index].x = (param >> 16) & 0xFFFF; + nv3->pgraph.win95_gdi_text.rect_a_size[index].y = param & 0xFFFF; - nv_log("Method Execution: Rect GDI-A%d Size=%d,%d", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].w, - nv3->pgraph.win95_gdi_text.rect_a_size[index].h); + nv_log("Method Execution: Rect GDI-A%d Size=%d,%d", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].x, + nv3->pgraph.win95_gdi_text.rect_a_size[index].y); nv3_render_rect(nv3->pgraph.win95_gdi_text.rect_a_position[index], nv3->pgraph.win95_gdi_text.rect_a_size[index], nv3->pgraph.win95_gdi_text.color_a, grobj); @@ -231,7 +231,7 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Mammoth logger! */ nv_log("Method Execution: Rect GDI-C%d Data=%08x Size%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_c.w, nv3->pgraph.win95_gdi_text.size_c.h, + index, param, nv3->pgraph.win95_gdi_text.size_c.x, nv3->pgraph.win95_gdi_text.size_c.y, nv3->pgraph.win95_gdi_text.point_c.x, nv3->pgraph.win95_gdi_text.point_c.y, nv3->pgraph.win95_gdi_text.color1_c, nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.right, @@ -249,8 +249,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Mammoth logger! */ nv_log("Method Execution: Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_in_d.w, nv3->pgraph.win95_gdi_text.size_in_d.h, - nv3->pgraph.win95_gdi_text.size_out_d.w, nv3->pgraph.win95_gdi_text.size_out_d.h, + index, param, nv3->pgraph.win95_gdi_text.size_in_d.x, nv3->pgraph.win95_gdi_text.size_in_d.y, + nv3->pgraph.win95_gdi_text.size_out_d.x, nv3->pgraph.win95_gdi_text.size_out_d.y, nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y, nv3->pgraph.win95_gdi_text.color1_d, nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, @@ -268,8 +268,8 @@ void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ /* Mammoth logger! */ nv_log("Method Execution: Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_in_e.w, nv3->pgraph.win95_gdi_text.size_in_e.h, - nv3->pgraph.win95_gdi_text.size_out_e.w, nv3->pgraph.win95_gdi_text.size_out_e.h, + index, param, nv3->pgraph.win95_gdi_text.size_in_e.x, nv3->pgraph.win95_gdi_text.size_in_e.y, + nv3->pgraph.win95_gdi_text.size_out_e.x, nv3->pgraph.win95_gdi_text.size_out_e.y, nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y, nv3->pgraph.win95_gdi_text.color1_e, nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.top, nv3->pgraph.win95_gdi_text.clip_e.bottom); diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c index 5f58ce6f9..ed1ac465b 100644 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ b/src/video/nv/nv3/classes/nv3_class_010_blit.c @@ -45,14 +45,9 @@ void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; case NV3_BLIT_SIZE: /* This is the last one*/ - nv3->pgraph.blit.size.w = (param & 0xFFFF); - nv3->pgraph.blit.size.h = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB Size %d,%d grobj_0=0x%08x\n", nv3->pgraph.blit.size.w, nv3->pgraph.blit.size.h, grobj.grobj_0); - - /* Some of these seem to have sizes of 0, so skip */ - if (nv3->pgraph.blit.size.h == 0 - && nv3->pgraph.blit.size.w == 0) - return; + nv3->pgraph.blit.size.x = (param & 0xFFFF); + nv3->pgraph.blit.size.y = ((param >> 16) & 0xFFFF); + nv_log("Method Execution: S2SB Size %d,%d grobj_0=0x%08x\n", nv3->pgraph.blit.size.x, nv3->pgraph.blit.size.y, grobj.grobj_0); nv3_render_blit_screen2screen(grobj); diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c index bd5dfa6d3..1eda15df7 100644 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ b/src/video/nv/nv3/classes/nv3_class_011_image.c @@ -39,15 +39,15 @@ void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; /* Seems to allow scaling of the bitblt. */ case NV3_IMAGE_SIZE: - nv3->pgraph.image.size.w = (param & 0xFFFF); - nv3->pgraph.image.size.h = (param >> 16); - nv_log("Method Execution: Image Size (Clip)=%d,%d\n", nv3->pgraph.image.size.w, nv3->pgraph.image.size.h); + nv3->pgraph.image.size.x = (param & 0xFFFF); + nv3->pgraph.image.size.y = (param >> 16); + nv_log("Method Execution: Image Size (Clip)=%d,%d\n", nv3->pgraph.image.size.x, nv3->pgraph.image.size.y); break; case NV3_IMAGE_SIZE_IN: - nv3->pgraph.image.size_in.w = (param & 0xFFFF); - nv3->pgraph.image.size_in.h = (param >> 16); + nv3->pgraph.image.size_in.x = (param & 0xFFFF); + nv3->pgraph.image.size_in.y = (param >> 16); nv3->pgraph.image_current_position = nv3->pgraph.image.point; - nv_log("Method Execution: Image SizeIn=%d,%d\n", nv3->pgraph.image.size_in.w, nv3->pgraph.image.size_in.h); + nv_log("Method Execution: Image SizeIn=%d,%d\n", nv3->pgraph.image.size_in.x, nv3->pgraph.image.size_in.y); break; default: if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 2875f3cbc..ad032abe9 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -271,7 +271,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) ret = (NV_PCI_DEVICE_NV3 >> 8); break; - // various capabilities + // various capabilities enabled by default // IO space enabled // Memory space enabled // Bus master enabled @@ -281,7 +281,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) // 66Mhz FSB capable case PCI_REG_COMMAND_L: - ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L] ; // we actually respond to the fucking + ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L]; break; case PCI_REG_COMMAND_H: @@ -536,7 +536,7 @@ void nv3_recalc_timings(svga_t* svga) case NV3_CRTC_REGISTER_PIXELMODE_16BPP: /* This is some sketchy shit that is an attempt at an educated guess at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, - this is what's causing it. Possibly fucking *ReactOS* of all things */ + this is what's causing it. Possibly fucking up *ReactOS* of all things */ if ((svga->crtc[NV3_CRTC_REGISTER_VRETRACESTART] >> 1) & 0x01) svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; else diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 8b7d9cbf8..6d29965d4 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -34,10 +34,9 @@ void nv3_class_011_check_line_bounds(void) { uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; - //uint32_t relative_y = nv3->pgraph.image_current_position.y - nv3->pgraph.image.point.y; - /* In theory, relative_y should never be exceeded...because it only submits enough pixels to render the image*/ - if (relative_x >= nv3->pgraph.image.size_in.w) + /* In the case of class 0x11 there is no requirement to check for relative_y because we have exceeded the size of the image */ + if (relative_x >= nv3->pgraph.image.size_in.x) { nv3->pgraph.image_current_position.y++; nv3->pgraph.image_current_position.x = nv3->pgraph.image.point.x; @@ -47,16 +46,13 @@ void nv3_class_011_check_line_bounds(void) /* Renders an image from cpu */ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) { - // shift left by 2 because it's 4 bits per size.. - uint32_t current_buffer = (nv3->pgraph.context_switch >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - /* todo: a lot of stuff */ uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; /* Some extra data is sent as padding, we need to clip it off using size_out */ - uint16_t clip_x = nv3->pgraph.image.point.x + nv3->pgraph.image.size.w; + uint16_t clip_x = nv3->pgraph.image.point.x + nv3->pgraph.image.size.x; /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ /* the reverse order is due to the endianness */ switch (nv3->nvbase.svga.bpp) @@ -126,9 +122,9 @@ uint32_t nv3_s2sb_line_buffer[NV3_MAX_HORIZONTAL_SIZE*NV3_MAX_VERTICAL_SIZE] = { void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { - if (nv3->pgraph.blit.size.w < NV3_MAX_HORIZONTAL_SIZE - && nv3->pgraph.blit.size.h < NV3_MAX_VERTICAL_SIZE) - memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.h) * (sizeof(uint32_t) * nv3->pgraph.blit.size.w)); + if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE + && nv3->pgraph.blit.size.y < NV3_MAX_VERTICAL_SIZE) + memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.y) * (sizeof(uint32_t) * nv3->pgraph.blit.size.x)); /* First calculate our source and destination buffer */ uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; @@ -141,11 +137,11 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) bool cross_buffer_blit = (nv3->pgraph.boffset[src_buffer] != nv3->pgraph.boffset[dst_buffer]); - nv3_position_16_t old_position = nv3->pgraph.blit.point_in; - nv3_position_16_t new_position = nv3->pgraph.blit.point_out; + nv3_coord_16_t old_position = nv3->pgraph.blit.point_in; + nv3_coord_16_t new_position = nv3->pgraph.blit.point_out; /* Coordinates for copying an entire line at a time */ - uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.w; + uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.x; /* Read the old pixel into the line buffer Assumption: All data is sent in an unpacked format. In the case of an NVIDIA GPU this means that all data is sent 32 bits at a time regardless of if @@ -161,9 +157,9 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) else if (nv3->nvbase.svga.bpp == 32) size_x <<= 2; - for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) + for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) { - buf_position = (nv3->pgraph.blit.size.w * y); + buf_position = (nv3->pgraph.blit.size.x * y); /* shouldn't matter in non-wtf mode */ vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, src_buffer); @@ -173,9 +169,9 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) } /* simply write it all back to vram */ - for (int32_t y = 0; y < nv3->pgraph.blit.size.h; y++) + for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) { - buf_position = (nv3->pgraph.blit.size.w * y); + buf_position = (nv3->pgraph.blit.size.x * y); vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, dst_buffer); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); @@ -187,8 +183,8 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) We also need to update all of the areas of the screen that moved. */ - nv3_position_16_t blit_position = {0}; - nv3_size_16_t blit_size = {0}; + nv3_coord_16_t blit_position = {0}; + nv3_coord_16_t blit_size = {0}; /* Change the smallest area of the screen that moved */ @@ -200,18 +196,18 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) else { if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) - blit_size.w = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.w; + blit_size.x = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.x; else if (nv3->pgraph.blit.point_out.x < nv3->pgraph.blit.point_in.x) - blit_size.w = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.w; + blit_size.x = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.x; else - blit_size.w = nv3->pgraph.blit.size.w; + blit_size.x = nv3->pgraph.blit.size.x; if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) - blit_size.h = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.h; + blit_size.y = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.y; else if (nv3->pgraph.blit.point_out.y < nv3->pgraph.blit.point_in.y) - blit_size.h = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.h; + blit_size.y = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.y; else - blit_size.h = nv3->pgraph.blit.size.h; + blit_size.y = nv3->pgraph.blit.size.y; if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) blit_position.x = nv3->pgraph.blit.point_in.x; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 916717984..d03ee9481 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -31,10 +31,10 @@ #include <86box/utils/video_stdlib.h> /* Functions only used in this translation unit */ -void nv3_render_8bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_15bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_16bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_32bpp(nv3_position_16_t position, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_8bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_15bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_16bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_32bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! @@ -229,7 +229,7 @@ void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_ } /* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ -uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t grobj) +uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; @@ -260,7 +260,7 @@ uint32_t nv3_render_get_vram_address(nv3_position_16_t position, nv3_grobj_t gro /* Combine the current buffer with the pitch to get the address in the video ram for a specific position relative to a specific framebuffer */ -uint32_t nv3_render_get_vram_address_for_buffer(nv3_position_16_t position, nv3_grobj_t grobj, uint32_t buffer) +uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; @@ -289,9 +289,9 @@ uint32_t nv3_render_get_vram_address_for_buffer(nv3_position_16_t position, nv3_ } /* Convert a dumb framebuffer address to a position. No buffer setup or anything, but just start at 0,0 for address 0. */ -nv3_position_16_t nv3_render_get_dfb_position(uint32_t vram_address) +nv3_coord_16_t nv3_render_get_dfb_position(uint32_t vram_address) { - nv3_position_16_t pos = {0}; + nv3_coord_16_t pos = {0}; uint32_t pitch = nv3->nvbase.svga.hdisp; @@ -321,7 +321,7 @@ nv3_position_16_t nv3_render_get_dfb_position(uint32_t vram_address) } /* Read an 8bpp pixel from the framebuffer. */ -uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj) +uint8_t nv3_render_read_pixel_8(nv3_coord_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit uint32_t vram_address = nv3_render_get_vram_address(position, grobj); @@ -330,7 +330,7 @@ uint8_t nv3_render_read_pixel_8(nv3_position_16_t position, nv3_grobj_t grobj) } /* Read an 16bpp pixel from the framebuffer. */ -uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) +uint16_t nv3_render_read_pixel_16(nv3_coord_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit uint32_t vram_address = nv3_render_get_vram_address(position, grobj); @@ -342,7 +342,7 @@ uint16_t nv3_render_read_pixel_16(nv3_position_16_t position, nv3_grobj_t grobj) } /* Read an 16bpp pixel from the framebuffer. */ -uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) +uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj) { // hope you call it with the right bit uint32_t vram_address = nv3_render_get_vram_address(position, grobj); @@ -354,16 +354,12 @@ uint32_t nv3_render_read_pixel_32(nv3_position_16_t position, nv3_grobj_t grobj) } /* Plots a pixel. */ -void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grobj_t grobj) +void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj) { // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. // It seems, you are meant to - /* put this here for debugging + it may be needed later. */ - #ifdef DEBUG - uint8_t color_format_object = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_COLOR_FORMAT) & 0x07; - #endif bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z @@ -489,8 +485,8 @@ void nv3_render_write_pixel(nv3_position_16_t position, uint32_t color, nv3_grob } /* Go write the pixel */ - nv3_size_16_t size = {0}; - size.w = size.h = 1; + nv3_coord_16_t size = {0}; + size.x = size.y = 1; nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, true, false); } @@ -529,10 +525,10 @@ void nv3_render_ensure_screen_size(void) /* Blit to the monitor from DFB, 8bpp */ void nv3_render_current_bpp_dfb_8(uint32_t address) { - nv3_size_16_t size = {0}; - size.w = size.h = 1; + nv3_coord_16_t size = {0}; + size.x = size.y = 1; - nv3_position_16_t pos = nv3_render_get_dfb_position(address); + nv3_coord_16_t pos = nv3_render_get_dfb_position(address); uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); @@ -543,10 +539,10 @@ void nv3_render_current_bpp_dfb_8(uint32_t address) /* Blit to the monitor from DFB, 15/16bpp */ void nv3_render_current_bpp_dfb_16(uint32_t address) { - nv3_size_16_t size = {0}; - size.w = size.h = 1; + nv3_coord_16_t size = {0}; + size.x = size.y = 1; - nv3_position_16_t pos = nv3_render_get_dfb_position(address); + nv3_coord_16_t pos = nv3_render_get_dfb_position(address); uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); @@ -564,10 +560,10 @@ void nv3_render_current_bpp_dfb_16(uint32_t address) /* Blit to the monitor from DFB, 32bpp */ void nv3_render_current_bpp_dfb_32(uint32_t address) { - nv3_size_16_t size = {0}; - size.w = size.h = 1; + nv3_coord_16_t size = {0}; + size.x = size.y = 1; - nv3_position_16_t pos = nv3_render_get_dfb_position(address); + nv3_coord_16_t pos = nv3_render_get_dfb_position(address); uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); @@ -592,7 +588,7 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) /* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer) +void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer) { /* Ensure that we are in the correct mode. Modified SVGA core code */ nv3_render_ensure_screen_size(); @@ -647,7 +643,7 @@ void nv3_render_current_bpp(svga_t *svga, nv3_position_16_t pos, nv3_size_16_t s Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, indexed 8 bits per pixel format */ -void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_8bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -659,7 +655,7 @@ void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grob p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - for (uint32_t y = 0; y < size.h; y++) + for (uint32_t y = 0; y < size.y; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) @@ -667,7 +663,7 @@ void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grob else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - for (uint32_t x = 0; x < size.w; x++) + for (uint32_t x = 0; x < size.x; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; @@ -688,7 +684,7 @@ void nv3_render_8bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grob Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 15 bits per pixel format */ -void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_15bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -700,7 +696,7 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - for (uint32_t y = 0; y < size.h; y++) + for (uint32_t y = 0; y < size.y; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) @@ -708,7 +704,7 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - for (uint32_t x = 0; x < size.w; x++) + for (uint32_t x = 0; x < size.x; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; @@ -729,7 +725,7 @@ void nv3_render_15bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 16 bits per pixel format */ -void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_16bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -741,7 +737,7 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - for (uint32_t y = 0; y < size.h; y++) + for (uint32_t y = 0; y < size.y; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) @@ -749,7 +745,7 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - for (uint32_t x = 0; x < size.w; x++) + for (uint32_t x = 0; x < size.x; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; @@ -770,7 +766,7 @@ void nv3_render_16bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 32 bits per pixel format */ -void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_32bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { if (!nv3) return; @@ -782,7 +778,7 @@ void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - for (uint32_t y = 0; y < size.h; y++) + for (uint32_t y = 0; y < size.y; y++) { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) @@ -790,7 +786,7 @@ void nv3_render_32bpp(nv3_position_16_t pos, nv3_size_16_t size, nv3_grobj_t gro else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - for (uint32_t x = 0; x < size.w; x++) + for (uint32_t x = 0; x < size.x; x++) { p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c index c89fd2d6e..e2d088c3f 100644 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ b/src/video/nv/nv3/render/nv3_render_primitives.c @@ -29,15 +29,15 @@ #include <86box/nv/vid_nv3.h> #include <86box/utils/video_stdlib.h> -void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t color, nv3_grobj_t grobj) +void nv3_render_rect(nv3_coord_16_t position, nv3_coord_16_t size, uint32_t color, nv3_grobj_t grobj) { - nv3_position_16_t current_pos = {0}; + nv3_coord_16_t current_pos = {0}; - for (int32_t y = position.y; y < (position.y + size.h); y++) + for (int32_t y = position.y; y < (position.y + size.y); y++) { current_pos.y = y; - for (int32_t x = position.x; x < (position.x + size.w); x++) + for (int32_t x = position.x; x < (position.x + size.x); x++) { current_pos.x = x; @@ -49,7 +49,7 @@ void nv3_render_rect(nv3_position_16_t position, nv3_size_16_t size, uint32_t co /* Render GDI-B clipped rectangle */ void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj) { - nv3_position_16_t current_pos = {0}; + nv3_coord_16_t current_pos = {0}; for (int32_t y = clip.top; y < clip.bottom; y++) { @@ -89,8 +89,8 @@ void nv3_render_gdi_transparent_bitmap_blit(bool bit, bool clip, uint32_t color, Also clip if we are outside of the SIZE_OUT range We only need to do this in one direction just to get rid of the crud sent by NV */ - uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_out_d.w); - uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + (nv3->pgraph.win95_gdi_text.size_out_d.h); + uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_out_d.x); + uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + (nv3->pgraph.win95_gdi_text.size_out_d.y); if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) @@ -106,7 +106,7 @@ void nv3_render_gdi_transparent_bitmap_blit(bool bit, bool clip, uint32_t color, Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) */ - uint32_t end_x = (clip) ? nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.w : nv3->pgraph.win95_gdi_text.point_c.x + nv3->pgraph.win95_gdi_text.size_c.w; + uint32_t end_x = (clip) ? nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.x : nv3->pgraph.win95_gdi_text.point_c.x + nv3->pgraph.win95_gdi_text.size_c.x; nv3->pgraph.win95_gdi_text_current_position.x++; @@ -135,7 +135,7 @@ void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitma We store a global bit count for this purpose. */ - uint32_t bitmap_size = (clip) ? nv3->pgraph.win95_gdi_text.size_in_d.w * nv3->pgraph.win95_gdi_text.size_in_d.h : nv3->pgraph.win95_gdi_text.size_c.w * nv3->pgraph.win95_gdi_text.size_c.h; + uint32_t bitmap_size = (clip) ? nv3->pgraph.win95_gdi_text.size_in_d.x * nv3->pgraph.win95_gdi_text.size_in_d.y : nv3->pgraph.win95_gdi_text.size_c.x * nv3->pgraph.win95_gdi_text.size_c.y; uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; /* we have to interpret every bit in reverse bit order but in the right byte order */ @@ -231,8 +231,8 @@ void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, Also clip if we are outside of the SIZE_OUT range We only need to do this in one direction just to get rid of the crud sent by NV */ - uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.w); - uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + (nv3->pgraph.win95_gdi_text.size_out_e.h); + uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.x); + uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + (nv3->pgraph.win95_gdi_text.size_out_e.y); if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) @@ -253,7 +253,7 @@ void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) */ - uint32_t end_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_in_e.w; + uint32_t end_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_in_e.x; nv3->pgraph.win95_gdi_text_current_position.x++; @@ -278,7 +278,7 @@ void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitma We store a global bit count for this purpose. */ - uint32_t bitmap_size = nv3->pgraph.win95_gdi_text.size_in_e.w * nv3->pgraph.win95_gdi_text.size_in_e.h; + uint32_t bitmap_size = nv3->pgraph.win95_gdi_text.size_in_e.x * nv3->pgraph.win95_gdi_text.size_in_e.y; uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; /* we have to interpret every bit in reverse bit order but in the right byte order */ diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 32b6230ea..5a59ba51d 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -380,12 +380,11 @@ void nv3_pfifo_trigger_dma_if_required(void) /* page size is 0x1000 */ - uint32_t final_address = final_page_base + (tlb_pt_entry << 10); //x86 page size is 0x1000 + uint32_t final_address = final_page_base + (tlb_pt_entry << 10) + where_to_send; //x86 page size is 0x1000 (maybe rsh where_to_send by 2) nv_log_verbose_only("DMA Engine: DMA to %08x length=%08x", final_address, bytes_to_send); - //dma_bm_write() - + //-dma_bm_write() } //we're done @@ -576,7 +575,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); break; - /* Cache1 is handled below */ + /* Cache1 Context is handled below */ case NV3_PFIFO_CACHE0_CTX: nv3->pfifo.cache0_settings.context[0] = val; break; From e791f25a6b1d2211d74b67f1ac968d104da1cd90 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 23 Apr 2025 23:55:57 +0100 Subject: [PATCH 179/274] Get rid of redundant code --- src/include/86box/nv/vid_nv3.h | 2 -- src/video/nv/nv3/subsystems/nv3_pramdac.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index c27af6a1d..25049dbce 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -44,8 +44,6 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_DMA_CHANNELS_TOTAL 0x7F // This is also used somewhere despite there only being 8*8 = 64 channels -#define NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT 1 // The amount by which we have to ration out the memory clock because it's not fast enough... - // Multiply by this value to get the real clock speed. #define NV3_LAST_VALID_GRAPHICS_OBJECT_ID 0x1F // The class ids are represented with 5 bits in PGRAPH, but 7 bits in PFIFO! diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index b24d3d201..88b910b6f 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -134,7 +134,7 @@ void nv3_pramdac_set_vram_clock(void) // Convert to microseconds frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); - double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box + double time = 1000000.0 / (double)frequency; // needs to be a double for 86box nv_log("Memory clock = %.2f MHz\n", frequency / 1000000.0f); @@ -182,7 +182,7 @@ void nv3_pramdac_set_pixel_clock(void) nv3->nvbase.svga.clock = cpuclock / frequency; - double time = (1000000.0 * NV3_86BOX_TIMER_SYSTEM_FIX_QUOTIENT) / (double)frequency; // needs to be a double for 86box + double time = 1000000.0 / (double)frequency; // needs to be a double for 86box nv_log("Pixel clock = %.2f MHz\n", frequency / 1000000.0f); From 99823f3fa4632adaebd79ceec29f09b232756d79 Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 06:49:33 -0500 Subject: [PATCH 180/274] Fix build on macOS and NetBSD --- src/video/nv/nv3/nv3_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ad032abe9..031827677 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -762,11 +762,13 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) nv3->nvbase.svga.hdisp += 0x100; break; case NV3_CRTC_REGISTER_I2C_GPIO: + { uint8_t scl = !!(val & 0x20); uint8_t sda = !!(val & 0x10); // Set an I2C GPIO register i2c_gpio_set(nv3->nvbase.i2c, scl, sda); break; + } } From ffa24e100271aee092dae4392263e316b30b3dc9 Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 07:00:06 -0500 Subject: [PATCH 181/274] More NetBSD build fixes --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 5a59ba51d..2625adda4 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -558,7 +558,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.cache1_settings.get_address = val; break; case NV3_PFIFO_RUNOUT_GET: - + { uint32_t size_get = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_get == 0) //512b @@ -566,7 +566,9 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) else nv3->pfifo.runout_get = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); break; + } case NV3_PFIFO_RUNOUT_PUT: + { uint32_t size_put = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); if (size_put == 0) //512b @@ -575,6 +577,7 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); break; + } /* Cache1 Context is handled below */ case NV3_PFIFO_CACHE0_CTX: nv3->pfifo.cache0_settings.context[0] = val; From cd0e8b074117cec31a156081651cdeb6d5724331 Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 07:15:27 -0500 Subject: [PATCH 182/274] More build fixes --- src/video/nv/nv3/classes/nv3_class_003_chroma_key.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c index 139099e78..f19a06932 100644 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c @@ -38,12 +38,14 @@ void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ break; case NV3_CHROMA_KEY: + { nv3_color_expanded_t expanded_color = nv3_render_expand_color(param, grobj); nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); nv_log("Method Execution: Chroma = 0x%08x", nv3->pgraph.chroma_key); - break; + break; + } default: warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); From 988c29064a9fa2a4e179a3838b7d1b48d14068c8 Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 08:01:48 -0500 Subject: [PATCH 183/274] Fix --- src/video/nv/nv3/classes/nv3_class_006_pattern.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c index b66db499e..5aa5bf0fc 100644 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ b/src/video/nv/nv3/classes/nv3_class_006_pattern.c @@ -60,13 +60,17 @@ void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ case NV3_PATTERN_UNUSED_DRIVER_BUG: break; case NV3_PATTERN_COLOR0: + { nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(param, grobj); nv3_render_set_pattern_color(expanded_colour0, false); break; + } case NV3_PATTERN_COLOR1: + { nv3_color_expanded_t expanded_colour1 = nv3_render_expand_color(param, grobj); nv3_render_set_pattern_color(expanded_colour1, true); break; + } case NV3_PATTERN_BITMAP_HIGH: nv3->pgraph.pattern_bitmap = 0; //reset nv3->pgraph.pattern_bitmap |= ((uint64_t)param << 32); From 5c9a9404d5418d481a5abd4737db7366bfd0d3ca Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 08:16:39 -0500 Subject: [PATCH 184/274] Fix --- src/video/nv/nv3/render/nv3_render_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index d03ee9481..acbf9fa05 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -443,6 +443,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t break; case 15: case 16: + { uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 1; @@ -471,7 +472,9 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; break; + } case 32: + { uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 2; @@ -482,6 +485,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t nv3->nvbase.svga.changedvram[pixel_addr_vram >> 10] = changeframecount; break; + } } /* Go write the pixel */ From a4af738ede11afcbd02c2dd892b2dbff6d575c6c Mon Sep 17 00:00:00 2001 From: fuel-pcbox Date: Thu, 24 Apr 2025 08:20:19 -0500 Subject: [PATCH 185/274] More fixes --- src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index 5a1d08733..a35135672 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -38,6 +38,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ { /* Color format of the image */ case NV3_IMAGE_IN_MEMORY_COLOR_FORMAT: + { // convert to how the bpixel registers represent surface uint32_t real_format = 1; @@ -65,6 +66,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: Image in Memory BUF%d COLOR_FORMAT=0x%04x\n", src_buffer_id, param); break; + } /* DOn't log invalid */ case NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE: nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); From 2007e3d0e56cde208fa8cf3144a312d6e250d6d8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 27 Apr 2025 17:04:24 +0100 Subject: [PATCH 186/274] start working on visual gpu debugging window; rename nv3_position_32 to nv3_coord_32; first pass at hardware cursor --- doc/nvidia_notes/hardware cursor.txt | 21 +++ .../86box/nv/classes/vid_nv3_classes.h | 8 +- src/include/86box/nv/render/vid_nv3_render.h | 2 +- src/include/86box/nv/vid_nv3.h | 55 +++++--- src/qt/CMakeLists.txt | 6 +- src/qt/qt_gpudebug_vram.cpp | 1 + src/qt/qt_gpudebug_vram.hpp | 18 +++ src/qt/qt_gpudebug_vram.ui | 56 ++++++++ src/qt/qt_mainwindow.ui | 18 +++ src/video/nv/nv3/nv3_core.c | 127 ++++++++++++++++-- src/video/nv/nv3/render/nv3_render_blit.c | 4 +- src/video/nv/nv3/render/nv3_render_core.c | 10 +- src/video/nv/nv3/subsystems/nv3_pramdac.c | 15 ++- 13 files changed, 294 insertions(+), 47 deletions(-) create mode 100644 doc/nvidia_notes/hardware cursor.txt create mode 100644 src/qt/qt_gpudebug_vram.cpp create mode 100644 src/qt/qt_gpudebug_vram.hpp create mode 100644 src/qt/qt_gpudebug_vram.ui diff --git a/doc/nvidia_notes/hardware cursor.txt b/doc/nvidia_notes/hardware cursor.txt new file mode 100644 index 000000000..0845778f4 --- /dev/null +++ b/doc/nvidia_notes/hardware cursor.txt @@ -0,0 +1,21 @@ +NV3/NV3T/NV4 hardware cursor + +Unlock extended CRTC registers + +CIO_CRE_HCUR_ADDR0 + Bits [6:0] = Address + +CIO_CRE_HCUR_ADDR1 + Bits [7:3] = Bits [11:7] of address + Bit 1 = Cursor Doubling + Bit 0 = Enable + +PRAMDAC_CU_START_POS (MMIO 0x680300) + Bits [11:0] = X Pos + Bits [27:16] = Y Pos + +CursorAddress >> 16 written to addr0 +(((CursorAddress >> 11) & 0x1F) << 3) | 1 (for enable) written to addr1 + +Lock extended CRTC registers +Enable - write \ No newline at end of file diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 163b6464e..d5dc3ed43 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -306,11 +306,11 @@ typedef struct nv3_clip_16_s } nv3_clip_16_t; /* In case your positions weren't HIGH PRECISION enough */ -typedef struct nv3_position_32_s +typedef struct nv3_coord_32_s { uint32_t x; uint32_t y; -} nv3_position_32_t; +} nv3_coord_32_t; // COLOUR FORMATS @@ -459,7 +459,7 @@ typedef struct nv3_object_class_008 uint32_t set_notify; // Set notifier nv3_color_expanded_t color; // argb? nv3_coord_16_t point[16]; // Boring points - nv3_position_32_t point32[16]; // Allows you to have points with full 32-bit precision + nv3_coord_32_t point32[16]; // Allows you to have points with full 32-bit precision nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points! } nv3_point_t; @@ -549,7 +549,7 @@ typedef struct nv3_object_class_00B uint32_t x2; nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? - nv3_position_32_t mesh32[16]; + nv3_coord_32_t mesh32[16]; nv3_color_and_coord_16_t ctriangle[3]; // Triangle with colour nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours } nv3_triangle_t; diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 795bae979..f4850b8f7 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -31,7 +31,7 @@ uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj); /* Address */ uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer); +uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer); /* Colour Conversion */ uint32_t nv3_render_get_palette_index(uint8_t index); // Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 25049dbce..ebb7c0755 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -697,6 +697,11 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PVIDEO_END 0x6802FF #define NV3_PRAMDAC_START 0x680300 +#define NV3_PRAMDAC_CURSOR_START 0x680300 + +#define NV3_PRAMDAC_CURSOR_SIZE_X 32 +#define NV3_PRAMDAC_CURSOR_SIZE_Y 32 + #define NV3_PRAMDAC_CLOCK_MEMORY 0x680504 #define NV3_PRAMDAC_CLOCK_MEMORY_VDIV 7:0 #define NV3_PRAMDAC_CLOCK_MEMORY_NDIV 15:8 @@ -759,30 +764,30 @@ extern const device_config_t nv3t_config[]; // Confi // control structures for dma'd in graphics objects from pfifo // these all have configurable sizes, define them here -#define NV3_RAMIN_START 0x1C00000 +#define NV3_RAMIN_START 0x1C00000 -#define NV3_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects -#define NV3_RAMIN_RAMHT_END 0x1C00FFF -#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF -#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF -#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF -#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF +#define NV3_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects +#define NV3_RAMIN_RAMHT_END 0x1C00FFF +#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF +#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF +#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF +#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF /* OBSOLETE AREA for AUDIO probably. DO NOT USE! */ -#define NV3_RAMIN_RAMAU_START 0x1C01000 -#define NV3_RAMIN_RAMAU_END 0x1C01BFF -#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels -#define NV3_RAMIN_RAMFC_END 0x1C01DFF -#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF -#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF -#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions -#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF -#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF -#define NV3_RAMIN_RAMRO_END 0x1C01FFF -#define NV3_RAMIN_RAMRM_START 0x1C02000 -#define NV3_RAMIN_RAMRM_END 0x1C02FFF +#define NV3_RAMIN_RAMAU_START 0x1C01000 +#define NV3_RAMIN_RAMAU_END 0x1C01BFF +#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels +#define NV3_RAMIN_RAMFC_END 0x1C01DFF +#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF +#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF +#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions +#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF +#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF +#define NV3_RAMIN_RAMRO_END 0x1C01FFF +#define NV3_RAMIN_RAMRM_START 0x1C02000 +#define NV3_RAMIN_RAMRM_END 0x1C02FFF -#define NV3_RAMIN_END 0x1FFFFFF +#define NV3_RAMIN_END 0x1FFFFFF // not done @@ -814,6 +819,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_PRESETROWSCAN 0x08 #define NV3_CRTC_REGISTER_MAXSCAN 0x09 #define NV3_CRTC_REGISTER_CURSOR_START 0x0A +#define NV3_CRTC_REGISTER_CURSOR_START_DISABLED 5 #define NV3_CRTC_REGISTER_CURSOR_END 0x0B #define NV3_CRTC_REGISTER_STARTADDR_HIGH 0x0C #define NV3_CRTC_REGISTER_STARTADDR_LOW 0x0D @@ -846,6 +852,9 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_HEB 0x2D // HRS most significant bit +#define NV3_CRTC_REGISTER_CURSOR_ADDR0 0x30 // Cursor high +#define NV3_CRTC_REGISTER_CURSOR_ADDR1 0x31 // Cursor low (1:0 = enable) + #define NV3_CRTC_REGISTER_PIXELMODE_VGA 0x00 // vga textmode #define NV3_CRTC_REGISTER_PIXELMODE_8BPP 0x01 #define NV3_CRTC_REGISTER_PIXELMODE_16BPP 0x02 @@ -1084,7 +1093,10 @@ typedef struct nv3_pramdac_s uint8_t user_pixel_mask; // pixel mask for DAC lookup uint32_t user_read_mode_address; // user read mode address uint32_t user_write_mode_address; // user write mode address - uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entriesxr,g,b = 768 bytes + uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entries, 1 byte for r,g,b = 768 bytes + + uint32_t cursor_address; // cursor address start + nv3_coord_16_t cursor_start; } nv3_pramdac_t; /* Holds DMA channel context information */ @@ -1473,6 +1485,7 @@ extern nv3_t* nv3; void* nv3_init(const device_t *info); void nv3_close(void* priv); void nv3_speed_changed(void *priv); +void nv3_draw_cursor(svga_t* svga, int32_t drawline); void nv3_recalc_timings(svga_t* svga); void nv3_force_redraw(void* priv); diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 4231034d5..c2e5e4df4 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -151,7 +151,11 @@ add_library(ui STATIC qt_newfloppydialog.ui qt_harddiskdialog.cpp qt_harddiskdialog.hpp - qt_harddiskdialog.ui + qt_harddiskdialog.ui + + qt_gpudebug_vram.cpp + qt_gpudebug_vram.hpp + qt_gpudebug_vram.ui qt_harddrive_common.cpp qt_harddrive_common.hpp diff --git a/src/qt/qt_gpudebug_vram.cpp b/src/qt/qt_gpudebug_vram.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/qt/qt_gpudebug_vram.cpp @@ -0,0 +1 @@ + diff --git a/src/qt/qt_gpudebug_vram.hpp b/src/qt/qt_gpudebug_vram.hpp new file mode 100644 index 000000000..975583f7f --- /dev/null +++ b/src/qt/qt_gpudebug_vram.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace Ui +{ + class GPUDebugVRAMDialog; +} + +class Ui::GPUDebugVRAMDialog : public QDialog +{ + public: + void Init(); + protected: + private: + + +}; \ No newline at end of file diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui new file mode 100644 index 000000000..0f6c9931f --- /dev/null +++ b/src/qt/qt_gpudebug_vram.ui @@ -0,0 +1,56 @@ + + + + GPUDebugVRAMDialog + + + + 0 + 0 + 421 + 269 + + + + + 0 + 0 + + + + + 421 + 269 + + + + + 421 + 269 + + + + Dialog + + + + + + false + + + 0 + + + true + + + + + + + Sectors: + + + + diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 83a80342b..2861b65be 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -222,11 +222,19 @@ + + + &Debugging Tools + + + + + @@ -899,6 +907,16 @@ Pen + + + GPU Debug - VRAM Viewer + + + + + GPU Debug - NV3 Visual Debugger + + diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 031827677..d08768e3c 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -725,18 +725,18 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) 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 + 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; + 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; @@ -769,6 +769,15 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) i2c_gpio_set(nv3->nvbase.i2c, scl, sda); break; } + /* [6:0] contains cursorAddr [23:17] */ + case NV3_CRTC_REGISTER_CURSOR_ADDR0: + nv3->pramdac.cursor_address |= val << 17; //bit7 technically ignored, but nv don't care, so neither do we + break; + /* [7:2] contains cursorAddr [16:11] */ + case NV3_CRTC_REGISTER_CURSOR_ADDR1: + nv3->pramdac.cursor_address |= (val >> 2) << 13; // bit0 and 1 aren't part of the address + break; + } @@ -852,11 +861,109 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) // sanity check if (!nv3) return; + + // if cursor disabled is set, return + if ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_CURSOR_START] >> NV3_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01) + return; // On windows, this shows up using NV_IMAGE_IN_MEMORY. // Do we need to emulate it? + uint32_t vram_cursor_base = nv3->pramdac.cursor_address; + /* let's just assume buffer 0 here...that code needs to be totally rewritten*/ + nv3_coord_16_t start_position = nv3->pramdac.cursor_start; - nv_log("nv3_draw_cursor drawline=0x%04x", drawline); + /* refuse to draw if thge cursor is offscreen */ + if (start_position.x >= nv3->nvbase.svga.hdisp + || start_position.y >= nv3->nvbase.svga.dispend) + { + return; + } + + nv_log("nv3_draw_cursor start=0x%04x,0x%04x", start_position.x, start_position.y); + + uint32_t final_position = nv3_render_get_vram_address_for_buffer(start_position, 0); + + uint16_t* vram_16 = (uint16_t*)nv3->nvbase.svga.vram; + uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; + + /* + We have to get a 32x32, "A"1R5G5B5-format cursor + out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1 + + Technically these are expanded to RGB10, but I don't see why this needs to happen. And our pipeline isn't set up for it anyway. + */ + for (int32_t y = 0; y < NV3_PRAMDAC_CURSOR_SIZE_Y; y++) + { + for (int32_t x = 0; x < NV3_PRAMDAC_CURSOR_SIZE_X; x++) + { + uint16_t current_pixel = vram_16[vram_cursor_base << 1]; + bool replace_bit = (current_pixel & 0x8000); + + switch (nv3->nvbase.svga.bpp) + { + /* this is indexed colour but... lol */ + case 8: + if (replace_bit) + { + uint8_t final = current_pixel ^ nv3->nvbase.svga.vram[final_position]; + nv3->nvbase.svga.vram[final_position] = final; + } + else // just override + nv3->nvbase.svga.vram[final_position] = current_pixel; + case 15 ... 16: // easy case (our cursor is 15bpp format) + uint32_t index_16 = final_position >> 1; + if (replace_bit) + { + uint16_t final = current_pixel ^ vram_16[index_16]; + vram_16[index_16] = final; + } + else // just override + vram_16[index_16] = current_pixel; + case 32: + uint32_t index_32 = final_position >> 2; + if (replace_bit) + { + uint16_t final = current_pixel ^ vram_32[index_32]; + vram_32[index_32] = final; + } + else // just override + vram_32[index_32] = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here + break; + } + + // increment vram position + vram_cursor_base += 2; + + // go + switch (nv3->nvbase.svga.bpp) + { + case 8: + final_position++; + case 15 ... 16: + final_position += 2; + break; + case 32: + final_position += 4; + break; + } + + start_position.x++; + } + + + start_position.y++; + start_position.x = nv3->pramdac.cursor_start.x; + + // reset at the end of each line so we "jump" to the start x + final_position = nv3_render_get_vram_address_for_buffer(start_position, 0); + } + + nv3_coord_16_t size = {0}; + size.x = size.y = 32; + nv3_grobj_t dummy = {0}; // need to clean it up + + /* do we need to update here? */ + nv3_render_current_bpp(&nv3->nvbase.svga, start_position, size, dummy, false, false); } // MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 6d29965d4..b7a45f9a8 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -161,7 +161,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { buf_position = (nv3->pgraph.blit.size.x * y); /* shouldn't matter in non-wtf mode */ - vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, src_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(old_position, src_buffer); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); old_position.y++; @@ -172,7 +172,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) { buf_position = (nv3->pgraph.blit.size.x * y); - vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, dst_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(new_position, dst_buffer); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); new_position.y++; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index acbf9fa05..6a1823883 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -260,7 +260,7 @@ uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj) /* Combine the current buffer with the pitch to get the address in the video ram for a specific position relative to a specific framebuffer */ -uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer) +uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; @@ -663,7 +663,7 @@ void nv3_render_8bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; @@ -704,7 +704,7 @@ void nv3_render_15bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; @@ -745,7 +745,7 @@ void nv3_render_16bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; @@ -786,7 +786,7 @@ void nv3_render_32bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj { /* re-set the vram address because we are basically "jumping" halfway across a line here */ if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now + vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now else vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index 88b910b6f..b7c984f26 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -182,7 +182,7 @@ void nv3_pramdac_set_pixel_clock(void) nv3->nvbase.svga.clock = cpuclock / frequency; - double time = 1000000.0 / (double)frequency; // needs to be a double for 86box + double time = 1000000.0 / (double)frequency; // needs to be a double for 86box nv_log("Pixel clock = %.2f MHz\n", frequency / 1000000.0f); @@ -205,6 +205,7 @@ void nv3_pramdac_set_pixel_clock(void) // NULL means handle in read functions nv_register_t pramdac_registers[] = { + { NV3_PRAMDAC_CURSOR_START, "PRAMDAC - Cursor Start Position"}, { 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}, @@ -320,6 +321,9 @@ uint32_t nv3_pramdac_read(uint32_t address) ret = nv3->pramdac.palette[nv3->pramdac.user_read_mode_address]; nv3->pramdac.user_read_mode_address++; break; + case NV3_PRAMDAC_CURSOR_START: + ret = (nv3->pramdac.cursor_start.y << 16) | nv3->pramdac.cursor_start.x; + break; } } @@ -345,8 +349,6 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) // if the register actually exists if (reg) { - - // on-read function if (reg->on_write) reg->on_write(value); @@ -434,6 +436,13 @@ void nv3_pramdac_write(uint32_t address, uint32_t value) nv3->pramdac.user_write_mode_address++; break; + /* cursor start location */ + case NV3_PRAMDAC_CURSOR_START: + // only 12 bits are used here instead of 16 for some stupid reason + nv3->pramdac.cursor_start.y = (value >> 16) & 0xFFF; + nv3->pramdac.cursor_start.x = (value) & 0xFFF; + nv3_draw_cursor(&nv3->nvbase.svga, 0);//drawline doesn't matter here + break; } } From ff96ce99b649174ca1bce0542afbcc96c1b12a0a Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 27 Apr 2025 23:23:50 +0100 Subject: [PATCH 187/274] Add a very very early (8 Aug 1997), hitherto entirely undumped VBIOS. It has bugs that prevent it from being used in Windows 98 Setup. --- src/include/86box/nv/vid_nv3.h | 7 +++++-- src/video/nv/nv3/nv3_core_config.c | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index ebb7c0755..4fd3c6676 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -61,7 +61,10 @@ extern const device_config_t nv3t_config[]; // Confi // Coming soon: MIROmagic Premium BIOS (when I get mine dumped) //todo: move to hash system -// Oldest one of these - September 6, 1997 +// Oldest one of these - August 8, 1997 + +// This particular VBIOS is very early and has certain bugs +#define NV3_VBIOS_STB_V128_V160 "roms/video/nvidia/nv3/stb-velocity128-1.6.bin" // STB Velocity 128 3D (Riva 128) Ver.1.60 #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] @@ -72,7 +75,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3T_VBIOS_ASUS_V170 "roms/video/nvidia/nv3/A170D03T.rom" // ASUS AGP-V3000 ZXTV BIOS - V1.70D.03 (C) 1996-98 Nvidia Corporation #define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation #define NV3T_VBIOS_REFERENCE_CEK_V172 "roms/video/nvidia/nv3/vgasgram.rom" // Reference(?) BIOS: RIVA 128 ZX BIOS - V1.72B (C) 1996-98 NVidia Corporation - +# // The default VBIOS to use #define NV3_VBIOS_DEFAULT NV3_VBIOS_ERAZOR_V15403 diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c index ec7d46e30..b039ff82b 100644 --- a/src/video/nv/nv3/nv3_core_config.c +++ b/src/video/nv/nv3/nv3_core_config.c @@ -38,6 +38,7 @@ const device_config_t nv3_config[] = .default_int = 0, .bios = { + { .name = "ELSA VICTORY Erazor - Version 1.47.00", .files_no = 1, .internal_name = "NV3_VBIOS_ERAZOR_V14700", @@ -63,6 +64,11 @@ const device_config_t nv3_config[] = .internal_name = "NV3_VBIOS_ASUS_V3000_V151", .files = {NV3_VBIOS_ASUS_V3000_V151, ""}, }, + { + .name = "STB Velocity 128 - Version 1.60 [BUGGY]", .files_no = 1, + .internal_name = "NV3_VBIOS_STB_V128_V160", + .files = {NV3_VBIOS_STB_V128_V160, ""}, + }, { .name = "STB Velocity 128 - Version 1.82", .files_no = 1, .internal_name = "NV3_VBIOS_STB_V128_V182", From 44bf760861710e8c62376e49e667ef2e21d05dbc Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 28 Apr 2025 02:04:49 +0100 Subject: [PATCH 188/274] add vram viewer window --- src/qt/qt_gpudebug_vram.cpp | 53 +++++++++++++++++++++++++++++++++++++ src/qt/qt_gpudebug_vram.hpp | 26 ++++++++++++++++-- src/qt/qt_gpudebug_vram.ui | 32 +++++++--------------- src/qt/qt_mainwindow.cpp | 12 +++++++++ src/qt/qt_mainwindow.hpp | 1 + 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/qt/qt_gpudebug_vram.cpp b/src/qt/qt_gpudebug_vram.cpp index 8b1378917..000f41263 100644 --- a/src/qt/qt_gpudebug_vram.cpp +++ b/src/qt/qt_gpudebug_vram.cpp @@ -1 +1,54 @@ +/* + * 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. + * + * GPU Debugging Tools - VRAM Viewer implementation + * + * + * + * Authors: starfrost + * + * Copyright 2025 starfrost + */ +/* C++ includes */ +#include +#include + + +/* Qt includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_qt_gpudebug_vram.h" + +/* 86Box core includes */ +extern "C" +{ + +} + +GPUDebugVRAMDialog::GPUDebugVRAMDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::GPUDebugVRAMDialog) +{ + ui->setupUi(this); +} + +GPUDebugVRAMDialog::~GPUDebugVRAMDialog() +{ + +} \ No newline at end of file diff --git a/src/qt/qt_gpudebug_vram.hpp b/src/qt/qt_gpudebug_vram.hpp index 975583f7f..18b2e8c96 100644 --- a/src/qt/qt_gpudebug_vram.hpp +++ b/src/qt/qt_gpudebug_vram.hpp @@ -1,3 +1,21 @@ +/* + * 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. + * + * GPU Debugging Tools - VRAM Viewer headers + * + * + * + * Authors: starfrost + * + * Copyright 2025 starfrost + */ + + #pragma once #include @@ -7,12 +25,16 @@ namespace Ui class GPUDebugVRAMDialog; } -class Ui::GPUDebugVRAMDialog : public QDialog +class GPUDebugVRAMDialog : public QDialog { + Q_OBJECT + public: - void Init(); + explicit GPUDebugVRAMDialog(QWidget *parent = nullptr); + ~GPUDebugVRAMDialog(); protected: private: + Ui::GPUDebugVRAMDialog* ui; }; \ No newline at end of file diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui index 0f6c9931f..be2bd4a20 100644 --- a/src/qt/qt_gpudebug_vram.ui +++ b/src/qt/qt_gpudebug_vram.ui @@ -1,8 +1,7 @@ - GPUDebugVRAMDialog - + 0 @@ -30,27 +29,16 @@ - Dialog + VRAM Viewer - - - - false - - - 0 - - - true - - - - - - - Sectors: - - + + + + VRAM Viewer + + + + diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 1fb9b3fb5..dc0b923b4 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -23,6 +23,7 @@ #include #include "qt_mainwindow.hpp" +#include "qt_gpudebug_vram.hpp" #include "ui_qt_mainwindow.h" #include "qt_specifydimensions.h" @@ -100,6 +101,8 @@ extern bool cpu_thread_running; #include "qt_mediamenu.hpp" #include "qt_util.hpp" +#include "qt_gpudebug_vram.hpp" + #if defined __unix__ && !defined __HAIKU__ # ifndef Q_OS_MACOS # include "evdev_keyboard.hpp" @@ -2267,3 +2270,12 @@ void MainWindow::on_actionACPI_Shutdown_triggered() { acpi_pwrbut_pressed = 1; } + +void MainWindow::on_actionDebug_GPUDebug_VRAM_triggered() +{ + GPUDebugVRAMDialog debugVramDialog(this); + debugVramDialog.setWindowFlag(Qt::CustomizeWindowHint, true); + debugVramDialog.setWindowFlag(Qt::WindowTitleHint, true); + debugVramDialog.setWindowFlag(Qt::WindowSystemMenuHint, false); + debugVramDialog.exec(); +} \ No newline at end of file diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 5811ac36a..1798c8a75 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -125,6 +125,7 @@ private slots: void on_actionPreferences_triggered(); void on_actionEnable_Discord_integration_triggered(bool checked); void on_actionRenderer_options_triggered(); + void on_actionDebug_GPUDebug_VRAM_triggered(); void refreshMediaMenu(); void showMessage_(int flags, const QString &header, const QString &message, bool richText, std::atomic_bool* done = nullptr); From 6ebcbdb0026466120d29c2a90fd38f5cc454c7e9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 29 Apr 2025 21:11:32 +0100 Subject: [PATCH 189/274] minor cleanups --- src/video/nv/nv3/render/nv3_render_core.c | 14 ++++++++++++-- src/video/nv/nv3/subsystems/nv3_pramin.c | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 6a1823883..42392a9ce 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -301,8 +301,6 @@ nv3_coord_16_t nv3_render_get_dfb_position(uint32_t vram_address) else if (nv3->nvbase.svga.bpp == 32) pitch <<= 2; - //vram_address -= nv3->pgraph.boffset[0]; - pos.y = (vram_address / pitch); pos.x = (vram_address % pitch); @@ -529,6 +527,10 @@ void nv3_render_ensure_screen_size(void) /* Blit to the monitor from DFB, 8bpp */ void nv3_render_current_bpp_dfb_8(uint32_t address) { + /* Broken as fuck early vbios does this. Wtf? */ + if (!nv3->nvbase.svga.hdisp) + return; + nv3_coord_16_t size = {0}; size.x = size.y = 1; @@ -543,6 +545,10 @@ void nv3_render_current_bpp_dfb_8(uint32_t address) /* Blit to the monitor from DFB, 15/16bpp */ void nv3_render_current_bpp_dfb_16(uint32_t address) { + /* Broken as fuck early vbios does this. Wtf? */ + if (!nv3->nvbase.svga.hdisp) + return; + nv3_coord_16_t size = {0}; size.x = size.y = 1; @@ -564,6 +570,10 @@ void nv3_render_current_bpp_dfb_16(uint32_t address) /* Blit to the monitor from DFB, 32bpp */ void nv3_render_current_bpp_dfb_32(uint32_t address) { + /* Broken as fuck early vbios does this. Wtf? */ + if (!nv3->nvbase.svga.hdisp) + return; + nv3_coord_16_t size = {0}; size.x = size.y = 1; diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 8556822f5..4cd6d10c3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -96,14 +96,14 @@ uint16_t nv3_ramin_read16(uint32_t addr, void* priv) // Read 32-bit ramin uint32_t nv3_ramin_read32(uint32_t addr, void* priv) { - if (!nv3) return 0x00; + if (!nv3) + return 0x00; addr &= (nv3->nvbase.svga.vram_max - 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 + uint32_t* vram_32bit = (uint32_t*)nv3->nvbase.svga.vram; + uint32_t raw_addr = addr; // saved after and logged addr ^= (nv3->nvbase.svga.vram_max - 0x10); addr >>= 2; // what From cd4ecff05663d5dc8bcf3307299e43827d87e10f Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 3 May 2025 22:41:49 +0100 Subject: [PATCH 190/274] Acknowledge the existence of dpram --- src/include/86box/nv/vid_nv3.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 4fd3c6676..965e2d0e1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -622,6 +622,8 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PGRAPH_DMA_INTR_NOTIFY 16 #define NV3_PGRAPH_DMA_INTR_EN_0 0x401140 // PGRAPH DMA Interrupt Enable 0 +#define NV3_PGRAPH_DPRAM_SIZE 12288 // Size of the internal texture cache + // 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_CLASS01_BETA_START 0x410000 // Beta blending factor @@ -1242,14 +1244,15 @@ typedef struct nv3_pgraph_s nv3_coord_16_bigy_t clip1_min; nv3_coord_16_bigy_t clip1_max; /* idk */ - nv3_coord_16_t clip_start; // Start of the clipping region - nv3_coord_16_t clip_size; // Size of the clipping region. + nv3_coord_16_t clip_start; // Start of the clipping region + nv3_coord_16_t clip_size; // Size of the clipping region. bool fifo_access; // Determines if PGRAPH can access PFIFO. nv3_pgraph_status_t status; // Current status of the 3D engine. uint32_t trapped_address; uint32_t trapped_data; uint32_t instance; // no idea what this is but possibly an object context uint32_t trapped_instance; + uint8_t dpram[NV3_PGRAPH_DPRAM_SIZE]; // Internal vertex/texturea cache. /* This area is used for holding universal representations of the U* registers, which are actually mapped into MMIO */ struct nv3_object_class_001 beta_factor_class; From aaaa4db881cb5e6ad7e909ce6f5aa612a1b4da2e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 18 May 2025 16:45:24 +0100 Subject: [PATCH 191/274] Fix various minor bugs; correctly use bpixel for rendering instead of svga->bpp. remove incorrect DBA check --- CMakePresets.json | 3 +- doc/nvidia_notes/NV128.xlsx | Bin 9708 -> 0 bytes src/include/86box/nv/render/vid_nv3_render.h | 2 +- src/qt/qt_gpudebug_vram.ui | 29 ++++++--- src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/render/nv3_render_blit.c | 13 ++-- src/video/nv/nv3/render/nv3_render_core.c | 59 ++++++++----------- src/video/nv/nv3/subsystems/nv3_pfb.c | 2 +- 8 files changed, 54 insertions(+), 56 deletions(-) delete mode 100644 doc/nvidia_notes/NV128.xlsx diff --git a/CMakePresets.json b/CMakePresets.json index b2ed64e97..30feab8ab 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -36,8 +36,7 @@ "name": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON", - "NV_LOG_ULTRA": "ON" + "NV_LOG": "ON" }, "inherits": "base" }, diff --git a/doc/nvidia_notes/NV128.xlsx b/doc/nvidia_notes/NV128.xlsx deleted file mode 100644 index fbf57c5eb136926abfc26b411f6aec0ae899a3fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9708 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Р3ChhSMhqF;CFdN*o>A3dOs_ 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 Tw+jHg0PlX_pshy!^V|OeRTkK? diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index f4850b8f7..c3e6182e5 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,7 +18,7 @@ #pragma once /* Core */ -void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer); +void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); void nv3_render_current_bpp_dfb_8(uint32_t address); void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui index be2bd4a20..12c64e3b4 100644 --- a/src/qt/qt_gpudebug_vram.ui +++ b/src/qt/qt_gpudebug_vram.ui @@ -32,13 +32,28 @@ VRAM Viewer - - - - VRAM Viewer - - + + + + VRAM + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + + + + + TextLabel + + + Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft + + - + + + diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index d08768e3c..824d96405 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -963,7 +963,7 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) nv3_grobj_t dummy = {0}; // need to clean it up /* do we need to update here? */ - nv3_render_current_bpp(&nv3->nvbase.svga, start_position, size, dummy, false, false); + nv3_render_current_bpp(&nv3->nvbase.svga, start_position, size, dummy, false); } // MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index b7a45f9a8..da139b496 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -57,7 +57,7 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) /* the reverse order is due to the endianness */ switch (nv3->nvbase.svga.bpp) { - // 4pixels packed into one color + // 4 pixels packed into one color in 8bpp case 8: //pixel3 @@ -82,7 +82,7 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) nv3_class_011_check_line_bounds(); break; - //2pixels packed into one color + // 2 pixels packed into one color in 15/16bpp case 15: case 16: pixel1 = (color) & 0xFFFF; @@ -98,8 +98,7 @@ void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) break; // just one pixel in 32bpp case 32: - pixel0 = color; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); + if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, color, grobj); nv3->pgraph.image_current_position.x++; nv3_class_011_check_line_bounds(); @@ -183,8 +182,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) We also need to update all of the areas of the screen that moved. */ - nv3_coord_16_t blit_position = {0}; - nv3_coord_16_t blit_size = {0}; + nv3_coord_16_t blit_position = {0}, blit_size = {0}; /* Change the smallest area of the screen that moved */ @@ -223,7 +221,6 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not Apply stupid hack */ - - nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, false, true); + nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, true); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 42392a9ce..3491c8df3 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -81,10 +81,10 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) break; case nv3_pgraph_pixel_format_r10g10b10: - color_final.a = (color << 31) & 0x01; - color_final.r = (color << 30) & 0x3FF; - color_final.g = (color << 20) & 0x1FF; - color_final.b = (color << 10); + color_final.a = (color >> 31) & 0x01; + color_final.r = (color >> 30) & 0x3FF; + color_final.g = (color >> 20) & 0x1FF; + color_final.b = (color >> 10); break; case nv3_pgraph_pixel_format_y8: @@ -138,7 +138,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co break; case nv3_pgraph_pixel_format_r10g10b10: /* sometimes alpha isn't used but we should incorporate it anyway */ - if (color.a > 0x00) packed_color | (1 << 31); + if (color.a > 0x00) packed_color |= (1 << 31); packed_color |= (color.r << 30); packed_color |= (color.g << 20); @@ -187,7 +187,7 @@ bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj) uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) { // convert the alpha to 1 bit. then return packed rgb10 - return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.a << 10); + return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.b << 10); } /* Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) */ @@ -360,6 +360,14 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + uint32_t dst_buffer = 0; // 5 = just use the source buffer + + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; + if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; + + uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; @@ -426,12 +434,14 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t It seems we can skip the downconversion step *for now*, since (framebuffer bits per pixel) == (object bits per pixel) I'm not sure how games will react. But it depends on how the D3D drivers operate, we may need ro convert texture formats to the current bpp internally. - TODO: MOVE TO BPIXEL DEPTH or GROBJ0 to determine this, once we figure out how to get the bpixel depth. + We use the pixel format of the destination buffer to achieve this (thanks frostbite2000) */ - switch (framebuffer_bpp) + uint32_t destination_format = (nv3->pgraph.bpixel[dst_buffer]) & 0x03; + + switch (destination_format) { - case 8: + case bpixel_fmt_8bit: rop_src = color & 0xFF; rop_dst = nv3->nvbase.svga.vram[pixel_addr_vram]; nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern) & 0xFF; @@ -439,8 +449,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; break; - case 15: - case 16: + case bpixel_fmt_16bit: { uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 1; @@ -471,7 +480,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t break; } - case 32: + case bpixel_fmt_32bit: { uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); pixel_addr_vram >>= 2; @@ -489,7 +498,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t /* Go write the pixel */ nv3_coord_16_t size = {0}; size.x = size.y = 1; - nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, true, false); + nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, false); } /* Ensure the correct monitor size */ @@ -602,35 +611,13 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) /* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool run_render_check, bool use_destination_buffer) +void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) { /* Ensure that we are in the correct mode. Modified SVGA core code */ nv3_render_ensure_screen_size(); /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM, so it can be used for s2sb's etc */ - /* Not needed for s2sb*/ - if (run_render_check) - { - /* Figure out the Display Buffer Address from the CRTCs */ - uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) - + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) - + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; - - /* Check our destination(?) buffer */ - uint32_t dst_buffer = 0; // 5 = just use the source buffer - - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - - /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not */ - if (nv3->pgraph.boffset[dst_buffer] != dba) - return; - } - - switch (nv3->nvbase.svga.bpp) { case 4: diff --git a/src/video/nv/nv3/subsystems/nv3_pfb.c b/src/video/nv/nv3/subsystems/nv3_pfb.c index 3e6cd4271..7360a2c9a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ b/src/video/nv/nv3/subsystems/nv3_pfb.c @@ -39,7 +39,7 @@ nv_register_t pfb_registers[] = { { NV3_PFB_GREEN_0, "PFB Green / Power Saving", NULL, NULL,}, { NV3_PFB_CONFIG_0, "PFB Framebuffer Config 0", nv3_pfb_config0_read, nv3_pfb_config0_write }, { NV3_PFB_CONFIG_1, "PFB Framebuffer Config 1", NULL, NULL }, - { NV3_PFB_RTL, "PFB RTL (Part of memory timings?)", NULL, NULL }, + { NV3_PFB_RTL, "PFB RTL (Part of memory timings)", NULL, NULL }, { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value }; From 6ba3d8981067d500ae4c5584995c71a1055ac803 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 18 May 2025 17:34:39 +0100 Subject: [PATCH 192/274] big rendering refactor --- src/include/86box/nv/render/vid_nv3_render.h | 2 +- src/video/nv/nv3/nv3_core.c | 7 - src/video/nv/nv3/render/nv3_render_blit.c | 47 ------ src/video/nv/nv3/render/nv3_render_core.c | 162 +++++++------------ src/video/nv/nv3/subsystems/nv3_pramdac.c | 1 + 5 files changed, 64 insertions(+), 155 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index c3e6182e5..9c7af77ab 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -18,7 +18,7 @@ #pragma once /* Core */ -void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_current_bpp(); void nv3_render_current_bpp_dfb_8(uint32_t address); void nv3_render_current_bpp_dfb_16(uint32_t address); void nv3_render_current_bpp_dfb_32(uint32_t address); diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 824d96405..21810386e 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -957,13 +957,6 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) // reset at the end of each line so we "jump" to the start x final_position = nv3_render_get_vram_address_for_buffer(start_position, 0); } - - nv3_coord_16_t size = {0}; - size.x = size.y = 32; - nv3_grobj_t dummy = {0}; // need to clean it up - - /* do we need to update here? */ - nv3_render_current_bpp(&nv3->nvbase.svga, start_position, size, dummy, false); } // MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index da139b496..0b0cb1c31 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -176,51 +176,4 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); new_position.y++; } - - /* - We need to blit manually here because we don't go through nv3_render_write_pixel - We also need to update all of the areas of the screen that moved. - */ - - nv3_coord_16_t blit_position = {0}, blit_size = {0}; - - /* Change the smallest area of the screen that moved */ - - if (cross_buffer_blit) - { - blit_position = nv3->pgraph.blit.point_out; - blit_size = nv3->pgraph.blit.size; - } - else - { - if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) - blit_size.x = (nv3->pgraph.blit.point_out.x - nv3->pgraph.blit.point_in.x) + nv3->pgraph.blit.size.x; - else if (nv3->pgraph.blit.point_out.x < nv3->pgraph.blit.point_in.x) - blit_size.x = (nv3->pgraph.blit.point_in.x - nv3->pgraph.blit.point_out.x) + nv3->pgraph.blit.size.x; - else - blit_size.x = nv3->pgraph.blit.size.x; - - if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) - blit_size.y = (nv3->pgraph.blit.point_out.y - nv3->pgraph.blit.point_in.y) + nv3->pgraph.blit.size.y; - else if (nv3->pgraph.blit.point_out.y < nv3->pgraph.blit.point_in.y) - blit_size.y = (nv3->pgraph.blit.point_in.y - nv3->pgraph.blit.point_out.y) + nv3->pgraph.blit.size.y; - else - blit_size.y = nv3->pgraph.blit.size.y; - - if (nv3->pgraph.blit.point_out.x > nv3->pgraph.blit.point_in.x) - blit_position.x = nv3->pgraph.blit.point_in.x; - else if (nv3->pgraph.blit.point_out.x <= nv3->pgraph.blit.point_in.x) // equals case, just use out - blit_position.x = nv3->pgraph.blit.point_out.x; - - if (nv3->pgraph.blit.point_out.y > nv3->pgraph.blit.point_in.y) - blit_position.y = nv3->pgraph.blit.point_in.y; - else if (nv3->pgraph.blit.point_out.y <= nv3->pgraph.blit.point_in.y) // equals case, just use out - blit_position.y = nv3->pgraph.blit.point_out.y; - - } - - /* If the BUFFER_ADDRESS of the last buffer is not the DBA, we don't *actually* want to draw this, so let's not - Apply stupid hack */ - - nv3_render_current_bpp(&nv3->nvbase.svga, blit_position, blit_size, grobj, true); } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 3491c8df3..72617c6aa 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -31,10 +31,10 @@ #include <86box/utils/video_stdlib.h> /* Functions only used in this translation unit */ -void nv3_render_8bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_15bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_16bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); -void nv3_render_32bpp(nv3_coord_16_t position, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer); +void nv3_render_8bpp(uint32_t vram_start, nv3_coord_16_t screen_size); +void nv3_render_15bpp(uint32_t vram_start, nv3_coord_16_t screen_size); +void nv3_render_16bpp(uint32_t vram_start, nv3_coord_16_t screen_size); +void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size); /* Expand a colour. NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! @@ -494,11 +494,6 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t break; } } - - /* Go write the pixel */ - nv3_coord_16_t size = {0}; - size.x = size.y = 1; - nv3_render_current_bpp(&nv3->nvbase.svga, position, size, grobj, false); } /* Ensure the correct monitor size */ @@ -590,19 +585,16 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); + uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + if (nv3->nvbase.svga.bpp == 32) { - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; *p = data; } /* Packed format */ else if (nv3->nvbase.svga.bpp == 15 || nv3->nvbase.svga.bpp == 16) { - //pos.x >>= 1; - - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, nv3->nvbase.svga.bpp); *p++; *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, (data >> 16) & 0xFFFF, nv3->nvbase.svga.bpp); @@ -611,9 +603,20 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) /* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_current_bpp() { - /* Ensure that we are in the correct mode. Modified SVGA core code */ + /* Figure out the Display Buffer Address from the CRTC */ + + uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) + + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) + + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; + + nv3_coord_16_t screen_size = {0}; + screen_size.x = nv3->nvbase.svga.hdisp; + screen_size.y = nv3->nvbase.svga.dispend; + + /* Ensure that we are + in the correct mode. Modified SVGA core code */ nv3_render_ensure_screen_size(); /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM, so it can be used for s2sb's etc */ @@ -625,16 +628,16 @@ void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t siz fatal("NV3 - 4bpp not implemented (not even sure if it's SVGA only)"); break; case 8: - nv3_render_8bpp(pos, size, grobj, use_destination_buffer); + nv3_render_8bpp(dba, screen_size); break; case 15: - nv3_render_15bpp(pos, size, grobj, use_destination_buffer); + nv3_render_15bpp(dba, screen_size); break; case 16: - nv3_render_16bpp(pos, size, grobj, use_destination_buffer); + nv3_render_16bpp(dba, screen_size); break; - case 32: - nv3_render_32bpp(pos, size, grobj, use_destination_buffer); + case 32: + nv3_render_32bpp(dba, screen_size); break; } @@ -644,40 +647,29 @@ void nv3_render_current_bpp(svga_t *svga, nv3_coord_16_t pos, nv3_coord_16_t siz Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, indexed 8 bits per pixel format */ -void nv3_render_8bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_8bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { if (!nv3) return; - uint32_t vram_base; //acquired for the start of each line + uint32_t vram_current_position = vram_start; uint32_t* p; - uint32_t data; - uint32_t start_x = pos.x; + uint32_t data = 0; - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - for (uint32_t y = 0; y < size.y; y++) + for (uint32_t y = 0; y < screen_size.y; y++) { - /* re-set the vram address because we are basically "jumping" halfway across a line here */ - if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now - else - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - - for (uint32_t x = 0; x < size.x; x++) + for (uint32_t x = 0; x < screen_size.x; x++) { - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; /* should just "tip over" to the next line */ *p = nv3_render_get_palette_index(data & 0xFF); - vram_base++; - pos.x++; + vram_current_position++; } - - pos.x = start_x; - pos.y++; } } @@ -685,40 +677,30 @@ void nv3_render_8bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 15 bits per pixel format */ -void nv3_render_15bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_15bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { if (!nv3) return; - uint32_t vram_base; //acquired for the start of each line + uint32_t vram_current_position = vram_start; uint32_t* p; - uint32_t data; - uint32_t start_x = pos.x; + uint32_t data = 0; - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - for (uint32_t y = 0; y < size.y; y++) + for (uint32_t y = 0; y < screen_size.y; y++) { - /* re-set the vram address because we are basically "jumping" halfway across a line here */ - if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now - else - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - - for (uint32_t x = 0; x < size.x; x++) + for (uint32_t x = 0; x < screen_size.x; x++) { - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; /* should just "tip over" to the next line */ *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); - vram_base += 2; - pos.x++; + vram_current_position += 2; } - - pos.x = start_x; - pos.y++; + } } @@ -726,40 +708,31 @@ void nv3_render_15bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 16 bits per pixel format */ -void nv3_render_16bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_16bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { if (!nv3) return; - uint32_t vram_base; //acquired for the start of each line + uint32_t vram_current_position = vram_start; uint32_t* p; - uint32_t data; - uint32_t start_x = pos.x; + uint32_t data = 0; - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - for (uint32_t y = 0; y < size.y; y++) + for (uint32_t y = 0; y < screen_size.y; y++) { - /* re-set the vram address because we are basically "jumping" halfway across a line here */ - if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now - else - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - - for (uint32_t x = 0; x < size.x; x++) + for (uint32_t x = 0; x < screen_size.x; x++) { - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; /* should just "tip over" to the next line */ *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); - vram_base += 2; - pos.x++; + vram_current_position += 2; + } - pos.x = start_x; - pos.y++; } } @@ -767,39 +740,28 @@ void nv3_render_16bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 32 bits per pixel format */ -void nv3_render_32bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj, bool use_destination_buffer) +void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { if (!nv3) return; - uint32_t vram_base; + uint32_t vram_current_position = vram_start; uint32_t* p; - uint32_t data; - uint32_t start_x = pos.x; + uint32_t data = 0; - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - for (uint32_t y = 0; y < size.y; y++) + for (uint32_t y = 0; y < screen_size.y; y++) { - /* re-set the vram address because we are basically "jumping" halfway across a line here */ - if (use_destination_buffer) - vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now - else - vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask; - - for (uint32_t x = 0; x < size.x; x++) + for (uint32_t x = 0; x < screen_size.x; x++) { - p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_base]; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; + data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; /* should just "tip over" to the next line */ *p = data; - vram_base += 4; - pos.x++; + vram_current_position += 4; } - - pos.y++; - pos.x = start_x; } } diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index b7c984f26..f179c8136 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -64,6 +64,7 @@ void nv3_pramdac_pixel_clock_poll(double real_time) if (nv3->nvbase.refresh_clock > nv3->nvbase.refresh_time) { /* Update the screen because something changed */ + nv3_render_current_bpp(); video_blit_memtoscreen(0, 0, xsize, ysize); nv3->nvbase.refresh_clock = 0; } From c1972f1b1389cbdbafbe22a862901e1dacb70374 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 18 May 2025 19:24:43 +0100 Subject: [PATCH 193/274] fix nv3_render_to_chroma --- src/video/nv/nv3/render/nv3_render_blit.c | 7 +++---- src/video/nv/nv3/render/nv3_render_core.c | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 0b0cb1c31..596d7b7a1 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -133,8 +133,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - - bool cross_buffer_blit = (nv3->pgraph.boffset[src_buffer] != nv3->pgraph.boffset[dst_buffer]); + nv3_coord_16_t old_position = nv3->pgraph.blit.point_in; nv3_coord_16_t new_position = nv3->pgraph.blit.point_out; @@ -160,7 +159,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { buf_position = (nv3->pgraph.blit.size.x * y); /* shouldn't matter in non-wtf mode */ - vram_position = nv3_render_get_vram_address_for_buffer(old_position, src_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(old_position, dst_buffer); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); old_position.y++; @@ -171,7 +170,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) { buf_position = (nv3->pgraph.blit.size.x * y); - vram_position = nv3_render_get_vram_address_for_buffer(new_position, dst_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(new_position, src_buffer); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); new_position.y++; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 72617c6aa..9611d8349 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -187,7 +187,7 @@ bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj) uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) { // convert the alpha to 1 bit. then return packed rgb10 - return !!expanded.a | (expanded.r << 30) | (expanded.b << 20) | (expanded.b << 10); + return !!expanded.a | (expanded.r << 30) | (expanded.g << 20) | (expanded.b << 10); } /* Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) */ @@ -611,6 +611,8 @@ void nv3_render_current_bpp() + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; + //uint32_t dba = 1920000; + nv3_coord_16_t screen_size = {0}; screen_size.x = nv3->nvbase.svga.hdisp; screen_size.y = nv3->nvbase.svga.dispend; From 28500a82baa6bb070e117739f1e057b153843f4b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 23 May 2025 15:59:54 +0100 Subject: [PATCH 194/274] sync --- doc/nvidia_notes/RIVA fansites.txt | 6 ++++-- src/include/86box/nv/vid_nv3.h | 8 ++++++++ src/video/nv/nv3/subsystems/nv3_pfifo.c | 1 - 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/nvidia_notes/RIVA fansites.txt b/doc/nvidia_notes/RIVA fansites.txt index 5a3104837..d1cfaeb90 100644 --- a/doc/nvidia_notes/RIVA fansites.txt +++ b/doc/nvidia_notes/RIVA fansites.txt @@ -3,7 +3,7 @@ RIVA User's Group http://tiger.tnstate.edu:8080/ RIVA 128 Homepage http://pages.prodigy.net/babblin5/Main.html -> Riva3D (riva3d.com) Zone 128 http://www.tc.umn.edu/~reda0003/zone128/ RIVAZone https://web.archive.org/web/19981212032348/http://www.rivazone.com/ (Launched January 2, 1998) -Dimension 128 (early domain name that was never archived) -> d128.com (1999-2001) +Dimension 128 http://dimension128.smartcom.net (early domain name that was mostly not archived) -> d128.com (1999-2001) Riva3D https://web.archive.org/web/20000525110305/http://riva3d.gxnetwork.com/s3.html nVNews https://web.archive.org/web/20001205171202/http://www.nvnews.net/ (1999-2015) @@ -21,4 +21,6 @@ https://web.archive.org/web/19980615024744/http://www.ogr.com/columns/techtalk/t https://web.archive.org/web/20001002193706/http://www.rivazone.com/files/rivalog.txt -https://web.archive.org/web/20010422044820/http://www.rivazone.com/finger/finger.cgi?nick@finger.nvidia.com nvidia .plan files \ No newline at end of file +https://web.archive.org/web/20010422044820/http://www.rivazone.com/finger/finger.cgi?nick@finger.nvidia.com nvidia .plan files + +WAVE Report - april 1997 date for tapeout \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 965e2d0e1..e91afb5c9 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -462,6 +462,14 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PME_INTR 0x200100 // Mediaport: Interrupt Pending? #define NV3_PME_INTR_EN 0x200140 // Mediaport: Interrupt Enable #define NV3_PME_END 0x200FFF + +// THIS IS NOT A REAL REGISTER. +// This is so my software e.g. nvplayground can determine if the software is being run in an emulator on a real RIVA 128. +// This register should have some sort of open bus, garbage or 00/FF on a real NV3/NV3T but have a string. +#define NV3_EMULATED_MARKER_START 0x269420 + +#define NV3_EMULATED_MARKER 0x0D15EA5E + #define NV3_PGRAPH_START 0x400000 // Scene graph for 2d/3d rendering...the most important part // PGRAPH Core diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 2625adda4..204d3f29c 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -955,7 +955,6 @@ void nv3_pfifo_cache1_pull(void) nv_log_verbose_only("***** DEBUG: CACHE1 PULLED ****** Contextual information below\n"); - nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; nv3_debug_ramin_print_context_info(current_param, context_structure); From 9f3e189455ebec810e1ed624ded51f296dd5ae02 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 23 May 2025 16:07:04 +0100 Subject: [PATCH 195/274] minor changes for nvplay --- src/video/nv/nv3/nv3_core_arbiter.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 99aebb5e7..7182cc955 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -107,8 +107,15 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) ret = nv3_user_read(address); else { - warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); - return 0x00; + //nvplay stuff + #ifdef ENABLE_NV_LOG_ULTRA + warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); + #else + nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); + #endif + + // I don't know why the real hardware does this. But it does. + return (ret & 1) ? 0x20 : 0x07; } return ret; @@ -173,7 +180,13 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) //RAMIN is its own thing else { - warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x\n", address); + //nvplay stuff + #ifdef ENABLE_NV_LOG_ULTRA + warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); + #else + nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); + #endif + return; } } From 230b90954d43e8e72273f7f4e8fa8b39c594d6c5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Mon, 26 May 2025 01:41:10 +0100 Subject: [PATCH 196/274] don't force 1-byte packing of vid_nv3_classes --- src/include/86box/nv/classes/vid_nv3_classes.h | 2 -- src/include/86box/nv/vid_nv.h | 6 +++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index d5dc3ed43..5c2cc5b8e 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -36,7 +36,6 @@ // or everything FUCKS UP // // DO NOT REMOVE! DO NOT REMOVE! DO NOT REMOVE! -#pragma pack(push, 1) // CLass names for debugging extern const char* nv3_class_names[]; @@ -1192,7 +1191,6 @@ typedef struct nv3_grobj_s uint32_t grobj_3; } nv3_grobj_t; // TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! -#pragma pack(pop) // return packing to whatever it was before this disaster // PIO Subchannel info #define NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE 0x0010 diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index ef5e80d71..84ccf5a15 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -6,7 +6,10 @@ * * This file is part of the 86Box distribution. * - * Shared implementation file for all NVIDIA GPUs (hopefully to be) emulated by 86box. + * The real Nvidia driver. + * Implements components shared by all variants of the Nvidia GPUs (hopefully to be) supported by 86Box. + * + * This driver draws on collaborative work by many people over many years. * * Credit to: * @@ -18,6 +21,7 @@ * - xemu developers https://github.com/xemu-project/xemu * - RivaTV developers https://rivatv.sourceforge.net (esp. https://rivatv.sourceforge.net/stuff/riva128.txt) * - Nvidia for leaking their driver symbols numerous times ;^) https://nvidia.com + * - Vort (Bochs GeForce fork) https://github.com/Vort/Bochs/tree/geforce * - People who prevented me from giving up (various) * * Authors: Connor Hyde / starfrost From ff0eaa7a6993dc1919f8744e1490269173943a94 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 3 Jun 2025 01:30:22 +0100 Subject: [PATCH 197/274] various minor fixes to how classes are defined --- .../86box/nv/classes/vid_nv3_classes.h | 75 ++++++++----------- src/include/86box/nv/vid_nv3.h | 4 +- src/video/nv/nv3/subsystems/nv3_pfifo.c | 12 +-- 3 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 5c2cc5b8e..e8fed0327 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -294,6 +294,15 @@ typedef struct nv3_color_and_coord_16_s nv3_coord_16_t points; } nv3_color_and_coord_16_t; +/* "UTRI" type triangle */ +typedef struct nv3_utri_s +{ + uint32_t color; // use nv3_color_expanded_t but changed for alignment reasons + nv3_coord_16_t point0; + nv3_coord_16_t point1; + nv3_coord_16_t point2; +} nv3_utri_t; + /* Generic 16-bit clip region */ typedef struct nv3_clip_16_s { @@ -322,35 +331,10 @@ typedef struct nv3_object_class_001 { nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) uint32_t set_notify; - uint8_t set_beta_factor_1d31; // 31:31 (?) value, 30:21 fraction + uint32_t set_beta_factor_1d31; // 31:31 (?) value, 30:21 fraction // Put the rest of it here } nv3_beta_factor_t; -/* Note: This is not used in the class, there are "special" rops that do certain things. So they need to be defined for code readability. It all gets optimised away -by the compiler anyway */ -typedef enum nv3_render_operation_type_e -{ - // Black - nv3_rop_blackness = 0x00, - // dst = !src - nv3_rop_dstinvert = 0x55, - // pattern invert - nv3_rop_patinvert = 0x5A, - // src ^ dst - nv3_rop_xor = 0x66, - // src & dst - nv3_rop_srcand = 0x88, - // dst = src (?) - nv3_rop_dstcopy = 0xAA, - // src = dst (?) - nv3_rop_srccopy = 0xCC, - // paint source - nv3_rop_srcpaint = 0xEE, - // pattern copy - nv3_rop_patcopy = 0xF0, - // White - nv3_rop_whiteness = 0xFF, -} nv3_render_operation_type; /* Object class 0x02 (real hardware) 0x14/0x43 (drivers) @@ -359,9 +343,9 @@ typedef enum nv3_render_operation_type_e */ typedef struct nv3_object_class_002 { - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint8_t rop; // ROP3 (ID = ????????) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint32_t set_notify; // Set notifier + uint8_t rop; // ROP3 (ID = ????????) } nv3_render_operation_t; /* @@ -372,9 +356,9 @@ typedef struct nv3_object_class_002 */ typedef struct nv3_object_class_003 { - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint8_t color; // ROP3 (ID = ????????) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint32_t set_notify; // Set notifier + uint32_t color; // ROP3 (ID = ????????) } nv3_chroma_key_t; /* @@ -385,9 +369,9 @@ typedef struct nv3_object_class_003 */ typedef struct nv3_object_class_004 { - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint8_t color; // ROP3 (ID = ????????) + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint32_t set_notify; // Set notifier + uint32_t color; // Plane mask } nv3_plane_mask_t; /* @@ -533,9 +517,9 @@ typedef struct nv3_object_class_00A */ typedef struct nv3_object_class_00B { - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - nv3_color_expanded_t color; // argb? + nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) + uint32_t set_notify; // Set notifier + nv3_color_expanded_t color; // argb? // The points of the triangle. nv3_coord_16_t points[3]; @@ -544,13 +528,14 @@ typedef struct nv3_object_class_00B uint32_t y0; uint32_t x1; uint32_t y1; - uint32_t y2; - uint32_t x2; + uint32_t x2; + uint32_t y2; - nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? - nv3_coord_32_t mesh32[16]; - nv3_color_and_coord_16_t ctriangle[3]; // Triangle with colour - nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours + nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? + nv3_coord_32_t mesh32[16]; // Mesh with 32-bit format + nv3_utri_t ctriangle[8]; // Triangles with colour + nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours + } nv3_triangle_t; /* @@ -834,13 +819,13 @@ typedef enum nv3_d3d5_texture_size_e nv3_d3d5_texture_size_128x128 = 7, - // Highest size supported natively by hardware? nv3_d3d5_texture_size_256x256 = 8, nv3_d3d5_texture_size_512x512 = 9, nv3_d3d5_texture_size_1024x1024 = 10, + // Kind of infeasible considering hardware VRAM size limitations nv3_d3d5_texture_size_2048x2048 = 11, diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index e91afb5c9..c89f2e9b2 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -269,7 +269,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C #define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 -#define NV3_PFIFO_CACHE0_PUSH0 0x3000 +#define NV3_PFIFO_CACHE0_PUSH_ENABLED 0x3000 #define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 #define NV3_PFIFO_CACHE0_PUT 0x3010 #define NV3_PFIFO_CACHE0_STATUS 0x3014 @@ -291,7 +291,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PFIFO_CACHE0_METHOD_END 0x3200 #define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 #define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 -#define NV3_PFIFO_CACHE1_PUSH0 0x3200 +#define NV3_PFIFO_CACHE1_PUSH_ENABLED 0x3200 #define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 #define NV3_PFIFO_CACHE1_PUT 0x3210 #define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 204d3f29c..9dc66fc2e 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -48,8 +48,8 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE0_PUSH0, "PFIFO - Cache0 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE1_PUSH0, "PFIFO - Cache1 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE0_ENABLED, "PFIFO - Cache0 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_ENABLED, "PFIFO - Cache1 Access", NULL, NULL, }, { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, @@ -160,10 +160,10 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; break; /* Does this automatically push? */ - case NV3_PFIFO_CACHE0_PUSH0: + case NV3_PFIFO_CACHE0_ENABLED: ret = nv3->pfifo.cache0_settings.push0; break; - case NV3_PFIFO_CACHE1_PUSH0: + case NV3_PFIFO_CACHE1_ENABLED: ret = nv3->pfifo.cache1_settings.push0; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: @@ -509,10 +509,10 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: nv3->pfifo.cache1_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; - case NV3_PFIFO_CACHE0_PUSH0: + case NV3_PFIFO_CACHE0_ENABLED: nv3->pfifo.cache0_settings.push0 = val; break; - case NV3_PFIFO_CACHE1_PUSH0: + case NV3_PFIFO_CACHE1_ENABLED: nv3->pfifo.cache1_settings.push0 = val; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: From 49da2e606c28a7f2d49ea95a3907d3b2841243b1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 3 Jun 2025 02:40:23 +0100 Subject: [PATCH 198/274] fix compile --- src/video/nv/nv3/subsystems/nv3_pfifo.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 9dc66fc2e..7cd7f71a9 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -48,8 +48,8 @@ nv_register_t pfifo_registers[] = { { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE0_ENABLED, "PFIFO - Cache0 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE1_ENABLED, "PFIFO - Cache1 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE0_PUSH_ENABLED, "PFIFO - Cache0 Access", NULL, NULL, }, + { NV3_PFIFO_CACHE1_PUSH_ENABLED, "PFIFO - Cache1 Access", NULL, NULL, }, { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 Push Channel ID", NULL, NULL, }, { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, @@ -160,10 +160,10 @@ uint32_t nv3_pfifo_read(uint32_t address) ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; break; /* Does this automatically push? */ - case NV3_PFIFO_CACHE0_ENABLED: + case NV3_PFIFO_CACHE0_PUSH_ENABLED: ret = nv3->pfifo.cache0_settings.push0; break; - case NV3_PFIFO_CACHE1_ENABLED: + case NV3_PFIFO_CACHE1_PUSH_ENABLED: ret = nv3->pfifo.cache1_settings.push0; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: @@ -509,10 +509,10 @@ void nv3_pfifo_write(uint32_t address, uint32_t val) case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: nv3->pfifo.cache1_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; break; - case NV3_PFIFO_CACHE0_ENABLED: + case NV3_PFIFO_CACHE0_PUSH_ENABLED: nv3->pfifo.cache0_settings.push0 = val; break; - case NV3_PFIFO_CACHE1_ENABLED: + case NV3_PFIFO_CACHE1_PUSH_ENABLED: nv3->pfifo.cache1_settings.push0 = val; break; case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: From 2508b2e86b16fbf984fe141a95a21dab6b0e5011 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 22 Jun 2025 14:10:15 +0100 Subject: [PATCH 199/274] redesign gpu visual debugging windows --- src/qt/CMakeLists.txt | 4 + src/qt/qt_gpudebug_visualnv.cpp | 54 ++++++++ src/qt/qt_gpudebug_visualnv.hpp | 40 ++++++ src/qt/qt_gpudebug_visualnv.ui | 154 ++++++++++++++++++++++ src/qt/qt_gpudebug_vram.ui | 20 +-- src/qt/qt_mainwindow.cpp | 11 ++ src/qt/qt_mainwindow.hpp | 3 +- src/qt/qt_mainwindow.ui | 2 +- src/video/nv/nv3/render/nv3_render_blit.c | 13 +- src/video/vid_mda.c | 6 +- 10 files changed, 285 insertions(+), 22 deletions(-) create mode 100644 src/qt/qt_gpudebug_visualnv.cpp create mode 100644 src/qt/qt_gpudebug_visualnv.hpp create mode 100644 src/qt/qt_gpudebug_visualnv.ui diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 64bcd01e5..5be854154 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -157,6 +157,10 @@ add_library(ui STATIC qt_gpudebug_vram.hpp qt_gpudebug_vram.ui + qt_gpudebug_visualnv.cpp + qt_gpudebug_visualnv.hpp + qt_gpudebug_visualnv.ui + qt_harddrive_common.cpp qt_harddrive_common.hpp qt_models_common.cpp diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp new file mode 100644 index 000000000..4c0a52dde --- /dev/null +++ b/src/qt/qt_gpudebug_visualnv.cpp @@ -0,0 +1,54 @@ +/* + * 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. + * + * GPU Debugging Tools - VRAM Viewer implementation + * + * + * + * Authors: starfrost + * + * Copyright 2025 starfrost + */ + +/* C++ includes */ +#include +#include + + +/* Qt includes*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_qt_gpudebug_visualnv.h" + +/* 86Box core includes */ +extern "C" +{ + +} + +VisualNVDialog::VisualNVDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::VisualNVDialog) +{ + ui->setupUi(this); +} + +VisualNVDialog::~VisualNVDialog() +{ + +} \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.hpp b/src/qt/qt_gpudebug_visualnv.hpp new file mode 100644 index 000000000..175d1c28a --- /dev/null +++ b/src/qt/qt_gpudebug_visualnv.hpp @@ -0,0 +1,40 @@ +/* + * 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. + * + * GPU Debugging Tools - VRAM Viewer headers + * + * + * + * Authors: starfrost + * + * Copyright 2025 starfrost + */ + + +#pragma once + +#include + +namespace Ui +{ + class VisualNVDialog; +} + +class VisualNVDialog : public QDialog +{ + Q_OBJECT + + public: + explicit VisualNVDialog(QWidget *parent = nullptr); + ~VisualNVDialog(); + protected: + private: + Ui::VisualNVDialog* ui; + + +}; \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui new file mode 100644 index 000000000..e103ff03e --- /dev/null +++ b/src/qt/qt_gpudebug_visualnv.ui @@ -0,0 +1,154 @@ + + + VisualNVDialog + + + + 0 + 0 + 600 + 350 + + + + + 0 + 0 + + + + + 600 + 350 + + + + + 600 + 350 + + + + Nvidia GPU Realtime Debugger + + + + + + + + 10 + 0 + 201 + 171 + + + + PFIFO State + + + + + + 220 + 0 + 361 + 171 + + + + PGRAPH State + + + + + + 10 + 180 + 571 + 141 + + + + VRAM Control + + + + + 160 + 30 + 104 + 21 + + + + + + + 10 + 30 + 151 + 16 + + + + <html><head/><body><p>VRAM Visual Aperture Start</p></body></html> + + + + + + 420 + 30 + 91 + 16 + + + + <html><head/><body><p>FB Start Address:</p></body></html> + + + + + + 510 + 30 + 49 + 16 + + + + <html><head/><body><p><span style=" font-weight:700;">PLACEHOLDER 9000</span></p></body></html> + + + + + + 160 + 60 + 104 + 21 + + + + + + + 10 + 60 + 151 + 16 + + + + <html><head/><body><p>Pixel Depth</p></body></html> + + + + + + + + + + diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui index 12c64e3b4..8118f0613 100644 --- a/src/qt/qt_gpudebug_vram.ui +++ b/src/qt/qt_gpudebug_vram.ui @@ -32,16 +32,6 @@ VRAM Viewer - - - - VRAM - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - @@ -52,6 +42,16 @@ + + + + VRAM + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index eb238051f..7a9132031 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -24,6 +24,7 @@ #include "qt_mainwindow.hpp" #include "qt_gpudebug_vram.hpp" +#include "qt_gpudebug_visualnv.hpp" #include "ui_qt_mainwindow.h" #include "qt_specifydimensions.h" @@ -2326,4 +2327,14 @@ void MainWindow::on_actionDebug_GPUDebug_VRAM_triggered() debugVramDialog.setWindowFlag(Qt::WindowTitleHint, true); debugVramDialog.setWindowFlag(Qt::WindowSystemMenuHint, false); debugVramDialog.exec(); +} + + +void MainWindow::on_actionDebug_GPUDebug_VisualNv_triggered() +{ + VisualNVDialog visualNvDialog(this); + visualNvDialog.setWindowFlag(Qt::CustomizeWindowHint, true); + visualNvDialog.setWindowFlag(Qt::WindowTitleHint, true); + visualNvDialog.setWindowFlag(Qt::WindowSystemMenuHint, false); + visualNvDialog.exec(); } \ No newline at end of file diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 5cda49fa8..c71dd1ffd 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -128,7 +128,8 @@ private slots: void on_actionEnable_Discord_integration_triggered(bool checked); void on_actionRenderer_options_triggered(); void on_actionDebug_GPUDebug_VRAM_triggered(); - + void on_actionDebug_GPUDebug_VisualNv_triggered(); + void refreshMediaMenu(); void showMessage_(int flags, const QString &header, const QString &message, bool richText, std::atomic_bool* done = nullptr); void getTitle_(wchar_t *title); diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 2861b65be..762361ae1 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -914,7 +914,7 @@ - GPU Debug - NV3 Visual Debugger + GPU Realtime Debugger (NVIDIA ONLY) diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 596d7b7a1..9bf87ec6d 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -134,9 +134,8 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - - nv3_coord_16_t old_position = nv3->pgraph.blit.point_in; - nv3_coord_16_t new_position = nv3->pgraph.blit.point_out; + nv3_coord_16_t in_position = nv3->pgraph.blit.point_in; + nv3_coord_16_t out_position = nv3->pgraph.blit.point_out; /* Coordinates for copying an entire line at a time */ uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.x; @@ -159,10 +158,10 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) { buf_position = (nv3->pgraph.blit.size.x * y); /* shouldn't matter in non-wtf mode */ - vram_position = nv3_render_get_vram_address_for_buffer(old_position, dst_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(in_position, src_buffer); memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); - old_position.y++; + in_position.y++; /* 32bit buffer */ } @@ -170,9 +169,9 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) { buf_position = (nv3->pgraph.blit.size.x * y); - vram_position = nv3_render_get_vram_address_for_buffer(new_position, src_buffer); + vram_position = nv3_render_get_vram_address_for_buffer(out_position, dst_buffer); memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); - new_position.y++; + out_position.y++; } } \ No newline at end of file diff --git a/src/video/vid_mda.c b/src/video/vid_mda.c index 7b6e54806..63bb2df07 100644 --- a/src/video/vid_mda.c +++ b/src/video/vid_mda.c @@ -317,7 +317,7 @@ mda_standalone_init(UNUSED(const device_t *info)) mda->vram = malloc(0x1000); - switch(device_get_config_int("font")) { + switch (device_get_config_int("font")) { case 0: loadfont(FONT_IBM_MDA_437_PATH, 0); break; @@ -375,7 +375,7 @@ mda_speed_changed(void *priv) } static const device_config_t mda_config[] = { - // clang-format off + // clang-format off { .name = "rgb_type", .description = "Display type", @@ -411,7 +411,7 @@ static const device_config_t mda_config[] = { .bios = { { 0 } } }, { .name = "", .description = "", .type = CONFIG_END } - // clang-format on + // clang-format on }; const device_t mda_device = { From d5a9411fe732fabe2dc2731b8715506db9d68ad4 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 22 Jun 2025 15:59:44 +0100 Subject: [PATCH 200/274] Implement hardware cursor support (9x now displays mouse cursor) --- src/include/86box/nv/vid_nv3.h | 11 ++- src/qt/qt_gpudebug_visualnv.ui | 31 ++++++--- src/video/nv/nv3/nv3_core.c | 92 +++++++++++++++---------- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- 4 files changed, 83 insertions(+), 53 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index c89f2e9b2..632ee01fa 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -463,13 +463,6 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PME_INTR_EN 0x200140 // Mediaport: Interrupt Enable #define NV3_PME_END 0x200FFF -// THIS IS NOT A REAL REGISTER. -// This is so my software e.g. nvplayground can determine if the software is being run in an emulator on a real RIVA 128. -// This register should have some sort of open bus, garbage or 00/FF on a real NV3/NV3T but have a string. -#define NV3_EMULATED_MARKER_START 0x269420 - -#define NV3_EMULATED_MARKER 0x0D15EA5E - #define NV3_PGRAPH_START 0x400000 // Scene graph for 2d/3d rendering...the most important part // PGRAPH Core @@ -800,6 +793,10 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_RAMIN_RAMRM_START 0x1C02000 #define NV3_RAMIN_RAMRM_END 0x1C02FFF +// I'm not sure if this can be moved. +// 4K of "PRAM" is listed at 0x6000. +#define NV3_RAMIN_OFFSET_CURSOR 0x6000 + #define NV3_RAMIN_END 0x1FFFFFF // not done diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui index e103ff03e..313df859d 100644 --- a/src/qt/qt_gpudebug_visualnv.ui +++ b/src/qt/qt_gpudebug_visualnv.ui @@ -6,7 +6,7 @@ 0 0 - 600 + 620 350 @@ -18,13 +18,13 @@ - 600 + 620 350 - 600 + 620 350 @@ -88,7 +88,7 @@ 10 30 151 - 16 + 21 @@ -98,7 +98,7 @@ - 420 + 390 30 91 16 @@ -111,14 +111,14 @@ - 510 + 480 30 - 49 + 81 16 - <html><head/><body><p><span style=" font-weight:700;">PLACEHOLDER 9000</span></p></body></html> + <html><head/><body><p><span style=" font-weight:700;">PLACEHOLDER</span></p></body></html> @@ -137,13 +137,26 @@ 10 60 151 - 16 + 21 <html><head/><body><p>Pixel Depth</p></body></html> + + + + 10 + 110 + 231 + 24 + + + + Load nvplay savestate from real hardware + + diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 21810386e..39aa0178d 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -866,9 +866,14 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) if ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_CURSOR_START] >> NV3_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01) return; - // On windows, this shows up using NV_IMAGE_IN_MEMORY. + // NT GDI drivers: Load cursor using NV_IMAGE_FROM_MEMORY ("NV3LCD") + // 9x GDI drivers: Use H/W cursor in RAMIN + // Do we need to emulate it? - uint32_t vram_cursor_base = nv3->pramdac.cursor_address; + + // THIS IS CORRECT. BUT HOW DO WE FIND IT? + uint32_t ramin_cursor_position = NV3_RAMIN_OFFSET_CURSOR; + /* let's just assume buffer 0 here...that code needs to be totally rewritten*/ nv3_coord_16_t start_position = nv3->pramdac.cursor_start; @@ -890,49 +895,64 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline) We have to get a 32x32, "A"1R5G5B5-format cursor out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1 - Technically these are expanded to RGB10, but I don't see why this needs to happen. And our pipeline isn't set up for it anyway. + These are expanded to RGB10 only if they are XORed. We don't do this (we don't really need to + there is no grobj specified here so special casing + would be needed) so we just xor it with the current pixel format */ for (int32_t y = 0; y < NV3_PRAMDAC_CURSOR_SIZE_Y; y++) { for (int32_t x = 0; x < NV3_PRAMDAC_CURSOR_SIZE_X; x++) { - uint16_t current_pixel = vram_16[vram_cursor_base << 1]; - bool replace_bit = (current_pixel & 0x8000); - - switch (nv3->nvbase.svga.bpp) + uint16_t current_pixel = nv3_ramin_read16(ramin_cursor_position, nv3); + + // 0000 = transparent, so skip drawing + if (current_pixel) { - /* this is indexed colour but... lol */ - case 8: - if (replace_bit) - { - uint8_t final = current_pixel ^ nv3->nvbase.svga.vram[final_position]; - nv3->nvbase.svga.vram[final_position] = final; - } - else // just override - nv3->nvbase.svga.vram[final_position] = current_pixel; - case 15 ... 16: // easy case (our cursor is 15bpp format) - uint32_t index_16 = final_position >> 1; - if (replace_bit) - { - uint16_t final = current_pixel ^ vram_16[index_16]; - vram_16[index_16] = final; - } - else // just override - vram_16[index_16] = current_pixel; - case 32: - uint32_t index_32 = final_position >> 2; - if (replace_bit) - { - uint16_t final = current_pixel ^ vram_32[index_32]; - vram_32[index_32] = final; - } - else // just override - vram_32[index_32] = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here - break; + bool replace_bit = (current_pixel & 0x8000); + + // use buffer 0 BPIXEL + uint32_t bpixel_format = (nv3->pgraph.bpixel[0]) & 0x03; + + switch (bpixel_format) + { + case bpixel_fmt_8bit: + if (replace_bit) + nv3->nvbase.svga.vram[final_position] = current_pixel; + else //xor + { + // not sure what to do here. we'd have to search through the palette to find the closest possible colour. + uint8_t final = current_pixel ^ nv3->nvbase.svga.vram[final_position]; + nv3->nvbase.svga.vram[final_position] = final; + } + case bpixel_fmt_16bit: // easy case (our cursor is 15bpp format) + uint32_t index_16 = final_position >> 1; + + if (replace_bit) // just replace + vram_16[index_16] = current_pixel; + else // xor + { + current_pixel &= ~0x8000; // mask off the xor bit + uint16_t final = current_pixel ^ vram_16[index_16]; + vram_16[index_16] = final; + } + case bpixel_fmt_32bit: + uint32_t index_32 = final_position >> 2; + + if (replace_bit) // just replace + vram_32[index_32] = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here + else //xor + { + current_pixel &= ~0x8000; // mask off the xor bit + uint32_t current_pixel_32 = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here + + uint32_t final = current_pixel_32 ^ vram_32[index_32]; + vram_32[index_32] = final; + } + break; + } } // increment vram position - vram_cursor_base += 2; + ramin_cursor_position += 2; // go switch (nv3->nvbase.svga.bpp) diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 7cd7f71a9..542aa26fa 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -739,7 +739,7 @@ void nv3_pfifo_cache0_pull(void) uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; - // Tell the CPU if we found a software method + // Tell the CPU if we found a software method and turn off cache pulling if (!(current_context & 0x800000)) { nv_log("The object in CACHE0 is a software object\n"); From 305d305173a7b041fca1b76e6d98470d6ac2c7c6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 22 Jun 2025 17:34:54 +0100 Subject: [PATCH 201/274] Basic AGP support. Card is now detected, but can't get to desktop --- src/include/86box/nv/vid_nv.h | 1 + src/include/86box/nv/vid_nv3.h | 59 +++++++++++++++++ src/video/nv/nv3/nv3_core.c | 80 ++++++++++++++++++++++- src/video/nv/nv3/render/nv3_render_blit.c | 11 ++-- 4 files changed, 145 insertions(+), 6 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 84ccf5a15..8a8e2985c 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -131,6 +131,7 @@ typedef struct nv_base_s void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID uint32_t last_buffer_address; // Last buffer address. + bool agp_enabled; // AGP Enabled (for debugging) } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 632ee01fa..80307a5ea 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -140,6 +140,8 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PCI_CFG_VBIOS_BASE_L 0x32 #define NV3_PCI_CFG_VBIOS_BASE_H 0x33 +#define NV3_AGP_CAPABILITIES_POINTER 0x34 + #define NV3_PCI_CFG_INT_LINE 0x3C #define NV3_PCI_CFG_INT_PIN 0x3D @@ -151,6 +153,63 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PCI_CFG_MAX_LATENCY 0x3F #define NV3_PCI_CFG_MAX_LATENCY_DEFAULT 0x01 +// +// AGP configuration +// + +#define NV3_AGP_START 0x44 + +// stupid stupid pci system +#define NV3_AGP_CAPABILITIES_START 0x44 +#define NV3_AGP_CAPABILITIES_CAP_ID 0x44 +#define NV3_AGP_CAPABILITIES_CAP_ID_AGP 0x02 // "AGP" +#define NV3_AGP_CAPABILITIES_NEXT_PTR 0x45 +#define NV3_AGP_CAPABILITIES_AGP_VERSION 0x46 +#define NV3_AGP_CAPABILITIES_AGP_VERSION_MINOR 0 +#define NV3_AGP_CAPABILITIES_AGP_VERSION_MAJOR 4 +#define NV3_AGP_CAPABILITIES_H 0x47 + +#define NV3_AGP_STATUS_RATE 0x48 +#define NV3_AGP_STATUS_RATE_1X_SUPPORTED 0x1 +#define NV3_AGP_STATUS_RATE_2X_SUPPORTED 0x2 + +#define NV3_AGP_STATUS_BYTE1 0x49 +#define NV3_AGP_STATUS_BYTE1_SBA 1 +#define NV3_AGP_STATUS_SBA_SUPPORTED 0x0 +#define NV3_AGP_STATUS_SBA_UNSUPPORTED 0x1 +#define NV3_AGP_STATUS_MAX_REQUESTS 0x4B +#define NV3_AGP_STATUS_MAX_REQUESTS_AMOUNT 4 + +#define NV3_AGP_COMMAND 0x4C +#define NV3_AGP_COMMAND_DATA_RATE 0 +#define NV3_AGP_COMMAND_DATA_RATE_1X 0x1 +#define NV3_AGP_COMMAND_DATA_RATE_2X 0x2 +#define NV3_AGP_COMMAND_BYTE1 0x4D +#define NV3_AGP_COMMAND_BYTE1_ENABLE 0 +#define NV3_AGP_COMMAND_BYTE1_ENABLE_DISABLED 0x0 +#define NV3_AGP_COMMAND_BYTE1_ENABLE_ENABLED 0x1 +#define NV3_AGP_COMMAND_SBA_ENABLE 1 +#define NV3_AGP_COMMAND_SBA_ENABLE_DISABLED 0x0 +#define NV3_AGP_COMMAND_SBA_ENABLE_ENABLED 0x1 +#define NV3_AGP_COMMAND_REQUEST_DEPTH 0x4F + +#define NV3_AGP_END 0x4F + +// +// ACPI (NV3T only) +// TODO: IMPLEMENT THIS!!!!!! +// +#define NV3_POWER_CAP_ID 0x60 +#define NV3_POWER_NEXT_PTR 0x61 +#define NV3_POWER_VERSION 0x62 + +// "The RIVA128ZX does not physically change its power consumption when +// POWER_STATE is modified." +#define NV3_POWER_STATE 0x64 + +#define NV3_POWER_STATE_D0 0x0 +#define NV3_POWER_STATE_D3HOT 0x3 + // GPU Subsystems // These most likely correspond to functional blocks in the original design diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 39aa0178d..e97c5fa1f 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -235,6 +235,46 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) nv3_mmio_arbitrate_write(addr, val); } +// AGP read function +uint8_t nv3_agp_read(int32_t func, int32_t addr) +{ + uint8_t ret = 0x00; + + switch (addr) + { + case NV3_AGP_CAPABILITIES_CAP_ID: + ret = NV3_AGP_CAPABILITIES_CAP_ID_AGP; // AGP capable device + break; + case NV3_AGP_CAPABILITIES_NEXT_PTR: // Always off + ret = 0x00; + case NV3_AGP_CAPABILITIES_AGP_VERSION: + ret = (0x1 << NV3_AGP_CAPABILITIES_AGP_VERSION_MAJOR) | NV3_AGP_CAPABILITIES_AGP_VERSION_MINOR; + break; + case NV3_AGP_STATUS_RATE: + // NV3T = AGP 2X, NV3 = AGP 1X + if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) + ret = NV3_AGP_STATUS_RATE_1X_SUPPORTED | NV3_AGP_STATUS_RATE_2X_SUPPORTED; + else + ret = NV3_AGP_STATUS_RATE_1X_SUPPORTED; + break; + case NV3_AGP_STATUS_BYTE1: + ret = 0x00; // SBA not supported + break; + case NV3_AGP_STATUS_MAX_REQUESTS: + ret = NV3_AGP_STATUS_MAX_REQUESTS_AMOUNT; + break; + // This is also used for SBA but SBA is always off so we can use a bool + case NV3_AGP_COMMAND_BYTE1: + ret = nv3->nvbase.agp_enabled; + break; + default: + ret = nv3->pci_config.pci_regs[addr]; + break; + } + + return ret; +} + // PCI stuff // BAR0 Pointer to MMIO space // BAR1 Pointer to Linear Framebuffer (NV_USER) @@ -332,7 +372,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) 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); + ret = (NV3_PCI_CFG_BAR_PREFETCHABLE_ENABLED << NV3_PCI_CFG_BAR_PREFETCHABLE); break; // These registers are hardwired to zero per the datasheet @@ -355,6 +395,13 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) ret = nv3->pci_config.vbios_enabled; break; + case NV3_AGP_CAPABILITIES_POINTER: + if (nv3->nvbase.bus_generation >= nv_bus_agp_1x) + ret = NV3_AGP_CAPABILITIES_START; + else + ret = 0x00; + break; + case NV3_PCI_CFG_INT_LINE: ret = nv3->pci_config.int_line; break; @@ -381,6 +428,15 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) ret = nv3->pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)]; break; + case NV3_AGP_START ... NV3_AGP_END: + if (nv3->nvbase.bus_generation < nv_bus_agp_1x) + break; + + ret = nv3_agp_read(func, addr); + + break; + + default: // by default just return pci_config.pci_regs ret = nv3->pci_config.pci_regs[addr]; break; @@ -391,6 +447,20 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) return ret; } +void nv3_agp_write(int32_t func, int32_t addr, uint8_t val) +{ + nv3->pci_config.pci_regs[addr] = val; + + switch (addr) + { + case NV3_AGP_COMMAND_BYTE1: + nv3->nvbase.agp_enabled = val; + break; + default: + break; + } +} + void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) { @@ -489,6 +559,14 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) nv3->pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)] = val; break; + case NV3_AGP_START ... NV3_AGP_END: + if (nv3->nvbase.bus_generation < nv_bus_agp_1x) + break; + + nv3_agp_write(func, addr, val); + + break; + default: break; } diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 9bf87ec6d..c1b221648 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -140,12 +140,13 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) /* Coordinates for copying an entire line at a time */ uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.x; - /* Read the old pixel into the line buffer - Assumption: All data is sent in an unpacked format. In the case of an NVIDIA GPU this means that all data is sent 32 bits at a time regardless of if - the actual source data is 32 bits in size or not. For pixel data, the upper bits are left as 0 in 8bpp/16bpp mode. For 86box purposes, the data is written - 8/16 bits at a time. + /* + Read the old pixel into the line buffer + Assumption: All data is sent in an unpacked format. In the case of an NVIDIA GPU this means that all data is sent 32 bits at a time regardless of if + the actual source data is 32 bits in size or not. For pixel data, the upper bits are left as 0 in 8bpp/16bpp mode. For 86box purposes, the data is written + 8/16 bits at a time. - TODO: CHECK FOR PACKED FORMAT!!!!! + TODO: CHECK FOR PACKED FORMAT!!!!! */ if (nv3->nvbase.svga.bpp == 15 From 4e2266e761ed39b6aef0fc11c85ab2493e927127 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 24 Jun 2025 11:46:16 +0100 Subject: [PATCH 202/274] update status.xlsx --- doc/nvidia_notes/status.xlsx | Bin 15953 -> 15945 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx index b73941d31dae703054ddc6f338acef9f0a12bcd4..bfeeb14528f00e6aa6a6f1d9e1f055ab1399947f 100644 GIT binary patch delta 6790 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 eYmbJ4@+Z#u4>^IaOG=@-!9h|CSf*nCDE Date: Wed, 2 Jul 2025 18:03:16 +0100 Subject: [PATCH 203/274] Fix compile due to svga changes Improve RMA code which is one of the oldest pieces of code in the entire emulation. --- src/include/86box/nv/vid_nv3.h | 8 ++++---- src/video/CMakeLists.txt | 1 - src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/subsystems/nv3_pbus.c | 16 ++++------------ src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 80307a5ea..96d9d3dd1 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -908,7 +908,7 @@ extern const device_config_t nv3t_config[]; // Confi // These are nvidia, licensed from weitek (25-63) #define NV3_CRTC_REGISTER_RPC0 0x19 // 7:5 - [10:8] of CRTC. 4:0 - [20:16] of 21-bit display buffer address -#define NV3_CRTC_REGISTER_RPC1 0x1A // What does this mean? +#define NV3_CRTC_REGISTER_RPC1 0x1A // bit7=hsync enabled, bit6=vsync enabled, bit4="compatible text", bit2=large screen, bit1=6bit palette width (>1280) #define NV3_CRTC_REGISTER_READ_BANK 0x1D #define NV3_CRTC_REGISTER_WRITE_BANK 0x1E #define NV3_CRTC_REGISTER_FORMAT 0x25 @@ -921,8 +921,8 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_HEB 0x2D // HRS most significant bit -#define NV3_CRTC_REGISTER_CURSOR_ADDR0 0x30 // Cursor high -#define NV3_CRTC_REGISTER_CURSOR_ADDR1 0x31 // Cursor low (1:0 = enable) +#define NV3_CRTC_REGISTER_CURSOR_ADDR0 0x30 // Cursor high 21:16 +#define NV3_CRTC_REGISTER_CURSOR_ADDR1 0x31 // Cursor low (1:0 = enable) 15:11 #define NV3_CRTC_REGISTER_PIXELMODE_VGA 0x00 // vga textmode #define NV3_CRTC_REGISTER_PIXELMODE_8BPP 0x01 @@ -1413,7 +1413,7 @@ typedef struct nv3_ptimer_s } nv3_ptimer_t; // Object name is just a uint32_t identifier it doesn't need a struct -// This is howt he cotnext is represented in ramin +// This is how the context is represented in ramin // IN PGRAPH IT IS DIFFERENT! ONLY 5 BITS FOR THE CLASS ID! WHY? typedef struct nv3_ramin_context_s { diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 6f9693e5a..fa6887b50 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -192,7 +192,6 @@ add_library(vid OBJECT # Generic vid_bochs_vbe.c - ) if(G100) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index e97c5fa1f..273582f84 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -585,7 +585,7 @@ void nv3_recalc_timings(svga_t* svga) nv3_t* nv3 = (nv3_t*)svga->priv; uint32_t pixel_mode = svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03; - svga->ma_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; + svga->memaddr_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; /* Turn off override if we are in VGA mode */ svga->override = !(pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA); diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c index 16934998c..a2e340e85 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus.c @@ -164,13 +164,8 @@ uint8_t nv3_pbus_rma_read(uint16_t addr) 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) & (nv3->nvbase.svga.vram_max - 1), &nv3->nvbase.svga); - nv3->nvbase.svga.chain4 = false; - nv3->nvbase.svga.packed_chain4 = false; + /* Do we need to read RAMIN here? */ + ret = nv3->nvbase.svga.vram[real_final_address - NV3_MMIO_SIZE] & (nv3->nvbase.svga.vram_max - 1); } // log current location for vbios RE @@ -246,11 +241,8 @@ void nv3_pbus_rma_write(uint16_t addr, uint8_t val) 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) & (nv3->nvbase.svga.vram_max - 1), nv3->pbus.rma.data, &nv3->nvbase.svga); - nv3->nvbase.svga.chain4 = false; - nv3->nvbase.svga.packed_chain4 = false; + uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; + vram_32[(nv3->pbus.rma.addr - NV3_MMIO_SIZE) >> 2] = nv3->pbus.rma.data; } diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 4cd6d10c3..59e3dcdbc 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -202,7 +202,7 @@ void nv3_pfifo_interrupt(uint32_t id, bool fire_now) /* RAMIN access arbitration functions -Arbitrates reads and writes to RAMFC (unused dma context storage), RAMRO (invalid object submission location), RAMHT (hashtable for graphics objectstorage) (RAMAU?) +Arbitrates reads and writes to RAMFC (unused dma context storage), RAMRO (invalid object submission location), RAMHT (hashtable for graphics objectstorage) unused audio memory (RAMAU?) and generic RAMIN Takes a pointer to a result integer. This is because we need to check its result in our normal write function. From 76a83d600197f140f424c94c3fde8ef1776f6476 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 4 Jul 2025 21:55:58 +0100 Subject: [PATCH 204/274] *manually* fix cmakelists conflict because stupid github won't let me reverse-merge fixes?? --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 504a563e3..2f654497d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,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) +# Remove when merged, should just be -D +option(NV_LOG "NVidia RIVA 128 debug logging" ON) +option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) +option(LIBASAN "Enable compilation with the addresss sanitizer" OFF) + # Remove when merged, should just be -D option(NV_LOG "NVidia RIVA 128 debug logging" ON) option(NV_LOG_ULTRA "Even more NVidia RIVA 128 debug logging" OFF) From ebea107e820ef7aa93738c30ec44d9b1625182ec Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Fri, 4 Jul 2025 23:54:39 +0100 Subject: [PATCH 205/274] Remove nvidia debugging option if not an nvidia card --- src/machine/machine_table.c | 1 + src/qt/qt_gpudebug_visualnv.cpp | 3 ++ src/qt/qt_gpudebug_vram.ui | 33 ++++++---------------- src/qt/qt_mainwindow.cpp | 50 ++++++++++++++++++++++++--------- src/qt/qt_mainwindow.hpp | 2 ++ 5 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 87dd38786..98619452c 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -16,6 +16,7 @@ * Copyright 2017-2025 Fred N. van Kempen. * Copyright 2025 Jasmine Iwanek. */ +#include #include #include #include diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp index 4c0a52dde..42f108730 100644 --- a/src/qt/qt_gpudebug_visualnv.cpp +++ b/src/qt/qt_gpudebug_visualnv.cpp @@ -35,6 +35,9 @@ #include #include "ui_qt_gpudebug_visualnv.h" +/* NOTE: DO NOT REMOVE */ +#include <86box/nv/vid_nv3.h> + /* 86Box core includes */ extern "C" { diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui index 8118f0613..a92a60306 100644 --- a/src/qt/qt_gpudebug_vram.ui +++ b/src/qt/qt_gpudebug_vram.ui @@ -6,8 +6,8 @@ 0 0 - 421 - 269 + 600 + 400 @@ -18,39 +18,22 @@ - 421 - 269 + 600 + 400 - 421 - 269 + 600 + 400 VRAM Viewer - - - - TextLabel - - - Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft - - - - - - - VRAM - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - + + diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 330cee551..28200afac 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -268,20 +268,7 @@ MainWindow::MainWindow(QWidget *parent) this->setWindowTitle(QString("%1 - %2 %3").arg(vmname, EMU_NAME, EMU_VERSION_FULL)); connect(this, &MainWindow::hardResetCompleted, this, [this]() { - ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA)); - num_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - scroll_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - caps_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - /* TODO: Base this on keyboard type instead when that's done. */ - kana_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD) && - machine_has_flags(machine, MACHINE_AX)); - while (QApplication::overrideCursor()) - QApplication::restoreOverrideCursor(); -#ifdef USE_WACOM - ui->menuTablet_tool->menuAction()->setVisible(mouse_input_mode >= 1); -#else - ui->menuTablet_tool->menuAction()->setVisible(false); -#endif + onHardResetCompleted(); }); connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::QueuedConnection); @@ -798,6 +785,41 @@ MainWindow::MainWindow(QWidget *parent) updateShortcuts(); } +void MainWindow::onHardResetCompleted() +{ + ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA)); + num_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + scroll_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + caps_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + /* TODO: Base this on keyboard type instead when that's done. */ + kana_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD) && + machine_has_flags(machine, MACHINE_AX)); + while (QApplication::overrideCursor()) + QApplication::restoreOverrideCursor(); +#ifdef USE_WACOM + ui->menuTablet_tool->menuAction()->setVisible(mouse_input_mode >= 1); +#else + ui->menuTablet_tool->menuAction()->setVisible(false); +#endif + +#ifdef ENABLE_NV_LOG + /* + THIS CODE SUCKS AND THIS DESIGN IS TERRIBLE - EVERYTHING ABOUT IT IS BAD AND WRONG. + ENTIRE DEVICE SUBSYSTEM IDEALLY WOULD BE DECOUPLED FROM UI BUT MEH + */ + + const device_t* vid_device = video_card_getdevice(gfxcard[0]); + + bool is_nv3 = (vid_device == &nv3_device_agp + || vid_device == &nv3_device_pci + || vid_device == &nv3t_device_agp + || vid_device == &nv3t_device_pci); + + ui->actionDebug_GPUDebug_VisualNv->setVisible(is_nv3); +#endif +} + + void MainWindow::closeEvent(QCloseEvent *event) { diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 5cf95e7b9..5db957d45 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -70,6 +70,8 @@ signals: public slots: void showSettings(); void hardReset(); + void onHardResetCompleted(); + void togglePause(); void initRendererMonitorSlot(int monitor_index); void destroyRendererMonitorSlot(int monitor_index); From 383a8f468d0957fa6c19bedaa4d85ea2709bfb07 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 5 Jul 2025 00:50:10 +0100 Subject: [PATCH 206/274] Cleanup some unused code + UI test --- .../86box/nv/classes/vid_nv3_classes.h | 7 +----- src/include/86box/nv/vid_nv3.h | 24 ++++--------------- src/qt/qt_gpudebug_visualnv.cpp | 22 +++++++++++++---- src/qt/qt_gpudebug_visualnv.hpp | 3 +++ src/qt/qt_gpudebug_visualnv.ui | 4 ++-- src/video/nv/nv3/nv3_core.c | 4 ++-- 6 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index e8fed0327..85979dd45 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -14,7 +14,7 @@ * (they are converted to pointers). In the case of NV3, these map directly to the PHYSICAL PGRAPH REGISTERS while sitting in RAMHT!!!!. * * Also, these class IDs don't relate to the internal architecture of the GPU. - * Effectively, the NVIDIA drivers are faking shit. There are only 16 classes but the drivers recognise many more. See nv3_object_classes_driver.txt for the list of + * Effectively, the NVIDIA drivers are faking shit. There are only 22 classes but the drivers recognise many more and have a different naming scheme. See nv3_object_classes_driver.txt for the list of * classes recognised by the driver. * This is why the Class IDs you see here are not the same as you may see in other places. * @@ -32,11 +32,6 @@ #include #include -// This is slower, but these need to map *****EXACTLY***** to the registers in PGRAPH, -// or everything FUCKS UP -// -// DO NOT REMOVE! DO NOT REMOVE! DO NOT REMOVE! - // CLass names for debugging extern const char* nv3_class_names[]; diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 96d9d3dd1..f103e0fb8 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -935,7 +935,6 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_I2C 0x3E #define NV3_CRTC_REGISTER_I2C_GPIO 0x3F -// 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 @@ -1066,7 +1065,7 @@ typedef struct nv3_pbus_s typedef struct nv3_pfifo_cache_s { - bool push0; // Can we even access this cache? + bool push0; // Can we even access this cache? uint8_t put_address; // Trigger a DMA into the value you put here. uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. uint8_t channel; // The DMA channel ID of this cache. @@ -1180,7 +1179,7 @@ typedef struct nv3_pgraph_context_control_s } nv3_pgraph_context_control_t; /* DMA object context info - Context uploaded from CACHE0/CACH1 by DMA Puller + Context uploaded from CACHE0/CACHE1 by DMA Puller */ typedef struct nv3_pgraph_context_user_s { @@ -1193,7 +1192,7 @@ typedef struct nv3_pgraph_context_user_s bool reserved3 : 1; uint8_t channel : 7; uint8_t reserved2 : 3; - uint8_t class : 5; + uint8_t class_id : 5; uint8_t subchannel : 3; uint16_t reserved : 13; }; @@ -1468,19 +1467,6 @@ typedef enum nv3_ramin_ramro_reason_e } nv3_ramin_ramro_reason; -/* This is a gigantic error handling system */ -typedef struct nv3_ramin_ramro_entry_s -{ - - //todo -} nv3_ramin_ramro_entry_t; - -// Anti-fuckup device -typedef struct nv3_ramin_ramro_s -{ - -} nv3_ramin_ramro_t; - // context for unused channels typedef struct nv3_ramin_ramfc_s { @@ -1533,10 +1519,10 @@ typedef struct nv3_s nv3_pextdev_t pextdev; // Chip configuration nv3_ptimer_t ptimer; // programmable interval timer nv3_ramin_ramht_t ramht; // hashtable for PGRAPH objects - nv3_ramin_ramro_t ramro; // anti-fuckup mechanism for idiots who fucked up the FIFO submission + // (ramro does not need a struct) nv3_ramin_ramfc_t ramfc; // context for unused channels nv3_ramin_ramau_t ramau; // auxillary weirdnes - nv3_ramin_t pramin; // Ram for INput of DMA objects. Very important! + nv3_ramin_t pramin; // INstance memory for graphics objects. Very important! nv3_pvideo_t pvideo; // Video overlay nv3_pme_t pme; // Mediaport - external MPEG decoder and video interface //more here diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp index 42f108730..dd56653e5 100644 --- a/src/qt/qt_gpudebug_visualnv.cpp +++ b/src/qt/qt_gpudebug_visualnv.cpp @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * GPU Debugging Tools - VRAM Viewer implementation + * GPU Debugging Tools - Visual NV Debugger implementation * * * @@ -35,13 +35,18 @@ #include #include "ui_qt_gpudebug_visualnv.h" -/* NOTE: DO NOT REMOVE */ -#include <86box/nv/vid_nv3.h> - /* 86Box core includes */ extern "C" { - + /* NOTE: DO NOT REMOVE */ + #include <86box/86box.h> + #include <86box/device.h> + #include <86box/mem.h> + #include <86box/pci.h> + #include <86box/rom.h> + #include <86box/video.h> + #include <86box/nv/vid_nv.h> + #include <86box/nv/vid_nv3.h> } VisualNVDialog::VisualNVDialog(QWidget *parent) @@ -49,9 +54,16 @@ VisualNVDialog::VisualNVDialog(QWidget *parent) , ui(new Ui::VisualNVDialog) { ui->setupUi(this); + connect(ui->btnLoadSavestate, &QPushButton::clicked, this, &VisualNVDialog::on_btnLoadSavestate_clicked); } +// VisualNV dialog destructor VisualNVDialog::~VisualNVDialog() { +} + +void VisualNVDialog::on_btnLoadSavestate_clicked() +{ + warning("THIS IS VisualNVDialog::on_btnLoadSavestate_clicked!!!! (throws into hole)"); } \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.hpp b/src/qt/qt_gpudebug_visualnv.hpp index 175d1c28a..db7d66dc6 100644 --- a/src/qt/qt_gpudebug_visualnv.hpp +++ b/src/qt/qt_gpudebug_visualnv.hpp @@ -32,6 +32,9 @@ class VisualNVDialog : public QDialog public: explicit VisualNVDialog(QWidget *parent = nullptr); ~VisualNVDialog(); + + void on_btnLoadSavestate_clicked(); + protected: private: Ui::VisualNVDialog* ui; diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui index 313df859d..5bae3bb19 100644 --- a/src/qt/qt_gpudebug_visualnv.ui +++ b/src/qt/qt_gpudebug_visualnv.ui @@ -1,7 +1,7 @@ VisualNVDialog - + 0 @@ -144,7 +144,7 @@ <html><head/><body><p>Pixel Depth</p></body></html> - + 10 diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 273582f84..146069e7e 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -614,7 +614,7 @@ void nv3_recalc_timings(svga_t* svga) case NV3_CRTC_REGISTER_PIXELMODE_16BPP: /* This is some sketchy shit that is an attempt at an educated guess at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, - this is what's causing it. Possibly fucking up *ReactOS* of all things */ + this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ if ((svga->crtc[NV3_CRTC_REGISTER_VRETRACESTART] >> 1) & 0x01) svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; else @@ -710,7 +710,7 @@ uint8_t nv3_svga_read(uint16_t addr, void* priv) ret = nv3->nvbase.svga.crtcreg; break; case NV3_CRTC_REGISTER_WTF: - ret = 0x08; // Required to not freeze in certain situations on v3.xx drivers + ret = 0x08; // Required to not freeze in certain situations on v3.xx drivers. Even though this register doesn't actually exist lol break; case NV3_CRTC_REGISTER_CURRENT: // Support the extended NVIDIA CRTC register range From 9968645f1d77ad3981ca31dfeecdbae6cf13e524 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 15 Jul 2025 16:45:40 +0100 Subject: [PATCH 207/274] Still increment the get address if a software method is found. This might cause slightly more cache errors and FIFO refills, but seems like the correct thing to do. It also prevents endless loops of finding bugs while debugging --- CMakePresets.json | 3 ++- src/include/86box/nv/vid_nv3.h | 3 --- src/video/nv/nv3/nv3_core_arbiter.c | 6 +++--- src/video/nv/nv3/subsystems/nv3_pfifo.c | 20 +++++++++++--------- src/video/nv/nv3/subsystems/nv3_pramin.c | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 30feab8ab..b2ed64e97 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -36,7 +36,8 @@ "name": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON" + "NV_LOG": "ON", + "NV_LOG_ULTRA": "ON" }, "inherits": "base" }, diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index f103e0fb8..7a3fb0e36 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -951,9 +951,6 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_CRTC_REGISTER_RMA_MODE_MAX 0x0F - - - /* STRUCTURES FOR THE GPU START HERE OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index 7182cc955..e23386cac 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -114,8 +114,8 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); #endif - // I don't know why the real hardware does this. But it does. - return (ret & 1) ? 0x20 : 0x07; + // The real hardware returns a garbage pattern + return 0x00; } return ret; @@ -132,7 +132,7 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) // Ensure the addresses are dword aligned. - // I don't know why this is needed because writepriv32 is always to dword align, but it crashes if you don't do this. + // I don't know why this is needed because writepriv32 is always dword aligned in Nvidia's drivers, but it crashes if you don't do this. // Exclude the 4bpp/8bpp CLUT for this purpose if (!(address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) address &= 0xFFFFFC; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 542aa26fa..b7b5a7dfb 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -927,7 +927,17 @@ void nv3_pfifo_cache1_pull(void) uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; - // Tell the CPU if we found a software method + + + // start by incrementing + uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_index) + 1; + + if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX + next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); + else + next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); + + // Tell the CPU if we found a software method //bit23 unset=software //bit23 set=hardware if (!(current_context & 0x800000)) @@ -940,14 +950,6 @@ void nv3_pfifo_cache1_pull(void) return; } - // start by incrementing - uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_index) + 1; - - if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX - next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); - else - next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); - // Is this needed? nv3->pfifo.cache1_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c index 59e3dcdbc..77e2d1a6a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ b/src/video/nv/nv3/subsystems/nv3_pramin.c @@ -508,7 +508,7 @@ void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t conte nv_log_verbose_only("Context:\n"); nv_log_verbose_only("DMA Channel %d (0-7 valid)\n", context.channel); - nv_log_verbose_only("Class ID: =0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); + nv_log_verbose_only("Class ID: 0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); nv_log_verbose_only("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); nv_log_verbose_only("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); #endif From 991511197a53051ba96f3f263d6f9843ca0ac076 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 16 Jul 2025 12:27:55 +0100 Subject: [PATCH 208/274] Make visualnv window not modal and also allow writing to multiple destination buffers --- src/include/86box/nv/vid_nv.h | 7 ++- src/include/86box/nv/vid_nv3.h | 9 ++++ src/qt/qt_gpudebug_visualnv.cpp | 19 +++++++- src/qt/qt_gpudebug_visualnv.hpp | 1 + src/qt/qt_gpudebug_visualnv.ui | 48 ++++++++++++++++++- src/qt/qt_mainwindow.cpp | 24 +++++----- src/qt/qt_mainwindow.hpp | 8 ++++ .../classes/nv3_class_01c_image_in_memory.c | 4 +- src/video/nv/nv3/render/nv3_render_core.c | 47 ++++++++++-------- src/video/nv/nv3/subsystems/nv3_pfifo.c | 2 +- 10 files changed, 130 insertions(+), 39 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 8a8e2985c..655fb3950 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -130,8 +130,13 @@ typedef struct nv_base_s bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID - uint32_t last_buffer_address; // Last buffer address. bool agp_enabled; // AGP Enabled (for debugging) + + // + // DEBUG UI STUFF + // + bool debug_dba_enabled; // Debug DBA override + uint32_t debug_dba; // Debug DBA } nv_base_t; #define NV_REG_LIST_END 0xD15EA5E diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 7a3fb0e36..8e3bd87da 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1032,6 +1032,7 @@ typedef struct nv3_pfb_s #define NV3_NOTIFICATION_PAGE_FRAME_ADDRESS 12 // The pageframe to use + // Core notification structure typedef struct nv3_notification_s { @@ -1248,6 +1249,14 @@ typedef enum nv3_pgraph_bpixel_format_e bpixel_fmt_32bit = 3, } nv3_pgraph_bpixel_format; +typedef enum nv3_pgraph_destination_buffer_e +{ + pgraph_dest_buffer0 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED), + pgraph_dest_buffer1 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED), + pgraph_dest_buffer2 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED), + pgraph_dest_buffer3 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED), +} nv3_pgraph_destination_buffer; + // Graphics Subsystem typedef struct nv3_pgraph_s { diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp index dd56653e5..bbbd6c8ab 100644 --- a/src/qt/qt_gpudebug_visualnv.cpp +++ b/src/qt/qt_gpudebug_visualnv.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ VisualNVDialog::VisualNVDialog(QWidget *parent) { ui->setupUi(this); connect(ui->btnLoadSavestate, &QPushButton::clicked, this, &VisualNVDialog::on_btnLoadSavestate_clicked); + connect(ui->fbStartAddress, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_fbStartAddress_changed); } // VisualNV dialog destructor @@ -66,4 +68,19 @@ VisualNVDialog::~VisualNVDialog() void VisualNVDialog::on_btnLoadSavestate_clicked() { warning("THIS IS VisualNVDialog::on_btnLoadSavestate_clicked!!!! (throws into hole)"); -} \ No newline at end of file +} + +void VisualNVDialog::on_fbStartAddress_changed() +{ + if (nv3) + { + nv3->nvbase.debug_dba_enabled = true; + + bool ok = true; + + nv3->nvbase.debug_dba = ui->fbStartAddress->toPlainText().toInt(&ok); + + if (!ok) + nv3->nvbase.debug_dba_enabled = false; + } +} diff --git a/src/qt/qt_gpudebug_visualnv.hpp b/src/qt/qt_gpudebug_visualnv.hpp index db7d66dc6..effbff67b 100644 --- a/src/qt/qt_gpudebug_visualnv.hpp +++ b/src/qt/qt_gpudebug_visualnv.hpp @@ -34,6 +34,7 @@ class VisualNVDialog : public QDialog ~VisualNVDialog(); void on_btnLoadSavestate_clicked(); + void on_fbStartAddress_changed(); protected: private: diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui index 5bae3bb19..3c7b7d659 100644 --- a/src/qt/qt_gpudebug_visualnv.ui +++ b/src/qt/qt_gpudebug_visualnv.ui @@ -72,7 +72,7 @@ VRAM Control - + 160 @@ -157,6 +157,52 @@ Load nvplay savestate from real hardware + + + + 390 + 60 + 61 + 16 + + + + <html><head/><body><p>BPITCH[0]:</p></body></html> + + + + + + 460 + 60 + 104 + 21 + + + + + + + 460 + 90 + 104 + 21 + + + + + + + 390 + 90 + 61 + 16 + + + + <html><head/><body><p>BPITCH[1]:</p></body></html> + + diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 28200afac..fd8ccaa47 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -23,9 +23,9 @@ #include #include "qt_mainwindow.hpp" -#include "qt_gpudebug_vram.hpp" -#include "qt_gpudebug_visualnv.hpp" #include "ui_qt_mainwindow.h" +#include "ui_qt_gpudebug_vram.h" +#include "ui_qt_gpudebug_visualnv.h" #include "qt_specifydimensions.h" #include "qt_soundgain.hpp" @@ -2326,19 +2326,19 @@ void MainWindow::on_actionACPI_Shutdown_triggered() void MainWindow::on_actionDebug_GPUDebug_VRAM_triggered() { - GPUDebugVRAMDialog debugVramDialog(this); - debugVramDialog.setWindowFlag(Qt::CustomizeWindowHint, true); - debugVramDialog.setWindowFlag(Qt::WindowTitleHint, true); - debugVramDialog.setWindowFlag(Qt::WindowSystemMenuHint, false); - debugVramDialog.exec(); + debugVramDialog = new GPUDebugVRAMDialog; + debugVramDialog->setWindowFlag(Qt::CustomizeWindowHint, true); + debugVramDialog->setWindowFlag(Qt::WindowTitleHint, true); + debugVramDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); + debugVramDialog->show(); } void MainWindow::on_actionDebug_GPUDebug_VisualNv_triggered() { - VisualNVDialog visualNvDialog(this); - visualNvDialog.setWindowFlag(Qt::CustomizeWindowHint, true); - visualNvDialog.setWindowFlag(Qt::WindowTitleHint, true); - visualNvDialog.setWindowFlag(Qt::WindowSystemMenuHint, false); - visualNvDialog.exec(); + visualNvDialog = new VisualNVDialog; + visualNvDialog->setWindowFlag(Qt::CustomizeWindowHint, true); + visualNvDialog->setWindowFlag(Qt::WindowTitleHint, true); + visualNvDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); + visualNvDialog->show(); } \ No newline at end of file diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 5db957d45..8f7d4d53c 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -15,6 +15,10 @@ #include "qt_vmmanager_protocol.hpp" +// NON-modal dialogs +#include "qt_gpudebug_vram.hpp" +#include "qt_gpudebug_visualnv.hpp" + class MediaMenu; class RendererStack; @@ -170,6 +174,10 @@ private slots: private: Ui::MainWindow *ui; + + // NON-modal dialogs - these use ::show() and therefore have to be maintained as objects + GPUDebugVRAMDialog *debugVramDialog; + VisualNVDialog *visualNvDialog; std::unique_ptr status; std::shared_ptr mm; diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index a35135672..eb7f266d2 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -84,9 +84,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv3->pgraph.boffset[src_buffer_id] = param & 0x7FFFFF; else nv3->pgraph.boffset[src_buffer_id] = param & 0x3FFFFF; - - nv3->nvbase.last_buffer_address = nv3->pgraph.boffset[src_buffer_id]; - + nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); break; case NV3_NVCLASS_CRAP_START ... NV3_NVCLASS_CRAP_END: diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 9611d8349..88c91688b 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -351,25 +351,10 @@ uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj) return vram_32[vram_address]; } -/* Plots a pixel. */ -void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj) +void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj, uint32_t buffer) { - - // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. - // It seems, you are meant to - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; - uint32_t dst_buffer = 0; // 5 = just use the source buffer - - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; - - - uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; // maybe y16 too?z - int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; int32_t clip_end_y = nv3->pgraph.clip_start.y + nv3->pgraph.clip_size.y; @@ -388,7 +373,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t if (!nv3_render_chroma_test(color, grobj)) return; - uint32_t pixel_addr_vram = nv3_render_get_vram_address(position, grobj); + uint32_t pixel_addr_vram = nv3_render_get_vram_address_for_buffer(position, buffer); uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; uint8_t bit = 0x00; @@ -437,7 +422,7 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t We use the pixel format of the destination buffer to achieve this (thanks frostbite2000) */ - uint32_t destination_format = (nv3->pgraph.bpixel[dst_buffer]) & 0x03; + uint32_t destination_format = (nv3->pgraph.bpixel[buffer]) & 0x03; switch (destination_format) { @@ -496,6 +481,26 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t } } +/* Plots a pixel. */ +void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj) +{ + // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. + // It seems, you are meant to use the CRTC + + nv3_pgraph_destination_buffer dst_buffer = (nv3_pgraph_destination_buffer)grobj.grobj_0; + + if (dst_buffer & (pgraph_dest_buffer0)) + nv3_render_write_pixel_to_buffer(position, color, grobj, 0); + if (dst_buffer & (pgraph_dest_buffer1)) + nv3_render_write_pixel_to_buffer(position, color, grobj, 1); + if (dst_buffer & (pgraph_dest_buffer2)) + nv3_render_write_pixel_to_buffer(position, color, grobj, 2); + if (dst_buffer & (pgraph_dest_buffer3)) + nv3_render_write_pixel_to_buffer(position, color, grobj, 3); + + +} + /* Ensure the correct monitor size */ void nv3_render_ensure_screen_size(void) { @@ -606,12 +611,14 @@ void nv3_render_current_bpp_dfb_32(uint32_t address) void nv3_render_current_bpp() { /* Figure out the Display Buffer Address from the CRTC */ - + uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; - //uint32_t dba = 1920000; + if (nv3->nvbase.debug_dba_enabled + && nv3->nvbase.debug_dba > 0) + dba = nv3->nvbase.debug_dba; nv3_coord_16_t screen_size = {0}; screen_size.x = nv3->nvbase.svga.hdisp; diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index b7b5a7dfb..617bda649 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -819,7 +819,7 @@ void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) // 0x0 is used for creating the object. if (method_offset > 0 && method_offset < 0x100) { - // Reserved NVIDIA Objects + // Reserved nvidia methods oh_shit = true; oh_shit_reason = nv3_runout_reason_reserved_access; new_address |= (nv3_runout_reason_reserved_access << NV3_PFIFO_RUNOUT_RAMIN_ERR); From 5fc903fb48e138d38838a43786643255bfbabac8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 17 Jul 2025 19:16:39 +0100 Subject: [PATCH 209/274] Amazingly, if I make a 32-bit write to SVGA, it probably shouldn't write to four different CRTC registers..... --- src/video/nv/nv3/nv3_core.c | 16 ++++++++++++---- src/video/nv/nv3/render/nv3_render_core.c | 5 +---- src/video/nv/nv3/subsystems/nv3_pfifo.c | 3 ++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 146069e7e..fbb248de7 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -197,7 +197,9 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_write(real_address, val & 0xFF, nv3); - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); + + if (val > 0xFF) + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); return; } @@ -225,9 +227,15 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_write(real_address, val & 0xFF, nv3); - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); - nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); - nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); + + if (val > 0xFF) + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); + + if (val > 0xFFFF) + nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); + + if (val > 0xFFFFFF) + nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); return; } diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 88c91688b..ac8789226 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -454,8 +454,7 @@ void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, n return; } - // convert to 16bpp - // forcing it to render in 15bpp fixes it, + // convert to 15bpp or 16bpp based on if we are in 16bpp mode rop_dst = vram_16[pixel_addr_vram]; @@ -497,8 +496,6 @@ void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t nv3_render_write_pixel_to_buffer(position, color, grobj, 2); if (dst_buffer & (pgraph_dest_buffer3)) nv3_render_write_pixel_to_buffer(position, color, grobj, 3); - - } /* Ensure the correct monitor size */ diff --git a/src/video/nv/nv3/subsystems/nv3_pfifo.c b/src/video/nv/nv3/subsystems/nv3_pfifo.c index 617bda649..9ddcda0d3 100644 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ b/src/video/nv/nv3/subsystems/nv3_pfifo.c @@ -742,7 +742,7 @@ void nv3_pfifo_cache0_pull(void) // Tell the CPU if we found a software method and turn off cache pulling if (!(current_context & 0x800000)) { - nv_log("The object in CACHE0 is a software object\n"); + nv_log_verbose_only("The object in CACHE0 is a software object\n"); nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; @@ -923,6 +923,7 @@ void nv3_pfifo_cache1_pull(void) return; // interrupt was fired, and we went to ramro } + // should this be obtained from the grobj? Test on real nv3 h/w after drawrect.nvp works uint32_t current_context = nv3->pfifo.cache1_settings.context[current_subchannel]; // get the current subchannel uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; From 97c9888b9c01cf60273ad178cdca2649d856376b Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 27 Jul 2025 15:44:44 +0100 Subject: [PATCH 210/274] Load VRAM savestates.... --- .../86box/nv/classes/vid_nv3_classes.h | 11 ++- src/qt/qt_gpudebug_visualnv.cpp | 93 ++++++++++++++++++- src/qt/qt_gpudebug_visualnv.hpp | 2 + src/qt/qt_gpudebug_visualnv.ui | 6 +- src/qt/qt_mainwindow.cpp | 15 ++- src/qt/qt_mainwindow.ui | 2 +- src/video/nv/nv3/render/nv3_render_blit.c | 67 +++++++++++-- src/video/nv/nv3/render/nv3_render_core.c | 24 ++++- 8 files changed, 197 insertions(+), 23 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 85979dd45..15f4d37f1 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -239,9 +239,14 @@ typedef struct nv3_color_expanded_s uint8_t a; /* WARNING: The internal format is 10-bit RGB! */ - uint16_t r : 10; - uint16_t g : 10; - uint16_t b : 10; + uint16_t r; + uint16_t g; + uint16_t b; + + // YUV stuff + float y; + float u; + float v; // Indexed colour union diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp index bbbd6c8ab..3b2ba8472 100644 --- a/src/qt/qt_gpudebug_visualnv.cpp +++ b/src/qt/qt_gpudebug_visualnv.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "ui_qt_gpudebug_visualnv.h" @@ -55,8 +56,11 @@ VisualNVDialog::VisualNVDialog(QWidget *parent) , ui(new Ui::VisualNVDialog) { ui->setupUi(this); + connect(ui->btnLoadSavestate, &QPushButton::clicked, this, &VisualNVDialog::on_btnLoadSavestate_clicked); connect(ui->fbStartAddress, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_fbStartAddress_changed); + connect(ui->bPitch0Value, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_bPitch0Value_changed); + connect(ui->bPitch1Value, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_bPitch1Value_changed); } // VisualNV dialog destructor @@ -67,7 +71,62 @@ VisualNVDialog::~VisualNVDialog() void VisualNVDialog::on_btnLoadSavestate_clicked() { - warning("THIS IS VisualNVDialog::on_btnLoadSavestate_clicked!!!! (throws into hole)"); + if (!nv3) + return; + + QString bar0_file_name = QFileDialog::getOpenFileName + ( + this, + tr("Please provide NVPlay 0.3.0.7+ NV3BAR0.BIN file"), + ".", + tr("NVPlay MMIO Dump Files (*.bin)") + ); + + QString bar1_file_name = QFileDialog::getOpenFileName + ( + this, + tr("Please provide NVPlay 0.3.0.7+ NV3BAR1.BIN file"), + ".", + tr("NVPlay VRAM/RAMIN Dump Files (*.bin)") + ); + + // + // Open both dump files + // + + QFile bar0(bar0_file_name); + QFile bar1(bar1_file_name); + + if (!bar0.open(QIODevice::ReadOnly)) + { + warning("Failed to open NV3BAR0.bin!"); + return; + } + + if (!bar1.open(QIODevice::ReadOnly)) + { + warning("Failed to open NV3BAR1.bin!"); + return; + } + + if (bar0.size() != NV3_MMIO_SIZE + || bar1.size() != NV3_MMIO_SIZE) + { + warning("NV3BAR0.bin and NV3BAR1.bin must be 16MB!"); + bar0.close(); + bar1.close(); + return; + } + + // Load VRAM contents only for now. Todo: MMIO+RAMIN + QString oldTitle = this->windowTitle(); + + this->setWindowTitle(tr("RIVA 128 Realtime Debugger: Savestate Loading...")); + + bar1.read((char*)nv3->nvbase.svga.vram, nv3->nvbase.vram_amount); + + this->setWindowTitle(oldTitle); + } void VisualNVDialog::on_fbStartAddress_changed() @@ -84,3 +143,35 @@ void VisualNVDialog::on_fbStartAddress_changed() nv3->nvbase.debug_dba_enabled = false; } } + +void VisualNVDialog::on_bPitch0Value_changed() +{ + if (nv3) + { + bool ok = true; + + uint32_t old_bpitch = nv3->pgraph.bpitch[0]; + + nv3->pgraph.bpitch[0] = ui->bPitch0Value->toPlainText().toInt(&ok); + + if (!ok) + nv3->pgraph.bpitch[0] = old_bpitch; + } + +} + +void VisualNVDialog::on_bPitch1Value_changed() +{ + if (nv3) + { + bool ok = true; + + uint32_t old_bpitch = nv3->pgraph.bpitch[1]; + + nv3->pgraph.bpitch[1] = ui->bPitch0Value->toPlainText().toInt(&ok); + + if (!ok) + nv3->pgraph.bpitch[1] = old_bpitch; + } + +} \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.hpp b/src/qt/qt_gpudebug_visualnv.hpp index effbff67b..ee87dc0b3 100644 --- a/src/qt/qt_gpudebug_visualnv.hpp +++ b/src/qt/qt_gpudebug_visualnv.hpp @@ -35,6 +35,8 @@ class VisualNVDialog : public QDialog void on_btnLoadSavestate_clicked(); void on_fbStartAddress_changed(); + void on_bPitch0Value_changed(); + void on_bPitch1Value_changed(); protected: private: diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui index 3c7b7d659..ce45c4e44 100644 --- a/src/qt/qt_gpudebug_visualnv.ui +++ b/src/qt/qt_gpudebug_visualnv.ui @@ -29,7 +29,7 @@ - Nvidia GPU Realtime Debugger + RIVA 128 Realtime Debugger @@ -170,7 +170,7 @@ <html><head/><body><p>BPITCH[0]:</p></body></html> - + 460 @@ -180,7 +180,7 @@ - + 460 diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index e5f52d0a7..11f6f2519 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -2328,19 +2328,26 @@ void MainWindow::on_actionACPI_Shutdown_triggered() void MainWindow::on_actionDebug_GPUDebug_VRAM_triggered() { - debugVramDialog = new GPUDebugVRAMDialog; + debugVramDialog = new GPUDebugVRAMDialog(this); debugVramDialog->setWindowFlag(Qt::CustomizeWindowHint, true); debugVramDialog->setWindowFlag(Qt::WindowTitleHint, true); debugVramDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); - debugVramDialog->show(); + // If I have this as a NON-MODAL dialog, input is just eaten without doing anything + // WTF?!?!?!?!? + //debugVramDialog->show(); + debugVramDialog->exec(); + } void MainWindow::on_actionDebug_GPUDebug_VisualNv_triggered() { - visualNvDialog = new VisualNVDialog; + visualNvDialog = new VisualNVDialog(this); visualNvDialog->setWindowFlag(Qt::CustomizeWindowHint, true); visualNvDialog->setWindowFlag(Qt::WindowTitleHint, true); visualNvDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); - visualNvDialog->show(); + // If I have this as a NON-MODAL dialog, input is just eaten without doing anything + // WTF?!?!?!?!? + //visualNvDialog->show(); + visualNvDialog->exec(); } \ No newline at end of file diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 89db6f495..072a5a6a6 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -890,7 +890,7 @@ - GPU Realtime Debugger (NVIDIA ONLY) + RIVA 128 Realtime Debugger diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index c1b221648..0896bd199 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -119,20 +119,14 @@ This is LUDICROUSLY INEFFICIENT (2*O(n^2)) and COMPLETELY TERRIBLE code, but it' */ uint32_t nv3_s2sb_line_buffer[NV3_MAX_HORIZONTAL_SIZE*NV3_MAX_VERTICAL_SIZE] = {0}; -void nv3_render_blit_screen2screen(nv3_grobj_t grobj) +void nv3_render_blit_screen2screen_for_buffer(nv3_grobj_t grobj, uint32_t dst_buffer) { - if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE +if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE && nv3->pgraph.blit.size.y < NV3_MAX_VERTICAL_SIZE) memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.y) * (sizeof(uint32_t) * nv3->pgraph.blit.size.x)); /* First calculate our source and destination buffer */ uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; - uint32_t dst_buffer = 0; // 5 = just use the source buffer - - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED) & 0x01) dst_buffer = 0; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED) & 0x01) dst_buffer = 1; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED) & 0x01) dst_buffer = 2; - if ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED) & 0x01) dst_buffer = 3; nv3_coord_16_t in_position = nv3->pgraph.blit.point_in; nv3_coord_16_t out_position = nv3->pgraph.blit.point_out; @@ -175,4 +169,61 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj) memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); out_position.y++; } + + /* + //32bit only as a test + uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; + + if (nv3->pgraph.boffset[src_buffer] != nv3->pgraph.boffset[dst_buffer]) + { + // stretch out the position to the new one + + nv3_coord_16_t current_pos_in; + nv3_coord_16_t current_pos_out; + + current_pos_in.x = nv3->pgraph.blit.point_in.x; + current_pos_in.y = nv3->pgraph.blit.point_in.y; + current_pos_out.x = nv3->pgraph.blit.point_out.x; + current_pos_out.y = nv3->pgraph.blit.point_out.y; + + for (uint32_t y = 0; y < nv3->pgraph.blit.size.y; y++) + { + current_pos_in.y = nv3->pgraph.blit.point_in.y + y; + current_pos_out.y = nv3->pgraph.blit.point_out.y + y; + + for (uint32_t x = 0; x < nv3->pgraph.blit.size.x; x++) + { + current_pos_in.x = nv3->pgraph.blit.point_in.x + x; + current_pos_out.x = nv3->pgraph.blit.point_out.x + x; + + uint32_t index = nv3_render_get_vram_address_for_buffer(current_pos_in, dst_buffer) >> 2; + uint32_t index_dst = nv3_render_get_vram_address_for_buffer(current_pos_out, src_buffer) >> 2; + + vram_32[index_dst] = vram_32[index]; + + //nv3_render_write_pixel(current_pos, vram_32[index], grobj); + } + + current_pos_in.x = nv3->pgraph.blit.point_in.x; + current_pos_out.x = nv3->pgraph.blit.point_out.x; + + } + } + */ +} + +void nv3_render_blit_screen2screen(nv3_grobj_t grobj) +{ + uint32_t dst_buffer = (nv3_pgraph_destination_buffer)grobj.grobj_0; // 5 = just use the source buffer + + if (dst_buffer & pgraph_dest_buffer0) + nv3_render_blit_screen2screen_for_buffer(grobj, 0); + if (dst_buffer & pgraph_dest_buffer1) + nv3_render_blit_screen2screen_for_buffer(grobj, 1); + if (dst_buffer & pgraph_dest_buffer2) + nv3_render_blit_screen2screen_for_buffer(grobj, 2); + if (dst_buffer & pgraph_dest_buffer3) + nv3_render_blit_screen2screen_for_buffer(grobj, 3); + + } \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index ac8789226..862d83371 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -100,6 +100,9 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) // yuv color_final.r = color_final.g = color_final.b = (color & 0xFFFF) * 4; // convert to rgb10 break; + case nv3_pgraph_pixel_format_y420: + warning("nv3_render_expand_color: YUV420 not implemented\n"); + break; default: warning("nv3_render_expand_color unknown format %d", format); break; @@ -148,8 +151,11 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co packed_color = nv3_render_get_palette_index((color.r >> 2) & 0xFF); break; case nv3_pgraph_pixel_format_y16: - warning("nv3_render_downconvert: Y16 not implemented"); + warning("nv3_render_downconvert_color: Y16 not implemented"); break; + case nv3_pgraph_pixel_format_y420: + warning("nv3_render_downconvert_color: YUV420 not implemented\n"); + break; default: warning("nv3_render_downconvert_color unknown format %d", format); break; @@ -668,6 +674,9 @@ void nv3_render_8bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { for (uint32_t x = 0; x < screen_size.x; x++) { + if (vram_current_position >= nv3->nvbase.vram_amount) + return; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; @@ -698,6 +707,9 @@ void nv3_render_15bpp(uint32_t vram_start, nv3_coord_16_t screen_size) { for (uint32_t x = 0; x < screen_size.x; x++) { + if (vram_current_position >= nv3->nvbase.vram_amount) + return; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; @@ -728,7 +740,10 @@ void nv3_render_16bpp(uint32_t vram_start, nv3_coord_16_t screen_size) for (uint32_t y = 0; y < screen_size.y; y++) { for (uint32_t x = 0; x < screen_size.x; x++) - { + { + if (vram_current_position >= nv3->nvbase.vram_amount) + return; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; @@ -760,7 +775,10 @@ void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size) for (uint32_t y = 0; y < screen_size.y; y++) { for (uint32_t x = 0; x < screen_size.x; x++) - { + { + if (vram_current_position >= nv3->nvbase.vram_amount) + return; + p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; From d470c9067913e6791ca7a128f7fb2de47675acf1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 12 Aug 2025 20:57:36 +0100 Subject: [PATCH 211/274] Partially fix S2SB. Makes Windows 95 behave a lot better! --- src/include/86box/nv/render/vid_nv3_render.h | 3 + src/include/86box/nv/vid_nv3.h | 80 ++++++++---- src/include/86box/utils/video_stdlib.h | 3 + .../classes/nv3_class_01c_image_in_memory.c | 2 +- src/video/nv/nv3/nv3_core_arbiter.c | 16 +-- src/video/nv/nv3/render/nv3_render_blit.c | 4 +- src/video/nv/nv3/render/nv3_render_core.c | 116 ++++++++++++++++-- src/video/nv/nv3/subsystems/nv3_pgraph.c | 8 +- 8 files changed, 186 insertions(+), 46 deletions(-) diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index 9c7af77ab..e0b28edd0 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -39,6 +39,9 @@ uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color); // Convert a colour from the current working format to RGB10 format. +/* ROP */ +uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop); + /* Pattern */ void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 8e3bd87da..48170bfcd 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -607,24 +607,58 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 //todo: add what this does #define NV3_PGRAPH_INTR_EN_1 0x400144 // Interrupt Control for PGRAPH #2 (it can receive two at onc) -#define NV3_PGRAPH_CONTEXT_SWITCH 0x400180 // Holds the current PGRAPH context, switched by context switching +#define NV3_PGRAPH_CTX_SWITCH 0x400180 // Holds the current PGRAPH context, switched by context switching /* Contextual information for pgraph */ -#define NV3_PGRAPH_CONTEXT_SWITCH_COLOR_FORMAT 2 // Holds the current color format used for drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_ALPHA 3 // Holds a boolean if alpha transparency is currently enabled in drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_MONO_FORMAT 8 // Holds the current color format used for monochome drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_DAC_BYPASS 9 // Holds if PRAMDAC should be bypassed, and an external DAC drawn. -#define NV3_PGRAPH_CONTEXT_SWITCH_Z_WRITE 12 // Holds if we should write back to the zbuffer. -#define NV3_PGRAPH_CONTEXT_SWITCH_CHROMA_KEY 13 // Holds the current chroma mask used for drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_PLANE_MASK 14 // Holds the current plane mask used for drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_USER_CLIP 15 // Holds the user-specified clipping information used for drawing operations. -#define NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER 16 // Holds the buffer ID used for drawing operation (i.e. which bpixel/bpitch/boffset index to use) -#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED 20 // Holds a boolean indicating if buffer 0 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED 21 // Holds a boolean indicating if buffer 1 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED 22 // Holds a boolean indicating if buffer 2 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED 23 // Holds a boolean indicating if buffer 3 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CONTEXT_SWITCH_PATCH_CONFIG 24 // Something to do with an operation to do during a patchcord? -#define NV3_PGRAPH_CONTEXT_SWITCH_VOLATILE 31 // HUH +#define NV3_PGRAPH_CTX_SWITCH_COLOR_FORMAT 0 // Holds the current color format used for drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_ALPHA 3 // Holds a boolean indicating in if alpha transparency is currently enabled in drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_MONO_FORMAT 8 // Holds the current color format used for monochome drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_DAC_BYPASS 9 // Holds if PRAMDAC should be bypassed, and an external DAC drawn. +#define NV3_PGRAPH_CTX_SWITCH_Z_WRITE 12 // Holds if we should write back to the zbuffer. +#define NV3_PGRAPH_CTX_SWITCH_CHROMA_KEY 13 // Holds the current chroma mask used for drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_PLANE_MASK 14 // Holds the current plane mask used for drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_USER_CLIP 15 // Holds the user-specified clipping information used for drawing operations. +#define NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER 16 // Holds the buffer ID used for drawing operation (i.e. which bpixel/bpitch/boffset index to use) +#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER0_ENABLED 20 // Holds a boolean indicating if buffer 0 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER1_ENABLED 21 // Holds a boolean indicating if buffer 1 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER2_ENABLED 22 // Holds a boolean indicating if buffer 2 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER3_ENABLED 23 // Holds a boolean indicating if buffer 3 can be used as the destination for a drawing operation. +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG 24 // ROP type + +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0 0x0 // Reserved +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_DST_SRC 0x1 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_DST 0x2 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_SRC 0x3 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_DST 0x4 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_SRC 0x5 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_DST 0x6 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_SRC0 0x7 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_SRC1 0x8 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_PAT 0x9 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_SRC 0xA +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_PAT 0xB +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_SRC 0xC +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_PAT 0xD +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_PAT_SRC 0xE +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD1 0xF +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST 0x10 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_DST_SRC 0x11 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_DST 0x12 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_PAT 0x13 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_PAT_SRC 0x14 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_PAT 0x15 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD2 0x16 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS 0x17 // Ignore +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD0 0x18 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_SRC_DST 0x19 +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_DST_SRC 0x1A +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD1 0x1B +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD2 0x1C +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_SRC 0x1D +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD3 0x1E +#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD4 0x1F + +#define NV3_PGRAPH_CTX_SWITCH_VOLATILE 31 // HUH #define NV3_PGRAPH_CONTEXT_CONTROL 0x400190 // DMA context control #define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename @@ -1166,10 +1200,10 @@ typedef struct nv3_pramdac_s } nv3_pramdac_t; /* Holds DMA channel context information */ -typedef struct nv3_pgraph_context_switch_s +typedef struct NV3_PGRAPH_CTX_SWITCH_s { /* TODO */ -} nv3_pgraph_context_switch_t; +} NV3_PGRAPH_CTX_SWITCH_t; typedef struct nv3_pgraph_context_control_s { @@ -1251,10 +1285,10 @@ typedef enum nv3_pgraph_bpixel_format_e typedef enum nv3_pgraph_destination_buffer_e { - pgraph_dest_buffer0 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER0_ENABLED), - pgraph_dest_buffer1 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER1_ENABLED), - pgraph_dest_buffer2 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER2_ENABLED), - pgraph_dest_buffer3 = (1 << NV3_PGRAPH_CONTEXT_SWITCH_DST_BUFFER3_ENABLED), + pgraph_dest_buffer0 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER0_ENABLED), + pgraph_dest_buffer1 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER1_ENABLED), + pgraph_dest_buffer2 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER2_ENABLED), + pgraph_dest_buffer3 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER3_ENABLED), } nv3_pgraph_destination_buffer; // Graphics Subsystem @@ -1273,7 +1307,7 @@ typedef struct nv3_pgraph_s uint32_t context_switch; // TODO: Make this a struct, it's just going to be enormous lol. nv3_pgraph_context_control_t context_control; - nv3_pgraph_context_switch_t context_user_submit; + NV3_PGRAPH_CTX_SWITCH_t context_user_submit; nv3_pgraph_context_user_t context_user; uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache (nv3_pgraph_context_user_t array?) diff --git a/src/include/86box/utils/video_stdlib.h b/src/include/86box/utils/video_stdlib.h index 4434ef43f..0d4b148d6 100644 --- a/src/include/86box/utils/video_stdlib.h +++ b/src/include/86box/utils/video_stdlib.h @@ -17,4 +17,7 @@ /* ROP */ + +#define VIDEO_ROP_SRC_COPY 0xCC + int32_t video_rop_gdi_ternary(int32_t rop, int32_t src, int32_t dst, int32_t pattern); \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c index eb7f266d2..12ac3fe6b 100644 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c @@ -31,7 +31,7 @@ void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { /* We need this for a lot of methods, so may as well store it here. */ - uint32_t src_buffer_id = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + uint32_t src_buffer_id = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; switch (method_id) diff --git a/src/video/nv/nv3/nv3_core_arbiter.c b/src/video/nv/nv3/nv3_core_arbiter.c index e23386cac..3399ca9fe 100644 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ b/src/video/nv/nv3/nv3_core_arbiter.c @@ -108,11 +108,11 @@ uint32_t nv3_mmio_arbitrate_read(uint32_t address) else { //nvplay stuff - #ifdef ENABLE_NV_LOG_ULTRA - warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); - #else + //#ifdef ENABLE_NV_LOG_ULTRA + //warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); + //#else nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); - #endif + //#endif // The real hardware returns a garbage pattern return 0x00; @@ -181,11 +181,11 @@ void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) else { //nvplay stuff - #ifdef ENABLE_NV_LOG_ULTRA - warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); - #else + //#ifdef ENABLE_NV_LOG_ULTRA + //warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); + //#else nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); - #endif + //#endif return; } diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c index 0896bd199..92b657cd0 100644 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ b/src/video/nv/nv3/render/nv3_render_blit.c @@ -121,12 +121,12 @@ uint32_t nv3_s2sb_line_buffer[NV3_MAX_HORIZONTAL_SIZE*NV3_MAX_VERTICAL_SIZE] = { void nv3_render_blit_screen2screen_for_buffer(nv3_grobj_t grobj, uint32_t dst_buffer) { -if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE + if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE && nv3->pgraph.blit.size.y < NV3_MAX_VERTICAL_SIZE) memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.y) * (sizeof(uint32_t) * nv3->pgraph.blit.size.x)); /* First calculate our source and destination buffer */ - uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; nv3_coord_16_t in_position = nv3->pgraph.blit.point_in; nv3_coord_16_t out_position = nv3->pgraph.blit.point_out; diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index 862d83371..db7bdb06d 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -44,7 +44,7 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) // grobj0 = seems to share the format of PGRAPH_CONTEXT_SWITCH register. uint8_t format = (grobj.grobj_0 & 0x07); - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; nv3_color_expanded_t color_final; // set the pixel format @@ -119,7 +119,7 @@ nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color) { uint8_t format = (grobj.grobj_0 & 0x07); - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; nv_log_verbose_only("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); @@ -168,7 +168,7 @@ uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t co /* Runs the chroma key/color key test */ bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj) { - bool chroma_enabled = ((grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_CHROMA_KEY) & 0x01); + bool chroma_enabled = ((grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_CHROMA_KEY) & 0x01); if (!chroma_enabled) return true; @@ -239,7 +239,7 @@ uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj) { uint32_t vram_x = position.x; uint32_t vram_y = position.y; - uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_SRC_BUFFER) & 0x03; + uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; @@ -359,7 +359,7 @@ uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj) void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj, uint32_t buffer) { - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CONTEXT_SWITCH_ALPHA) & 0x01; + bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; int32_t clip_end_y = nv3->pgraph.clip_start.y + nv3->pgraph.clip_size.y; @@ -428,6 +428,9 @@ void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, n We use the pixel format of the destination buffer to achieve this (thanks frostbite2000) */ + // translate the patch config to GDI rop + uint32_t final_rop = nv3_render_translate_nvrop(grobj, nv3->pgraph.rop); + uint32_t destination_format = (nv3->pgraph.bpixel[buffer]) & 0x03; switch (destination_format) @@ -435,7 +438,7 @@ void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, n case bpixel_fmt_8bit: rop_src = color & 0xFF; rop_dst = nv3->nvbase.svga.vram[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern) & 0xFF; + nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern) & 0xFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; @@ -464,7 +467,7 @@ void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, n rop_dst = vram_16[pixel_addr_vram]; - vram_16[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern) & 0xFFFF; + vram_16[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern) & 0xFFFF; nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; @@ -477,7 +480,7 @@ void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, n rop_src = color; rop_dst = vram_32[pixel_addr_vram]; - vram_32[pixel_addr_vram] = video_rop_gdi_ternary(nv3->pgraph.rop, rop_src, rop_dst, rop_pattern); + vram_32[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern); nv3->nvbase.svga.changedvram[pixel_addr_vram >> 10] = changeframecount; @@ -789,3 +792,100 @@ void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size) } } } + +// Translate an "NV-ROP" into a GDI Ternary ROP +uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop) +{ + // Credit to envytools for this function: + // https://github.com/envytools/envytools/blob/f102b82381f3f11cee113d16374c87091db039d9/nvhw/pgraph.c + // How does one even go about reverse engineering this (I'm sure the behaviour is simpler when you don't have to translate this but...) Marcelina is a legend. + + uint32_t patch_config_rop = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F; + + /* || patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0*/ + + // TODO: Blending + if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS) // 0x00 is used for "nothing here" it seems. + return VIDEO_ROP_SRC_COPY; + + uint8_t res = 0; + + int32_t swizzle[3]; + + if (patch_config_rop < 8) { + swizzle[0] = patch_config_rop >> 0 & 1; + swizzle[1] = patch_config_rop >> 1 & 1; + swizzle[2] = patch_config_rop >> 2 & 1; + } else if (patch_config_rop < 0x10) { + swizzle[0] = (patch_config_rop >> 0 & 1) + 1; + swizzle[1] = (patch_config_rop >> 1 & 1) + 1; + swizzle[2] = (patch_config_rop >> 2 & 1) + 1; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST) { + swizzle[0] = 0, swizzle[1] = 1, swizzle[2] = 2; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_DST_SRC) { + swizzle[0] = 1, swizzle[1] = 0, swizzle[2] = 2; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_DST) { + swizzle[0] = 0, swizzle[1] = 2, swizzle[2] = 1; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_PAT) { + swizzle[0] = 2, swizzle[1] = 0, swizzle[2] = 1; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_PAT_SRC) { + swizzle[0] = 1, swizzle[1] = 2, swizzle[2] = 0; + } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_PAT) { + swizzle[0] = 2, swizzle[1] = 1, swizzle[2] = 0; + } else { + warning("NV3 ROP: Invalid patch configuration %02x!", rop); + } + if (patch_config_rop == 0) { + if (rop & 0x01) + res |= 0x11; + if (rop & 0x16) + res |= 0x44; + if (rop & 0x68) + res |= 0x22; + if (rop & 0x80) + res |= 0x88; + } else if (patch_config_rop == 0xf) { + if (rop & 0x01) + res |= 0x03; + if (rop & 0x16) + res |= 0x0c; + if (rop & 0x68) + res |= 0x30; + if (rop & 0x80) + res |= 0xc0; + } else { + int32_t i; + for (i = 0; i < 8; i++) { + int32_t s0 = i >> swizzle[0] & 1; + int32_t s1 = i >> swizzle[1] & 1; + int32_t s2 = i >> swizzle[2] & 1; + int32_t s = s2 << 2 | s1 << 1 | s0; + if (rop >> s & 1) + res |= 1 << i; + } + } + return res; + + /* + uint32_t patch_config = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F; + + // Wtf do these even do? + switch (patch_config) + { + // don't do anything + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD1: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD2: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD0: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD1: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD2: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD3: + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD4: + return src; + case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST: // S2SB + 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 index dbca46216..3ad9fa2a1 100644 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ b/src/video/nv/nv3/subsystems/nv3_pgraph.c @@ -51,7 +51,7 @@ nv_register_t pgraph_registers[] = { { 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 }, - { NV3_PGRAPH_CONTEXT_SWITCH, "PGRAPH DMA Context Switch", NULL, NULL }, + { NV3_PGRAPH_CTX_SWITCH, "PGRAPH DMA Context Switch", NULL, NULL }, { NV3_PGRAPH_CONTEXT_CONTROL, "PGRAPH DMA Context Control", NULL, NULL }, { NV3_PGRAPH_CONTEXT_USER, "PGRAPH DMA Context User", NULL, NULL }, //{ NV3_PGRAPH_CONTEXT_CACHE(0), "PGRAPH DMA Context Cache", NULL, NULL }, @@ -156,7 +156,7 @@ uint32_t nv3_pgraph_read(uint32_t address) // In the future, these will most likely have their own functions... // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) - case NV3_PGRAPH_CONTEXT_SWITCH: + case NV3_PGRAPH_CTX_SWITCH: ret = nv3->pgraph.context_switch; break; case NV3_PGRAPH_CONTEXT_CONTROL: @@ -359,7 +359,7 @@ void nv3_pgraph_write(uint32_t address, uint32_t value) // In the future, these will most likely have their own functions... // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) - case NV3_PGRAPH_CONTEXT_SWITCH: + case NV3_PGRAPH_CTX_SWITCH: nv3->pgraph.context_switch = value; break; case NV3_PGRAPH_CONTEXT_CONTROL: @@ -625,7 +625,7 @@ void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t switch (method) { default: - // Object Method orchestration + // Object Method arbitration nv3_pgraph_arbitrate_method(param, method, channel, subchannel, class_id, context); break; } From 0e439cf0aeb32bd06c340adaf0a489e5fe1dac34 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 12 Aug 2025 22:07:19 +0100 Subject: [PATCH 212/274] Move notifications to DMA code --- CMakePresets.json | 3 +- src/include/86box/nv/vid_nv3.h | 2 +- .../nv/nv3/classes/nv3_class_shared_methods.c | 115 ------------- src/video/nv/nv3/nv3_core.c | 2 +- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 153 +++++++++++++++++- 5 files changed, 155 insertions(+), 120 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index b2ed64e97..30feab8ab 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -36,8 +36,7 @@ "name": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON", - "NV_LOG_ULTRA": "ON" + "NV_LOG": "ON" }, "inherits": "base" }, diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 48170bfcd..05281a5ac 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -75,7 +75,7 @@ extern const device_config_t nv3t_config[]; // Confi #define NV3T_VBIOS_ASUS_V170 "roms/video/nvidia/nv3/A170D03T.rom" // ASUS AGP-V3000 ZXTV BIOS - V1.70D.03 (C) 1996-98 Nvidia Corporation #define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation #define NV3T_VBIOS_REFERENCE_CEK_V172 "roms/video/nvidia/nv3/vgasgram.rom" // Reference(?) BIOS: RIVA 128 ZX BIOS - V1.72B (C) 1996-98 NVidia Corporation -# + // The default VBIOS to use #define NV3_VBIOS_DEFAULT NV3_VBIOS_ERAZOR_V15403 diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c index 80c635a21..89473d829 100644 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ b/src/video/nv/nv3/classes/nv3_class_shared_methods.c @@ -65,118 +65,3 @@ void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t return; } } - - -/* Sees if any notification is required after an object method is executed. If so, executes it... */ -void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - if (!nv3->pgraph.notify_pending) - return; - - uint32_t current_notification_object = nv3->pgraph.notifier; - uint32_t notification_type = ((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07); - - // check for a software method (0 = hardware, 1 = software) - if (notification_type != 0) - { - nv_log("Software Notification, firing interrupt"); - nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY); - //return; - } - - // set up the NvNotification structure - nv3_notification_t notify = {0}; - notify.nanoseconds = nv3->ptimer.time; - notify.status = NV3_NOTIFICATION_STATUS_DONE_OK; // it should be fine to just signal that it's ok - - // these are only nonzero when there is an error - notify.info32 = notify.info16 = 0; - - // notify object base=grobj_1 >> 12 - uint32_t notify_obj_base = grobj.grobj_1 >> 12; - - uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); - uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); - uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); - - /* extract some important information*/ - uint32_t info_adjust = notify_obj_info & 0xFFF; - bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; - uint8_t info_notification_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; - - /* paging information */ - bool page_is_present = notify_obj_page & 0x01; - bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); - uint32_t frame_base = notify_obj_page & 0xFFFFF000; - - // This code is temporary and will probably be moved somewhere else - // Print torns of debug info - #ifdef DEBUG - nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); - - nv_log_verbose_only("Notification Information:\n"); - nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); - (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); - - switch (info_notification_target) - { - case NV3_NOTIFICATION_TARGET_NVM: - nv_log_verbose_only("Notification Target: VRAM\n"); - break; - case NV3_NOTIFICATION_TARGET_CART: - nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); - break; - case NV3_NOTIFICATION_TARGET_PCI: - (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); - break; - case NV3_NOTIFICATION_TARGET_AGP: - (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); - break; - } - - nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); - (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); - (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); - nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); - #endif - - // set up the dma transfer. we need to translate to a physical address. - - uint32_t final_address = 0; - - /* Simple case: hardware notification, we can just take the pte since it's based on the type */ - if (notification_type == 0) - { - final_address = frame_base + info_adjust; - } - else - { - // for software we have to calculate the pte index - uint32_t pte_num = ((notification_type << 4) + info_adjust) >> 12; - - /* ramin entries are sorted - 1 object for each pte entry...*/ - final_address = nv3_ramin_read32(notify_obj_base + (0x10 * pte_num) + 8, nv3); - final_address += (info_adjust & 0xFFF); - } - - /* send the notification off */ - nv_log("About to send hardware notification to 0x%08x (Check target)\n", final_address); - - switch (info_notification_target) - { - case NV3_NOTIFICATION_TARGET_NVM: - svga_writel_linear(final_address, (notify.nanoseconds & 0xFFFFFFFF), &nv3->nvbase.svga); - svga_writel_linear(final_address + 4, (notify.nanoseconds >> 32), &nv3->nvbase.svga); - svga_writel_linear(final_address + 8, notify.info32, &nv3->nvbase.svga); - svga_writel_linear(final_address + 0x0C, (notify.info16 | notify.status), &nv3->nvbase.svga); - break; - case NV3_NOTIFICATION_TARGET_PCI: - case NV3_NOTIFICATION_TARGET_AGP: - dma_bm_write(final_address, (uint8_t*)¬ify, sizeof(nv3_notification_t), 4); - break; - } - - // we're done - nv3->pgraph.notify_pending = false; -} diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index fbb248de7..46b6cf9c3 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -315,7 +315,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) ret = (NV_PCI_DEVICE_NV3 & 0xFF); break; - case NV3_PCI_CFG_DEVICE_ID+1: + case NV3_PCI_CFG_DEVICE_ID + 1: ret = (NV_PCI_DEVICE_NV3 >> 8); break; diff --git a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c index da068e07f..49c844404 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -30,7 +30,158 @@ /* Nvidia DMA Engine */ -void nv3_dma_translate_address(void) +void nv3_perform_dma_m2mf(nv3_grobj_t grobj) +{ + switch (method_id) + { + /* mthdCreate in software(?)*/ + case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: + //nv_log("mthdCreate obj_name=0x%08x\n", param); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + break; + // set up the current notification request/object + // and check for double notifiers. + case NV3_SET_NOTIFY: + if (nv3->pgraph.notify_pending) + { + nv_log("Executed method NV3_SET_NOTIFY with nv3->pgraph.notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); + nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); + nv3_debug_ramin_print_context_info(param, context); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + + // disable + nv3->pgraph.notify_pending = false; + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); + /* may need to disable fifo in this state */ + return; + } + + // set a notify as pending. + nv3->pgraph.notifier = param; + nv3->pgraph.notify_pending = true; + break; + default: + nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); + nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + return; + } +} + + +/* Sees if any notification is required after an object method is executed. If so, executes it... */ +void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) { + if (!nv3->pgraph.notify_pending) + return; + + uint32_t current_notification_object = nv3->pgraph.notifier; + uint32_t notification_type = ((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07); + + // check for a software method (0 = hardware, 1 = software) + if (notification_type != 0) + { + nv_log("Software Notification, firing interrupt"); + nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY); + //return; + } + + // set up the NvNotification structure + nv3_notification_t notify = {0}; + notify.nanoseconds = nv3->ptimer.time; + notify.status = NV3_NOTIFICATION_STATUS_DONE_OK; // it should be fine to just signal that it's ok + // these are only nonzero when there is an error + notify.info32 = notify.info16 = 0; + + // notify object base=grobj_1 >> 12 + uint32_t notify_obj_base = grobj.grobj_1 >> 12; + + uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); + uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); + uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); + + /* extract some important information*/ + uint32_t info_adjust = notify_obj_info & 0xFFF; + bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; + uint8_t info_notification_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; + + /* paging information */ + bool page_is_present = notify_obj_page & 0x01; + bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); + uint32_t frame_base = notify_obj_page & 0xFFFFF000; + + // This code is temporary and will probably be moved somewhere else + // Print torns of debug info + #ifdef DEBUG + nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); + + nv_log_verbose_only("Notification Information:\n"); + nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); + (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); + + switch (info_notification_target) + { + case NV3_NOTIFICATION_TARGET_NVM: + nv_log_verbose_only("Notification Target: VRAM\n"); + break; + case NV3_NOTIFICATION_TARGET_CART: + nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); + break; + case NV3_NOTIFICATION_TARGET_PCI: + (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); + break; + case NV3_NOTIFICATION_TARGET_AGP: + (nv3->nvbase.bus_generation == nv_bus_agp_1x + || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); + break; + } + + nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); + (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); + (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); + nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); + #endif + + // set up the dma transfer. we need to translate to a physical address. + + uint32_t final_address = 0; + + /* Simple case: hardware notification, we can just take the pte since it's based on the type */ + if (notification_type == 0) + { + final_address = frame_base + info_adjust; + } + else + { + // for software we have to calculate the pte index + uint32_t pte_num = ((notification_type << 4) + info_adjust) >> 12; + + /* ramin entries are sorted - 1 object for each pte entry...*/ + final_address = nv3_ramin_read32(notify_obj_base + (0x10 * pte_num) + 8, nv3); + final_address += (info_adjust & 0xFFF); + } + + /* send the notification off */ + nv_log("About to send hardware notification to 0x%08x (Check target)\n", final_address); + + switch (info_notification_target) + { + case NV3_NOTIFICATION_TARGET_NVM: + + uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; + + // increment by 1 because each index increments by 4 + vram_32[final_address] = (notify.nanoseconds & 0xFFFFFFFF); + vram_32[final_address + 1] = (notify.nanoseconds >> 32); + vram_32[final_address + 2] = notify.info32; + vram_32[final_address + 3] = (notify.info16 | notify.status); + break; + case NV3_NOTIFICATION_TARGET_PCI: + case NV3_NOTIFICATION_TARGET_AGP: + dma_bm_write(final_address, (uint8_t*)¬ify, sizeof(nv3_notification_t), 4); + break; + } + + // we're done + nv3->pgraph.notify_pending = false; } \ No newline at end of file From 12a56203fa40036ee011f21a64f43039e5bc90d6 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Tue, 12 Aug 2025 23:30:27 +0100 Subject: [PATCH 213/274] First attempt at M2MF. It sucks --- .../86box/nv/classes/vid_nv3_classes.h | 12 +- src/include/86box/nv/render/vid_nv3_render.h | 5 +- src/include/86box/nv/vid_nv3.h | 8 +- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 9 +- src/video/nv/nv3/render/nv3_render_core.c | 26 +-- src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 153 ++++++++++++++---- 6 files changed, 141 insertions(+), 72 deletions(-) diff --git a/src/include/86box/nv/classes/vid_nv3_classes.h b/src/include/86box/nv/classes/vid_nv3_classes.h index 15f4d37f1..5106e5bff 100644 --- a/src/include/86box/nv/classes/vid_nv3_classes.h +++ b/src/include/86box/nv/classes/vid_nv3_classes.h @@ -124,12 +124,8 @@ typedef enum nv3_pgraph_class_e #define NV3_M2MF_FORMAT 0x0324 // M2MF formats (IN and OUT ORed together) -#define NV3_M2MF_FORMAT_INPUT_INC_1 0x1 -#define NV3_M2MF_FORMAT_INPUT_INC_2 0x2 -#define NV3_M2MF_FORMAT_INPUT_INC_4 0x4 -#define NV3_M2MF_FORMAT_OUTPUT_INC_1 0x100 -#define NV3_M2MF_FORMAT_OUTPUT_INC_2 0x200 -#define NV3_M2MF_FORMAT_OUTPUT_INC_4 0x400 +#define NV3_M2MF_FORMAT_INPUT 0 +#define NV3_M2MF_FORMAT_OUTPUT 8 #define NV3_M2MF_NOTIFY 0x0328 @@ -596,8 +592,8 @@ typedef struct nv3_object_class_00D uint32_t offset_out; uint32_t pitch_in; uint32_t pitch_out; - uint32_t line_length_in; // Stride? - uint32_t line_count; + uint32_t scanline_length; // Stride? + uint32_t num_scanlines; uint8_t format; // input increment 1 2 or 4, output increment 1 2 or 4 (represented by << 8) uint32_t buffer_notify; // Notify the Buffedr } nv3_memory_to_memory_format_t; diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h index e0b28edd0..cca3761be 100644 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ b/src/include/86box/nv/render/vid_nv3_render.h @@ -58,4 +58,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj); /* GDI */ void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj); -void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj); /* GDI Type-E: Clipped 1bpp colour-expanded bitmap */ \ No newline at end of file +void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj); /* GDI Type-E: Clipped 1bpp colour-expanded bitmap */ + +/* DMA */ +void nv3_perform_dma_m2mf(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index 05281a5ac..b954355bb 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1054,10 +1054,10 @@ typedef struct nv3_pfb_s #define NV3_NOTIFICATION_INFO_ADJUST 0 // Wut #define NV3_NOTIFICATION_PT_PRESENT 16 // Determines if the pagetable exists. #define NV3_NOTIFICATION_TARGET 24 // Determines where this notification goes -#define NV3_NOTIFICATION_TARGET_NVM 0 // VRAM target for DMA -#define NV3_NOTIFICATION_TARGET_CART 1 // "Cartridge" target for dma, only mentioned in a few places, !!! NV2 LEFTOVER !!! -#define NV3_NOTIFICATION_TARGET_PCI 2 // Send the data to the host system over PCI -#define NV3_NOTIFICATION_TARGET_AGP 3 // Send the data to the host system over AGP +#define NV3_DMA_TARGET_NODE_VRAM 0 // VRAM target for DMA +#define NV3_DMA_TARGET_NODE_CART 1 // "Cartridge" target for dma, only mentioned in a few places, !!! NV2 LEFTOVER !!! +#define NV3_DMA_TARGET_NODE_PCI 2 // Send the data to the host system over PCI +#define NV3_DMA_TARGET_NODE_AGP 3 // Send the data to the host system over AGP #define NV3_NOTIFICATION_PAGE_IS_PRESENT 0 // Determines if the page really exists #define NV3_NOTIFICATION_PAGE_ACCESS 1 // Determines the page access type diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c index 4a6d2e39c..f07aab737 100644 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c @@ -49,16 +49,21 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_ nv_log("Method Execution: M2MF Pitch Out = 0x%08x", param); break; case NV3_M2MF_SCANLINE_LENGTH_IN_BYTES: - nv3->pgraph.m2mf.line_length_in = param; + nv3->pgraph.m2mf.scanline_length = param; nv_log("Method Execution: M2MF Scanline Length in Bytes = 0x%08x", param); break; case NV3_M2MF_NUM_SCANLINES: - nv3->pgraph.m2mf.line_count = param; + nv3->pgraph.m2mf.num_scanlines = param; nv_log("Method Execution: M2MF Num Scanlines = 0x%08x", param); break; case NV3_M2MF_FORMAT: nv3->pgraph.m2mf.format = param; nv_log("Method Execution: M2MF Format = 0x%08x", param); + + // Format Done - start m2mf + + nv3_perform_dma_m2mf(grobj); + break; case NV3_M2MF_NOTIFY: /* This is technically its own thing, but I don't know if it's ever a problem with how we've designed it */ diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c index db7bdb06d..c40fa84a9 100644 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ b/src/video/nv/nv3/render/nv3_render_core.c @@ -803,7 +803,7 @@ uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop) uint32_t patch_config_rop = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F; /* || patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0*/ - + // TODO: Blending if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS) // 0x00 is used for "nothing here" it seems. return VIDEO_ROP_SRC_COPY; @@ -864,28 +864,6 @@ uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop) res |= 1 << i; } } + return res; - - /* - uint32_t patch_config = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F; - - // Wtf do these even do? - switch (patch_config) - { - // don't do anything - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD1: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD2: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD0: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD1: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD2: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD3: - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD4: - return src; - case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST: // S2SB - break; - - } - */ } \ 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 index 49c844404..3359e9079 100644 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ b/src/video/nv/nv3/subsystems/nv3_pbus_dma.c @@ -21,6 +21,7 @@ #include #include <86box/86box.h> #include <86box/device.h> +#include <86box/dma.h> #include <86box/mem.h> #include <86box/pci.h> #include <86box/rom.h> // DEPENDENT!!! @@ -31,40 +32,126 @@ /* Nvidia DMA Engine */ void nv3_perform_dma_m2mf(nv3_grobj_t grobj) -{ - switch (method_id) +{ + // notify object base=grobj_1 >> 12 + uint32_t notify_obj_base = grobj.grobj_1 >> 12; + + uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); + uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); + uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); + + /* extract some important information*/ + uint32_t info_adjust = notify_obj_info & 0xFFF; + bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; + uint8_t info_dma_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; + + /* paging information */ + bool page_is_present = notify_obj_page & 0x01; + bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); + uint32_t frame_base = notify_obj_page & 0xFFFFF000; + + // This code is temporary and will probably be moved somewhere else + // Print torns of debug info + #ifdef DEBUG + nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); + + nv_log_verbose_only("M2MF DMA Information:\n"); + nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); + (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); + + switch (info_dma_target) { - /* mthdCreate in software(?)*/ - case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - //nv_log("mthdCreate obj_name=0x%08x\n", param); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); + case NV3_DMA_TARGET_NODE_VRAM: + nv_log_verbose_only("Notification Target: VRAM\n"); break; - // set up the current notification request/object - // and check for double notifiers. - case NV3_SET_NOTIFY: - if (nv3->pgraph.notify_pending) + case NV3_DMA_TARGET_NODE_CART: + nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); + break; + case NV3_DMA_TARGET_NODE_PCI: + (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); + break; + case NV3_DMA_TARGET_NODE_AGP: + (nv3->nvbase.bus_generation == nv_bus_agp_1x + || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); + break; + } + + nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); + (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); + (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); + nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); + #endif + + // set up the dma transfer. we need to translate to a physical address. + + uint32_t final_address = 0; + + /* M2MF DMA only uses HW type */ + + final_address = frame_base + info_adjust; + + /* send the notification off */ + nv_log("About to send M2MF DMA to 0x%08x (Check target)\n", final_address); + + uint32_t offset_in = (nv3->pgraph.m2mf.offset_in); + uint32_t offset_out = (nv3->pgraph.m2mf.offset_out); + + uint32_t pitch_in = nv3->pgraph.m2mf.pitch_in; + uint32_t pitch_out = nv3->pgraph.m2mf.pitch_out; + + // pitch out surely can't be 0 + if (pitch_out == 0) + pitch_out = pitch_in; + + uint32_t bytes_per_scanline = nv3->pgraph.m2mf.scanline_length; + + uint8_t increment_in = (nv3->pgraph.m2mf.format) & 0x07; + uint8_t increment_out = (nv3->pgraph.m2mf.format >> NV3_M2MF_FORMAT_INPUT) & 0x07; + + for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++) + { + for (uint32_t pixel = offset_in; pixel < (offset_in + bytes_per_scanline); pixel += increment_in) + { + nv3->nvbase.svga.vram[offset_out] = nv3->nvbase.svga.vram[offset_in]; + offset_out += increment_out; + } + + offset_in += pitch_in; + offset_out += pitch_out; + } + + /* + switch (info_dma_target) + { + // for M2MF only NVM target node is used. + + case NV3_DMA_TARGET_NODE_VRAM: + + + uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; + + break; + case NV3_DMA_TARGET_NODE_PCI: + case NV3_DMA_TARGET_NODE_AGP: + // Idk how to implement increments of more than 1 but only 1 increments seem to be used with these. + uint32_t size_in = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_in; + uint32_t size_out = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_out; + + uint8_t* page_in = calloc(1, size_in); + + for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++) { - nv_log("Executed method NV3_SET_NOTIFY with nv3->pgraph.notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); - nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); - nv3_debug_ramin_print_context_info(param, context); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - // disable - nv3->pgraph.notify_pending = false; - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - /* may need to disable fifo in this state */ - return; } - // set a notify as pending. - nv3->pgraph.notifier = param; - nv3->pgraph.notify_pending = true; + dma_bm_read(offset_in, page_in, size_in, size_in); + dma_bm_write(offset_out, page_in, size_out, size_out); + break; - default: - nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; } +*/ + // we're done + nv3->pgraph.notify_pending = false; } @@ -121,16 +208,16 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t switch (info_notification_target) { - case NV3_NOTIFICATION_TARGET_NVM: + case NV3_DMA_TARGET_NODE_VRAM: nv_log_verbose_only("Notification Target: VRAM\n"); break; - case NV3_NOTIFICATION_TARGET_CART: + case NV3_DMA_TARGET_NODE_CART: nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); break; - case NV3_NOTIFICATION_TARGET_PCI: + case NV3_DMA_TARGET_NODE_PCI: (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); break; - case NV3_NOTIFICATION_TARGET_AGP: + case NV3_DMA_TARGET_NODE_AGP: (nv3->nvbase.bus_generation == nv_bus_agp_1x || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); break; @@ -166,7 +253,7 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t switch (info_notification_target) { - case NV3_NOTIFICATION_TARGET_NVM: + case NV3_DMA_TARGET_NODE_VRAM: uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; @@ -176,8 +263,8 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t vram_32[final_address + 2] = notify.info32; vram_32[final_address + 3] = (notify.info16 | notify.status); break; - case NV3_NOTIFICATION_TARGET_PCI: - case NV3_NOTIFICATION_TARGET_AGP: + case NV3_DMA_TARGET_NODE_PCI: + case NV3_DMA_TARGET_NODE_AGP: dma_bm_write(final_address, (uint8_t*)¬ify, sizeof(nv3_notification_t), 4); break; } From c12db12ec09b3f3243c031d7ab11a565bf8043a9 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Aug 2025 17:35:07 +0100 Subject: [PATCH 214/274] fix ui compile --- src/qt/qt_mainwindow.ui | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 752389779..9b1586104 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -895,6 +895,8 @@ &Pen + + GPU Debug - VRAM Viewer From ed9380c346cff01901c365156e444fd7c769f0c5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Wed, 27 Aug 2025 20:00:01 +0100 Subject: [PATCH 215/274] fully fix compile --- src/qt/qt_mainwindow.ui | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 9b1586104..68f2c43b5 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -897,6 +897,11 @@ &Pen + + + &CGA composite settings... + + GPU Debug - VRAM Viewer From d8bbb1c1f2dc214eafdc9d4c2ce3d843774b75a4 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 31 Aug 2025 16:19:10 +0100 Subject: [PATCH 216/274] Merge alls hit from nvplayground --- src/include/86box/nv/vid_nv1.h | 161 +++++++++++++++++++++++++++++ src/include/86box/nv/vid_nv4.h | 141 +++++++++++++++++++++++++ src/include/86box/video.h | 3 + src/video/CMakeLists.txt | 8 ++ src/video/nv/nv1/nv1_core.c | 110 ++++++++++++++++++++ src/video/nv/nv1/nv1_core_config.c | 133 ++++++++++++++++++++++++ src/video/nv/nv4/nv4_core.c | 86 +++++++++++++++ src/video/nv/nv4/nv4_core_config.c | 96 +++++++++++++++++ src/video/vid_table.c | 3 + 9 files changed, 741 insertions(+) create mode 100644 src/include/86box/nv/vid_nv1.h create mode 100644 src/include/86box/nv/vid_nv4.h create mode 100644 src/video/nv/nv1/nv1_core.c create mode 100644 src/video/nv/nv1/nv1_core_config.c create mode 100644 src/video/nv/nv4/nv4_core.c create mode 100644 src/video/nv/nv4/nv4_core_config.c diff --git a/src/include/86box/nv/vid_nv1.h b/src/include/86box/nv/vid_nv1.h new file mode 100644 index 000000000..98518e530 --- /dev/null +++ b/src/include/86box/nv/vid_nv1.h @@ -0,0 +1,161 @@ +/* + * 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. + * + * Quadratic Heaven + * + * Authors: Connor Hyde + * + * Copyright 2024-2025 Connor Hyde + */ + +#pragma once + +#include +#include + +extern const device_config_t nv1_config[]; // Config for RIVA 128 (revision A/B) + +// +// PCI Bus Information +// + +#define NV1_PCI_BAR0_SIZE 0xFFFFFF + +// +// VRAM +// +#define NV1_VRAM_SIZE_1MB 0x100000 +#define NV1_VRAM_SIZE_2MB 0x200000 +#define NV1_VRAM_SIZE_4MB 0x400000 + +// Video BIOS +#define NV1_VBIOS_E3D_2X00 "roms/video/nvidia/nv1/Diamond_Edge_3D_2x00.BIN" +#define NV1_VBIOS_E3D_3X00 "roms/video/nvidia/nv1/Diamond_Edge_3D_3400_BIOS_M27C256.BIN" + +// +// PMC +// +#define NV1_PMC_BOOT_0 0x0 +#define NV1_PMC_BOOT_0_REVISION 0 + +#define NV1_PMC_BOOT_0_REVISION_A 0x0 // Prototype (1994) +#define NV1_PMC_BOOT_0_REVISION_B1 0x0 // Prototype (early 1995) +#define NV1_PMC_BOOT_0_REVISION_B2 0x0 // Prototype (mid 1995) +#define NV1_PMC_BOOT_0_REVISION_B3 0x0 // Final? +#define NV1_PMC_BOOT_0_REVISION_C 0x0 // Final? + +#define NV1_PMC_BOOT_0_IMPLEMENTATION 8 +#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV0 0x1 // Nvidia Hardware Simulator (1993-1994) +#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV1_D32 0x2 // NV1 + DRAM + SGS-Thomson STG-1732/1764 DAC +#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV1_V32 0x3 // NV1 + VRAM + SGS-Thomson STG-1732/1764 DAC +#define NV1_PMC_BOOT_0_IMPLEMENTATION_PICASSO 0x4 // NV1 + VRAM + NV 128-bit DAC + + +// Defines the NV architecture version (NV1/NV2/...) +#define NV1_PMC_BOOT_0_ARCHITECTURE 16 +#define NV1_PMC_BOOT_0_ARCHITECTURE_NV0 0x0 // Nvidia Hardware Simulator (1993-1994) +#define NV1_PMC_BOOT_0_ARCHITECTURE_NV1 0x1 // NV1 (1995) +#define NV1_PMC_BOOT_0_ARCHITECTURE_NV2 0x2 // Mutara (1996, cancelled) + +#define NV1_PMC_DEBUG_0 0x80 + +#define NV1_PMC_INTR_0 0x100 +#define NV1_PMC_INTR_EN_0 0x140 + +#define NV1_PMC_INTR_EN_0_INTA 0 +#define NV1_PMC_INTR_EN_0_INTB 4 +#define NV1_PMC_INTR_EN_0_INTC 8 +#define NV1_PMC_INTR_EN_0_INTD 12 + +#define NV1_PMC_INTR_EN_0_DISABLED 0x0 +#define NV1_PMC_INTR_EN_0_HARDWARE 0x1 +#define NV1_PMC_INTR_EN_0_SOFTWARE 0x2 +#define NV1_PMC_INTR_EN_0_ALL 0x3 // (HARDWARE | SOFTWARE) + +#define NV1_PMC_INTR_READ 0x160 + +//TODO: DEFINE bits +#define NV1_PMC_ENABLE 0x200 + +// +// PRAMIN +// + +#define NV1_RAMIN_START 0x100000 + +// +// PAUTH +// Scary nvidia mode +// + +// Read only +#define NV1_PAUTH_DEBUG_0 0x605080 +#define NV1_PAUTH_DEBUG_0_BREACH_DETECTED 0 +#define NV1_PAUTH_DEBUG_0_EEPROM_INVALID 4 + +#define NV1_PAUTH_CHIP_TOKEN_0 0x605400 +#define NV1_PAUTH_CHIP_TOKEN_1 0x605404 +#define NV1_PAUTH_PASSWORD_0(i) 0x605800+(i*16) +#define NV1_PAUTH_PASSWORD_1(i) 0x605804+(i*16) +#define NV1_PAUTH_PASSWORD_2(i) 0x605808+(i*16) +#define NV1_PAUTH_PASSWORD_3(i) 0x60580C+(i*16) + +#define NV1_PAUTH_PASSWORD_SIZE 128 + +// +// PFB +// + +#define NV1_PFB_BOOT_0 0x600000 + +#define NV1_PFB_BOOT_0_RAM_AMOUNT 0 +#define NV1_PFB_BOOT_0_RAM_AMOUNT_1MB 0x0 +#define NV1_PFB_BOOT_0_RAM_AMOUNT_2MB 0x1 +#define NV1_PFB_BOOT_0_RAM_AMOUNT_4MB 0x2 + +// +// PEXTDEV +// + +#define NV1_STRAPS 0x608000 +#define NV1_STRAPS_STRAP_VENDOR 0 + +// +// PRAM+RAMIN +// + +#define NV1_PRAM_CONFIG 0x602200 +#define NV1_PRAM_CONFIG_SIZE 0 +#define NV1_PRAM_CONFIG_12KB 0 +#define NV1_PRAM_CONFIG_20KB 1 +#define NV1_PRAM_CONFIG_36KB 2 +#define NV1_PRAM_CONFIG_68KB 3 + +// Position of RAMPW in RAMIN +#define NV1_RAMPW_POSITION_CONFIG0 0x2c00 +#define NV1_RAMPW_POSITION_CONFIG1 0x4c00 +#define NV1_RAMPW_POSITION_CONFIG2 0x8c00 +#define NV1_RAMPW_POSITION_CONFIG3 0x10c00 + +// Static RAMPW mirror +#define NV1_PRAMPW 0x606000 +#define NV1_RAMPW_SIZE 0x400 + +// +// PROM +// +#define NV1_PROM 0x601000 +#define NV1_PROM_SIZE 32768 + +// Device Core +void nv1_init(); +void nv1_close(void* priv); +void nv1_speed_changed(void *priv); +void nv1_draw_cursor(svga_t* svga, int32_t drawline); +void nv1_recalc_timings(svga_t* svga); +void nv1_force_redraw(void* priv); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h new file mode 100644 index 000000000..8f2a20026 --- /dev/null +++ b/src/include/86box/nv/vid_nv4.h @@ -0,0 +1,141 @@ +/* + * 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. + * + * Riva TNT hardware defines + * + * Authors: Connor Hyde + * + * Copyright 2024-2025 Connor Hyde + */ + + +#pragma once + +extern const device_config_t nv4_config[]; // Config for RIVA 128 (revision A/B) + +// +// General +// +#define NV4_VRAM_SIZE_2MB 0x200000 // 2MB (never used; NV4 only) +#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) +#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB +#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB +#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only + +#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! + +// +// VBIOS +// +#define NV4_VBIOS_STB_REVA "roms/video/nvidia/nv4/NV4_STB_velocity.rom" + +// +// PMC +// + +#define NV4_PMC_START 0x0 + +#define NV4_PMC_INTR 0x100 +#define NV4_PMC_INTR_PMEDIA_PENDING 4 +#define NV4_PMC_INTR_PFIFO_PENDING 8 +#define NV4_PMC_INTR_PGRAPH_PENDING 12 +#define NV4_PMC_INTR_PVIDEO_PENDING 16 +#define NV4_PMC_INTR_PTIMER_PENDING 20 +#define NV4_PMC_INTR_PCRTC_PENDING 24 +#define NV4_PMC_INTR_PBUS_PENDING 28 +#define NV4_PMC_INTR_SOFTWARE_PENDING 31 + +#define NV4_PMC_INTR_EN 0x140 +#define NV4_PMC_INTR_EN_DISABLED 0x0 +#define NV4_PMC_INTR_EN_SOFTWARE 0x1 +#define NV4_PMC_INTR_EN_HARDWARE 0x2 +#define NV4_PMC_INTR_EN_ALL 0x3 + +#define NV4_PMC_BOOT 0x0 +#define NV4_PMC_ENABLE 0x200 +#define NV4_PMC_ENABLE_PMEDIA 4 // Enable mediaport external MPEG decoder engine +#define NV4_PMC_ENABLE_PFIFO 8 // Enable FIFO +#define NV4_PMC_ENABLE_PGRAPH 12 +#define NV4_PMC_ENABLE_PPMI 16 +#define NV4_PMC_ENABLE_PFB 20 +#define NV4_PMC_ENABLE_PCRTC 24 +#define NV4_PMC_ENABLE_PVIDEO 28 + +// +// PFB +// + +#define NV4_PFB_START 0x100000 +#define NV4_PFB_BOOT 0x100000 +#define NV4_PFB_BOOT_RAM_AMOUNT 0 +#define NV4_PFB_BOOT_RAM_AMOUNT_2MB 0x0 +#define NV4_PFB_BOOT_RAM_AMOUNT_4MB 0x1 +#define NV4_PFB_BOOT_RAM_AMOUNT_8MB 0x2 +#define NV4_PFB_BOOT_RAM_AMOUNT_16MB 0x3 +#define NV5_PFB_BOOT_RAM_AMOUNT_32MB 0x0 + +#define NV4_PSTRAPS 0x101000 + +#define NV4_PSTRAPS_CRYSTAL 6 +#define NV4_PSTRAPS_CRYSTAL_13500K 0x0 +#define NV4_PSTRAPS_CRYSTAL_14318180 0x1 + +// +// PRAMDAC +// + +#define NV4_PRAMDAC_START 0x680300 +#define NV4_PRAMDAC_CURSOR_START_POSITION 0x680300 + +#define NV4_PRAMDAC_CURSOR_SIZE_X 32 +#define NV4_PRAMDAC_CURSOR_SIZE_Y 32 + +// Same for all 3 clocks +#define NV4_PRAMDAC_CLOCK_VDIV 0 +#define NV4_PRAMDAC_CLOCK_NDIV 8 +#define NV4_PRAMDAC_CLOCK_PDIV 16 + +#define NV4_PRAMDAC_CLOCK_CORE 0x680500 // NVPLL +#define NV4_PRAMDAC_CLOCK_MEMORY 0x680504 +#define NV4_PRAMDAC_CLOCK_PIXEL 0x680508 +#define NV4_PRAMDAC_COEFF_SELECT 0x68050C +#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE 0 +#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE_XTAL 0x0 +#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE_VIP 0x1 +#define NV4_PRAMDAC_COEFF_SELECT_SOURCE 8 // Bit not set = hardware, otherwise software +#define NV4_PRAMDAC_COEFF_SELECT_MPLL_IS_SOFTWARE 0x1 +#define NV4_PRAMDAC_COEFF_SELECT_VPLL_IS_SOFTWARE 0x2 +#define NV4_PRAMDAC_COEFF_SELECT_NVPLL_IS_SOFTWARE 0x4 +#define NV4_PRAMDAC_COEFF_SELECT_ALL_SOFTWARE 0x7 +#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV 16 +#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_NONE 0x0 +#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_VSCLK 0x1 +#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_PCLK 0x2 +#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_BOTH 0x3 +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE 20 +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE_EXT 0x0 +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE_VIP 0x1 // VIP = Video Interface Port / Mediaport +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO 24 +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO_DB1 0x0 +#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO_DB2 0x1 +#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO 28 +#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO_DB1 0x0 +#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO_DB2 0x1 + +#define NV4_PRAMDAC_GENERAL_CONTROL 0x680600 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE 12 + +#define NV4_RAMIN_START 0x700000 // Nominal. In reality PROM is here on real NV4 + +// Device Core +void nv4_init(); +void nv4_close(void* priv); +void nv4_speed_changed(void *priv); +void nv4_draw_cursor(svga_t* svga, int32_t drawline); +void nv4_recalc_timings(svga_t* svga); +void nv4_force_redraw(void* priv); \ No newline at end of file diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 4271c4f78..fd8d79e8d 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -626,10 +626,13 @@ 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 nv1_device_edge2k; +extern const device_t nv1_device_edge3k; extern const device_t nv3_device_pci; extern const device_t nv3_device_agp; extern const device_t nv3t_device_pci; extern const device_t nv3t_device_agp; +extern const device_t nv4_device_agp; /* Wyse 700 */ extern const device_t wy700_device; diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 4e490094c..edd5de3a9 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -139,6 +139,10 @@ add_library(vid OBJECT nv/nv_base.c nv/nv_rivatimer.c + # NVidia NV1 + nv/nv1/nv1_core.c + nv/nv1/nv1_core_config.c + # NVidia RIVA 128 - Subsystems nv/nv3/nv3_core.c nv/nv3/nv3_core_config.c @@ -192,6 +196,10 @@ add_library(vid OBJECT nv/nv3/render/nv3_render_primitives.c nv/nv3/render/nv3_render_blit.c + # NVidia RIVA TNT/TNT2 - Core + nv/nv4/nv4_core.c + nv/nv4/nv4_core_config.c + # Generic vid_bochs_vbe.c ) diff --git a/src/video/nv/nv1/nv1_core.c b/src/video/nv/nv1/nv1_core.c new file mode 100644 index 000000000..3de528b26 --- /dev/null +++ b/src/video/nv/nv1/nv1_core.c @@ -0,0 +1,110 @@ +/* + * 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-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv1.h> + + +void nv1_init() +{ + +} + +void* nv1_init_edge2k(const device_t *info) +{ + +} + +void* nv1_init_edge3k(const device_t *info) +{ + +} + +void nv1_close(void* priv) +{ + +} + +void nv1_speed_changed(void *priv) +{ + +} + +void nv1_draw_cursor(svga_t* svga, int32_t drawline) +{ + +} + +void nv1_recalc_timings(svga_t* svga) +{ + +} + +void nv1_force_redraw(void* priv) +{ + +} + +// See if the bios rom is available. +int32_t nv1_available(void) +{ + return (rom_present(NV1_VBIOS_E3D_2X00) + || rom_present(NV1_VBIOS_E3D_3X00)); +} + +// NV3 (RIVA 128) +// PCI +// 2MB or 4MB VRAM +const device_t nv1_device_edge2k = +{ + .name = "nVIDIA NV1 [Diamond Edge 3D 2x00] [Not Direct3D Compatible]", + .internal_name = "nv1_edge2k", + .flags = DEVICE_PCI, + .local = 0, + .init = nv1_init_edge2k, + .close = nv1_close, + .speed_changed = nv1_speed_changed, + .force_redraw = nv1_force_redraw, + .available = nv1_available, + .config = nv1_config, +}; + +// NV3 (RIVA 128) +// AGP +// 2MB or 4MB VRAM +const device_t nv1_device_edge3k = +{ + .name = "nVIDIA NV1 [Diamond Edge 3D 3x00] [Not Direct3D Compatible]", + .internal_name = "nv1_edge3k", + .flags = DEVICE_PCI, + .local = 0, + .init = nv1_init_edge3k, + .close = nv1_close, + .speed_changed = nv1_speed_changed, + .force_redraw = nv1_force_redraw, + .available = nv1_available, + .config = nv1_config, +}; diff --git a/src/video/nv/nv1/nv1_core_config.c b/src/video/nv/nv1/nv1_core_config.c new file mode 100644 index 000000000..460067f6f --- /dev/null +++ b/src/video/nv/nv1/nv1_core_config.c @@ -0,0 +1,133 @@ +/* + * 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. + * + * Provides NV4 configuration + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv1.h> + +const device_config_t nv1_config[] = +{ + // Memory configuration + { + .name = "vram_size", + .description = "VRAM Size", + .type = CONFIG_SELECTION, + .default_int = NV1_VRAM_SIZE_4MB, + .selection = + { + // I thought this was never released, but it seems that at least one was released: + // The card was called the "NEC G7AGK" + { + .description = "1 MB", + .value = NV1_VRAM_SIZE_1MB, + }, + { + .description = "2 MB", + .value = NV1_VRAM_SIZE_2MB, + }, + { + .description = "4 MB", + .value = NV1_VRAM_SIZE_4MB, + }, + } + + }, + // Multithreading configuration + { + + .name = "pgraph_threads", +#ifndef RELEASE_BUILD + .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", +#else + .description = "Render threads", +#endif + .type = CONFIG_SELECTION, + .default_int = 1, // todo: change later + .selection = + { + { + .description = "1 thread (Only use if issues appear with more threads)", + .value = 1, + }, + { + .description = "2 threads", + .value = 2, + }, + { + .description = "4 threads", + .value = 4, + }, + { + .description = "8 threads", + .value = 8, + }, + }, + }, + { + .name = "RAMDAC Type", + .description = "SGS-Thomson RAMDAC type", + .default_int = 0x1764, + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "SGS-Thomson STG-1732X", + .value = 0x1732, + }, + { + .description = "SGS-Thomson STG-1764X/NVDAC64", + .value = 0x1764, + }, + } + }, + { + .name = "Chip type", + .description = "Chip type", + .default_int = 0x1, + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "SGS-Thomson STG-2000", + .value = 0x2000, + }, + { + .description = "Nvidia NV1", + .value = 0x1, + }, + } + }, +#ifndef RELEASE_BUILD + { + .name = "nv_debug_fulllog", + .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", + .type = CONFIG_BINARY, + .default_int = 0, + }, +#endif + { + .type = CONFIG_END + } +}; \ No newline at end of file diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c new file mode 100644 index 000000000..2af47f22a --- /dev/null +++ b/src/video/nv/nv4/nv4_core.c @@ -0,0 +1,86 @@ +/* + * 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-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +void nv4_init() +{ + +} + +void* nv4_init_stb4400(const device_t *info) +{ + +} + +void nv4_close(void* priv) +{ + +} + +void nv4_speed_changed(void *priv) +{ + +} + +void nv4_draw_cursor(svga_t* svga, int32_t drawline) +{ + +} + +void nv4_recalc_timings(svga_t* svga) +{ + +} + +void nv4_force_redraw(void* priv) +{ + +} + +// See if the bios rom is available. +int32_t nv4_available(void) +{ + return (rom_present(NV4_VBIOS_STB_REVA)); +} + +// NV3 (RIVA 128) +// AGP +// 8MB or 16MB VRAM +const device_t nv4_device_agp = +{ + .name = "nVIDIA RIVA TNT [STB Velocity 4400]", + .internal_name = "nv4_stb4400", + .flags = DEVICE_AGP, + .local = 0, + .init = nv4_init_stb4400, + .close = nv4_close, + .speed_changed = nv4_speed_changed, + .force_redraw = nv4_force_redraw, + .available = nv4_available, + .config = nv4_config, +}; diff --git a/src/video/nv/nv4/nv4_core_config.c b/src/video/nv/nv4/nv4_core_config.c new file mode 100644 index 000000000..ea9ef9332 --- /dev/null +++ b/src/video/nv/nv4/nv4_core_config.c @@ -0,0 +1,96 @@ +/* + * 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. + * + * Provides NV4 configuration + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +const device_config_t nv4_config[] = +{ + // Memory configuration + { + .name = "vram_size", + .description = "VRAM Size", + .type = CONFIG_SELECTION, + .default_int = NV4_VRAM_SIZE_16MB, + .selection = + { + // I thought this was never released, but it seems that at least one was released: + // The card was called the "NEC G7AGK" + { + .description = "8 MB", + .value = NV4_VRAM_SIZE_8MB, + }, + + { + .description = "16 MB", + .value = NV4_VRAM_SIZE_16MB, + }, + } + + }, + // Multithreading configuration + { + + .name = "pgraph_threads", +#ifndef RELEASE_BUILD + .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", +#else + .description = "Render threads", +#endif + .type = CONFIG_SELECTION, + .default_int = 1, // todo: change later + .selection = + { + { + .description = "1 thread (Only use if issues appear with more threads)", + .value = 1, + }, + { + .description = "2 threads", + .value = 2, + }, + { + .description = "4 threads", + .value = 4, + }, + { + .description = "8 threads", + .value = 8, + }, + }, + }, +#ifndef RELEASE_BUILD + { + .name = "nv_debug_fulllog", + .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", + .type = CONFIG_BINARY, + .default_int = 0, + }, +#endif + { + .type = CONFIG_END + } +}; \ No newline at end of file diff --git a/src/video/vid_table.c b/src/video/vid_table.c index db05e13ab..e2b9b3542 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -261,10 +261,13 @@ video_cards[] = { { .device = &compaq_voodoo_3_3500_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &voodoo_3_3500_se_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &voodoo_3_3500_si_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &nv1_device_edge2k, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &nv1_device_edge3k, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3t_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &nv3t_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &nv4_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = NULL, .flags = VIDEO_FLAG_TYPE_NONE } // clang-format on }; From e9b09082b91a548277ed45a86144d572361e18bd Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 31 Aug 2025 16:24:41 +0100 Subject: [PATCH 217/274] remove duplicate actions --- src/qt/qt_mainwindow.ui | 80 ----------------------------------------- 1 file changed, 80 deletions(-) diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 194c595e1..8f5f8c1cf 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -1125,86 +1125,6 @@ &1x - - - true - - - &0.5x - - - - - true - - - &1x - - - - - true - - - 1.&5x - - - - - true - - - &2x - - - - - true - - - &3x - - - - - true - - - &4x - - - - - true - - - &5x - - - - - true - - - &6x - - - - - true - - - &7x - - - - - true - - - &8x - - From 5f6efd5742f55f9e14d11c4c498f77968cbf833d Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 31 Aug 2025 23:51:16 +0100 Subject: [PATCH 218/274] Add nv4 defines --- src/include/86box/nv/vid_nv1.h | 7 + src/include/86box/nv/vid_nv4.h | 120 +- src/include/86box/nv/vid_nv4_defines.h | 4314 ++++++++++++++++++++++++ 3 files changed, 4330 insertions(+), 111 deletions(-) create mode 100644 src/include/86box/nv/vid_nv4_defines.h diff --git a/src/include/86box/nv/vid_nv1.h b/src/include/86box/nv/vid_nv1.h index 98518e530..9d08bb186 100644 --- a/src/include/86box/nv/vid_nv1.h +++ b/src/include/86box/nv/vid_nv1.h @@ -17,6 +17,7 @@ #include #include +#include <86Box/nv/vid_nv.h> extern const device_config_t nv1_config[]; // Config for RIVA 128 (revision A/B) @@ -152,6 +153,12 @@ extern const device_config_t nv1_config[]; // Confi #define NV1_PROM 0x601000 #define NV1_PROM_SIZE 32768 +// Structures +typedef struct nv1_s +{ + nv_base_t nvbase; // Base Nvidia structure +} nv1_t; + // Device Core void nv1_init(); void nv1_close(void* priv); diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 8f2a20026..7fef843d4 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -16,121 +16,19 @@ #pragma once +#include +#include +#include <86Box/nv/vid_nv.h> +#include <86Box/nv/vid_nv4_defines.h> extern const device_config_t nv4_config[]; // Config for RIVA 128 (revision A/B) -// -// General -// -#define NV4_VRAM_SIZE_2MB 0x200000 // 2MB (never used; NV4 only) -#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) -#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB -#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB -#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only -#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! -// -// VBIOS -// -#define NV4_VBIOS_STB_REVA "roms/video/nvidia/nv4/NV4_STB_velocity.rom" - -// -// PMC -// - -#define NV4_PMC_START 0x0 - -#define NV4_PMC_INTR 0x100 -#define NV4_PMC_INTR_PMEDIA_PENDING 4 -#define NV4_PMC_INTR_PFIFO_PENDING 8 -#define NV4_PMC_INTR_PGRAPH_PENDING 12 -#define NV4_PMC_INTR_PVIDEO_PENDING 16 -#define NV4_PMC_INTR_PTIMER_PENDING 20 -#define NV4_PMC_INTR_PCRTC_PENDING 24 -#define NV4_PMC_INTR_PBUS_PENDING 28 -#define NV4_PMC_INTR_SOFTWARE_PENDING 31 - -#define NV4_PMC_INTR_EN 0x140 -#define NV4_PMC_INTR_EN_DISABLED 0x0 -#define NV4_PMC_INTR_EN_SOFTWARE 0x1 -#define NV4_PMC_INTR_EN_HARDWARE 0x2 -#define NV4_PMC_INTR_EN_ALL 0x3 - -#define NV4_PMC_BOOT 0x0 -#define NV4_PMC_ENABLE 0x200 -#define NV4_PMC_ENABLE_PMEDIA 4 // Enable mediaport external MPEG decoder engine -#define NV4_PMC_ENABLE_PFIFO 8 // Enable FIFO -#define NV4_PMC_ENABLE_PGRAPH 12 -#define NV4_PMC_ENABLE_PPMI 16 -#define NV4_PMC_ENABLE_PFB 20 -#define NV4_PMC_ENABLE_PCRTC 24 -#define NV4_PMC_ENABLE_PVIDEO 28 - -// -// PFB -// - -#define NV4_PFB_START 0x100000 -#define NV4_PFB_BOOT 0x100000 -#define NV4_PFB_BOOT_RAM_AMOUNT 0 -#define NV4_PFB_BOOT_RAM_AMOUNT_2MB 0x0 -#define NV4_PFB_BOOT_RAM_AMOUNT_4MB 0x1 -#define NV4_PFB_BOOT_RAM_AMOUNT_8MB 0x2 -#define NV4_PFB_BOOT_RAM_AMOUNT_16MB 0x3 -#define NV5_PFB_BOOT_RAM_AMOUNT_32MB 0x0 - -#define NV4_PSTRAPS 0x101000 - -#define NV4_PSTRAPS_CRYSTAL 6 -#define NV4_PSTRAPS_CRYSTAL_13500K 0x0 -#define NV4_PSTRAPS_CRYSTAL_14318180 0x1 - -// -// PRAMDAC -// - -#define NV4_PRAMDAC_START 0x680300 -#define NV4_PRAMDAC_CURSOR_START_POSITION 0x680300 - -#define NV4_PRAMDAC_CURSOR_SIZE_X 32 -#define NV4_PRAMDAC_CURSOR_SIZE_Y 32 - -// Same for all 3 clocks -#define NV4_PRAMDAC_CLOCK_VDIV 0 -#define NV4_PRAMDAC_CLOCK_NDIV 8 -#define NV4_PRAMDAC_CLOCK_PDIV 16 - -#define NV4_PRAMDAC_CLOCK_CORE 0x680500 // NVPLL -#define NV4_PRAMDAC_CLOCK_MEMORY 0x680504 -#define NV4_PRAMDAC_CLOCK_PIXEL 0x680508 -#define NV4_PRAMDAC_COEFF_SELECT 0x68050C -#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE 0 -#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE_XTAL 0x0 -#define NV4_PRAMDAC_COEFF_SELECT_VPLL_SOURCE_VIP 0x1 -#define NV4_PRAMDAC_COEFF_SELECT_SOURCE 8 // Bit not set = hardware, otherwise software -#define NV4_PRAMDAC_COEFF_SELECT_MPLL_IS_SOFTWARE 0x1 -#define NV4_PRAMDAC_COEFF_SELECT_VPLL_IS_SOFTWARE 0x2 -#define NV4_PRAMDAC_COEFF_SELECT_NVPLL_IS_SOFTWARE 0x4 -#define NV4_PRAMDAC_COEFF_SELECT_ALL_SOFTWARE 0x7 -#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV 16 -#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_NONE 0x0 -#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_VSCLK 0x1 -#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_PCLK 0x2 -#define NV4_PRAMDAC_COEFF_SELECT_VS_PCLK_TV_BOTH 0x3 -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE 20 -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE_EXT 0x0 -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_SOURCE_VIP 0x1 // VIP = Video Interface Port / Mediaport -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO 24 -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO_DB1 0x0 -#define NV4_PRAMDAC_COEFF_SELECT_TVCLK_RATIO_DB2 0x1 -#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO 28 -#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO_DB1 0x0 -#define NV4_PRAMDAC_COEFF_SELECT_VCLK_RATIO_DB2 0x1 - -#define NV4_PRAMDAC_GENERAL_CONTROL 0x680600 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE 12 - -#define NV4_RAMIN_START 0x700000 // Nominal. In reality PROM is here on real NV4 +// Structures +typedef struct nv4_s +{ + nv_base_t nvbase; // Base Nvidia structure +} nv4_t; // Device Core void nv4_init(); diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h new file mode 100644 index 000000000..f7499e3a8 --- /dev/null +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -0,0 +1,4314 @@ +#pragma once + +#include +#include + +// +// General +// +#define NV4_VRAM_SIZE_2MB 0x200000 // 2MB (never used; NV4 only) +#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) +#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB +#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB +#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only + +#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! + +// +// VBIOS +// +#define NV4_VBIOS_STB_REVA "roms/video/nvidia/nv4/NV4_STB_velocity.rom" + +#define NV4_PRMIO_START 0x7000 +#define NV4_PRMIO_END 0x7FFF +#define NV4_PRMIO_RMA_ID 0x7100 +#define NV4_PRMIO_RMA_ID_CODE 0 +#define NV4_PRMIO_RMA_ID_CODE_VALID 0x2B16D065 +#define NV4_PRMIO_RMA_PTR 0x7104 +#define NV4_PRMIO_RMA_PTR_ADDRESS 2 +#define NV4_PRMIO_RMA_DATA 0x7108 +#define NV4_PRMIO_RMA_DATA_PORT 0 +#define NV4_PRMIO_RMA_DATA32 0x710C +#define NV4_PRMIO_RMA_DATA32_BYTE2 16 +#define NV4_PRMIO_RMA_DATA32_BYTE1 8 +#define NV4_PRMIO_RMA_DATA32_BYTE0 0 + +#define NV4_PRAMDAC_START 0x680300 +#define NV4_PRAMDAC_END 0x680FFF + +#define NV4_PRAMDAC_CU_START_POS 0x680300 +#define NV4_PRAMDAC_CU_START_POS_X 0 +#define NV4_PRAMDAC_CU_START_POS_Y 16 +#define NV4_PRAMDAC_NVPLL_COEFF 0x680500 +#define NV4_PRAMDAC_NVPLL_COEFF_MDIV 0 +#define NV4_PRAMDAC_NVPLL_COEFF_NDIV 8 +#define NV4_PRAMDAC_NVPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_MPLL_COEFF 0x680504 +#define NV4_PRAMDAC_MPLL_COEFF_MDIV 0 +#define NV4_PRAMDAC_MPLL_COEFF_NDIV 8 +#define NV4_PRAMDAC_MPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_VPLL_COEFF 0x680508 +#define NV4_PRAMDAC_VPLL_COEFF_MDIV 0 +#define NV4_PRAMDAC_VPLL_COEFF_NDIV 8 +#define NV4_PRAMDAC_VPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_PLL_COEFF_SELECT 0x68050C +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE 0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE_XTAL 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE_VIP 0x1 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE 8 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_DEFAULT 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL 0x1 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL 0x2 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL 0x4 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_ALL 0x7 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV 16 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_NONE 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_VSCLK 0x1 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_PCLK 0x2 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_BOTH 0x3 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE 20 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE_EXT 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE_VIP 0x1 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO 24 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO_DB1 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO_DB2 0x1 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO 28 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB1 0x0 +#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 0x1 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL 0x680510 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_VALUE 0 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_VAL 0x44E +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN 12 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_ON 0x0 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_MPLL 0x1 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_VPLL 0x2 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_NVPLL 0x4 +#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_OFF 0x7 +#define NV4_PRAMDAC_PLL_TEST_COUNTER 0x680514 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_NOOFIPCLKS 0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_VALUE 0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE 16 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE_DEASSERTED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE_ASSERTED 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET 20 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET_DEASSERTED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET_ASSERTED 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE 24 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_MCLK 0x2 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_VCLK 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_NVCLK 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIV_RST 28 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIVRST_DEASSERTED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIVRST_ASSERTED 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK 29 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_NOTLOCKED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCKED 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK 30 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_NOTLOCKED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCKED 0x1 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK 31 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_NOTLOCKED 0x0 +#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCKED 0x1 +#define NV4_PRAMDAC_PALETTE_TEST 0x680518 +#define NV4_PRAMDAC_PALETTE_TEST_BLUE_DATA 0 +#define NV4_PRAMDAC_PALETTE_TEST_GREEN_DATA 8 +#define NV4_PRAMDAC_PALETTE_TEST_RED_DATA 16 +#define NV4_PRAMDAC_PALETTE_TEST_MODE 24 +#define NV4_PRAMDAC_PALETTE_TEST_MODE_8BIT 0x0 +#define NV4_PRAMDAC_PALETTE_TEST_MODE_24BIT 0x1 +#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC 28 +#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC_READWRITE 0x0 +#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC_WRITEONLY 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL 0x680600 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT 0 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT_24 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT_31 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX 4 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_OFF 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_POS 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_NEG 0x2 +#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON 0x3 +#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE 8 +#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE_NOTSEL 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE 12 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_NOTSEL 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_15 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_16 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_24 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_30 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL 16 +#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL_OFF 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL_ON 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION 17 +#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION_37OHM 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_BPC 20 +#define NV4_PRAMDAC_GENERAL_CONTROL_BPC_6BITS 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_BPC_8BITS 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP 24 +#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP_DIS 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP_EN 0x1 +#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK 28 +#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK_EN 0x0 +#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK_DIS 0x1 +#define NV4_PRAMDAC_PALETTE_RECOVERY 0x680604 +#define NV4_PRAMDAC_PALETTE_RECOVERY_ACTIVE_ADDRESS 0 +#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER 8 +#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_RED 0x1 +#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_GREEN 0x2 +#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_BLUE 0x4 +#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE 12 +#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE_WRITE 0x0 +#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE_READ 0x3 +#define NV4_PRAMDAC_PALETTE_RECOVERY_RED_DATA 16 +#define NV4_PRAMDAC_PALETTE_RECOVERY_GREEN_DATA 24 +#define NV4_PRAMDAC_TEST_CONTROL 0x680608 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET 0 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET_DEASSERTED 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET_ASSERTED 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE 4 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE_DEASSERTED 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE_ASSERTED 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL 8 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_BLUE 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_GREEN 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_RED 0x2 +#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN 12 +#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN_DEASSERTED 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC 16 +#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_ON 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_DACTM 20 +#define NV4_PRAMDAC_TEST_CONTROL_DACTM_NORMAL 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_DACTM_TEST 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH1 24 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH1_CLEAR 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH1_SET 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH31 25 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH31_CLEAR 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_TPATH31_SET 0x1 +#define NV4_PRAMDAC_TEST_CONTROL_SENSEB 28 +#define NV4_PRAMDAC_TEST_CONTROL_SENSEB_SOMELO 0x0 +#define NV4_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI 0x1 +#define NV4_PRAMDAC_CHECKSUM 0x68060C +#define NV4_PRAMDAC_CHECKSUM_VALUE 0 +#define NV4_PRAMDAC_TESTPOINT_DATA 0x680610 +#define NV4_PRAMDAC_TESTPOINT_DATA_RED 0 +#define NV4_PRAMDAC_TESTPOINT_DATA_GREEN 10 +#define NV4_PRAMDAC_TESTPOINT_DATA_BLUE 20 +#define NV4_PRAMDAC_TESTPOINT_DATA_BLACK 30 +#define NV4_PRAMDAC_TESTPOINT_DATA_NOTBLANK 31 +#define NV4_PRAMDAC_TV_SETUP 0x680700 +#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE 0 +#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE_SLAVE 0x0 +#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE_MASTER 0x1 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT 4 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_555 0x0 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_565 0x1 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_888 0x2 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_101010 0x3 +#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_YUV 0x4 +#define NV4_PRAMDAC_TV_SETUP_DATA_SRC 8 +#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_COMP 0x0 +#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_SCALER 0x1 +#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_VIP 0x2 +#define NV4_PRAMDAC_TV_SETUP_COMP_SRC 12 +#define NV4_PRAMDAC_TV_SETUP_COMP_SRC_SCALER 0x0 +#define NV4_PRAMDAC_TV_SETUP_COMP_SRC_NO_SCALER 0x1 +#define NV4_PRAMDAC_TV_SETUP_SYNC_POL 16 +#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_NONE 0x0 +#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_HSYNC 0x1 +#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_VSYNC 0x2 +#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_BOTH 0x3 +#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC 20 +#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC_LEAD 0x0 +#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC_TRAIL 0x1 +#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS 24 +#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS_7_0 0x0 +#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS_11_4 0x1 +#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD 28 +#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD_0 0x0 +#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD_1 0x1 +#define NV4_PRAMDAC_TV_VBLANK_START 0x680704 +#define NV4_PRAMDAC_TV_VBLANK_START_VAL 0 +#define NV4_PRAMDAC_TV_VBLANK_END 0x680708 +#define NV4_PRAMDAC_TV_VBLANK_END_VAL 0 +#define NV4_PRAMDAC_TV_HBLANK_START 0x68070C +#define NV4_PRAMDAC_TV_HBLANK_START_VAL 0 +#define NV4_PRAMDAC_TV_HBLANK_END 0x680710 +#define NV4_PRAMDAC_TV_HBLANK_END_VAL 0 +#define NV4_PRAMDAC_BLANK_COLOR 0x680714 +#define NV4_PRAMDAC_BLANK_COLOR_VAL 0 +#define NV4_PRAMDAC_TV_CHECKSUM 0x680718 +#define NV4_PRAMDAC_TV_CHECKSUM_VAL 0 +#define NV4_PRAMDAC_TV_VSYNC 28 +#define NV4_PRAMDAC_TV_VSYNC_ACTIVE 0x0 +#define NV4_PRAMDAC_TV_VSYNC_INACTIVE 0x1 +#define NV4_PRAMDAC_TV_TEST_CONTROL 0x68071c +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET 0 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET_DEASSERTED 0x0 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET_ASSERTED 0x1 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE 4 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE_DEASSERTED 0x0 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE_ASSERTED 0x1 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL 8 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_7_0 0x0 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_15_8 0x1 +#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_23_16 0x2 + +#define NV4_USER_DAC_START 0x681200 +#define NV4_USER_DAC_END 0x681FFF + +#define NV4_USER_DAC_PIXEL_MASK 0x6813C6 +#define NV4_USER_DAC_PIXEL_MASK_VALUE 0 +#define NV4_USER_DAC_PIXEL_MASK_MASK 0xFF +#define NV4_USER_DAC_READ_MODE_ADDRESS 0x6813C7 +#define NV4_USER_DAC_READ_MODE_ADDRESS_VALUE 0 +#define NV4_USER_DAC_READ_MODE_ADDRESS_WO_VALUE 0 +#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE 0 +#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE_WRITE 0x0 +#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE_READ 0x3 +#define NV4_USER_DAC_WRITE_MODE_ADDRESS 0x6813C8 +#define NV4_USER_DAC_WRITE_MODE_ADDRESS_VALUE 0 +#define NV4_USER_DAC_PALETTE_DATA 0x6813C9 +#define NV4_USER_DAC_PALETTE_DATA_VALUE 0 + +#define NV4_PRMDIO_START 0x681000 +#define NV4_PRMDIO_END 0x681FFF + +#define NV4_IO_MPU_401_DATA 0x330 +#define NV4_IO_MPU_401_DATA_ALIAS_1 0x300 +#define NV4_IO_MPU_401_DATA_ALIAS_2 0x230 +#define NV4_IO_MPU_401_DATA_VALUE 0 +#define NV4_IO_MPU_401_DATA_ACK 0xFE +#define NV4_IO_MPU_401_STATUS 0x331 +#define NV4_IO_MPU_401_STATUS_ALIAS_1 0x301 +#define NV4_IO_MPU_401_STATUS_ALIAS_2 0x231 +#define NV4_IO_MPU_401_STATUS_DATA 0 +#define NV4_IO_MPU_401_STATUS_WRITE 6 +#define NV4_IO_MPU_401_STATUS_WRITE_EMPTY 0x0 +#define NV4_IO_MPU_401_STATUS_WRITE_FULL 0x1 +#define NV4_IO_MPU_401_STATUS_READ 7 +#define NV4_IO_MPU_401_STATUS_READ_FULL 0x0 +#define NV4_IO_MPU_401_STATUS_READ_EMPTY 0x1 +#define NV4_IO_MPU_401_COM 0x331 +#define NV4_IO_MPU_401_COM_ALIAS_1 0x301 +#define NV4_IO_MPU_401_COM_ALIAS_2 0x231 +#define NV4_IO_MPU_401_COM_UART_MODE 0 +#define NV4_IO_MPU_401_COM_UART_MODE_COMPLEX 0xff +#define NV4_IO_MPU_401_COM_UART_MODE_SIMPLE 0x3f + +#define NV4_PMC_START 0x0 +#define NV4_PMC_END 0xFFF + +#define NV4_PMC_BOOT_0 0x0 +#define NV4_PMC_BOOT_0_MINOR_REVISION 0 +#define NV4_PMC_BOOT_0_MINOR_REVISION_0 0x0 +#define NV4_PMC_BOOT_0_MAJOR_REVISION 4 +#define NV4_PMC_BOOT_0_MAJOR_REVISION_A 0x0 +#define NV4_PMC_BOOT_0_MAJOR_REVISION_B 0x1 +#define NV4_PMC_BOOT_0_IMPLEMENTATION 8 +#define NV4_PMC_BOOT_0_IMPLEMENTATION_NV4_0 0x0 +#define NV4_PMC_BOOT_0_ARCHITECTURE 12 +#define NV4_PMC_BOOT_0_ARCHITECTURE_NV0 0x0 +#define NV4_PMC_BOOT_0_ARCHITECTURE_NV1 0x1 +#define NV4_PMC_BOOT_0_ARCHITECTURE_NV2 0x2 +#define NV4_PMC_BOOT_0_ARCHITECTURE_NV3 0x3 +#define NV4_PMC_BOOT_0_ARCHITECTURE_NV4 0x4 +#define NV4_PMC_BOOT_0_FIB_REVISION 16 +#define NV4_PMC_BOOT_0_FIB_REVISION_0 0x0 +#define NV4_PMC_BOOT_0_MASK_REVISION 20 +#define NV4_PMC_BOOT_0_MASK_REVISION_A 0x0 +#define NV4_PMC_BOOT_0_MASK_REVISION_B 0x1 +#define NV4_PMC_BOOT_0_MASK_REVISION_C 0x2 +#define NV4_PMC_BOOT_0_MANUFACTURER 24 +#define NV4_PMC_BOOT_0_MANUFACTURER_NVIDIA 0x0 +#define NV4_PMC_BOOT_0_FOUNDRY 28 +#define NV4_PMC_BOOT_0_FOUNDRY_SGS 0x0 +#define NV4_PMC_BOOT_0_FOUNDRY_HELIOS 0x1 +#define NV4_PMC_BOOT_0_FOUNDRY_TSMC 0x2 +#define NV4_PMC_INTR_0 0x100 +#define NV4_PMC_INTR_0_PMEDIA 4 +#define NV4_PMC_INTR_0_PMEDIA_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PMEDIA_PENDING 0x1 +#define NV4_PMC_INTR_0_PFIFO 8 +#define NV4_PMC_INTR_0_PFIFO_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PFIFO_PENDING 0x1 +#define NV4_PMC_INTR_0_PGRAPH 12 +#define NV4_PMC_INTR_0_PGRAPH_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PGRAPH_PENDING 0x1 +#define NV4_PMC_INTR_0_PVIDEO 16 +#define NV4_PMC_INTR_0_PVIDEO_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PVIDEO_PENDING 0x1 +#define NV4_PMC_INTR_0_PTIMER 20 +#define NV4_PMC_INTR_0_PTIMER_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PTIMER_PENDING 0x1 +#define NV4_PMC_INTR_0_PCRTC 24 +#define NV4_PMC_INTR_0_PCRTC_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PCRTC_PENDING 0x1 +#define NV4_PMC_INTR_0_PBUS 28 +#define NV4_PMC_INTR_0_PBUS_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_PBUS_PENDING 0x1 +#define NV4_PMC_INTR_0_SOFTWARE 31 +#define NV4_PMC_INTR_0_SOFTWARE_NOT_PENDING 0x0 +#define NV4_PMC_INTR_0_SOFTWARE_PENDING 0x1 +#define NV4_PMC_INTR_EN_0 0x140 +#define NV4_PMC_INTR_EN_0_INTA 0 +#define NV4_PMC_INTR_EN_0_INTA_DISABLED 0x0 +#define NV4_PMC_INTR_EN_0_INTA_HARDWARE 0x1 +#define NV4_PMC_INTR_EN_0_INTA_SOFTWARE 0x2 +#define NV4_PMC_INTR_READ_0 0x160 +#define NV4_PMC_INTR_READ_0_INTA 0 +#define NV4_PMC_INTR_READ_0_INTA_LOW 0x0 +#define NV4_PMC_INTR_READ_0_INTA_HIGH 0x1 +#define NV4_PMC_ENABLE 0x200 +#define NV4_PMC_ENABLE_PMEDIA 4 +#define NV4_PMC_ENABLE_PMEDIA_ENABLED 0x1 +#define NV4_PMC_ENABLE_PFIFO 8 +#define NV4_PMC_ENABLE_PFIFO_ENABLED 0x1 +#define NV4_PMC_ENABLE_PGRAPH 12 +#define NV4_PMC_ENABLE_PGRAPH_ENABLED 0x1 +#define NV4_PMC_ENABLE_PPMI 16 +#define NV4_PMC_ENABLE_PPMI_ENABLED 0x1 +#define NV4_PMC_ENABLE_PFB 20 +#define NV4_PMC_ENABLE_PFB_ENABLED 0x1 +#define NV4_PMC_ENABLE_PCRTC 24 +#define NV4_PMC_ENABLE_PCRTC_ENABLED 0x1 +#define NV4_PMC_ENABLE_PVIDEO 28 +#define NV4_PMC_ENABLE_PVIDEO_ENABLED 0x1 + +#define NV4_PBUS_START 0x1000 +#define NV4_PBUS_END 0x1FFF + +#define NV4_PBUS_DEBUG_0 0x1080 +#define NV4_PBUS_DEBUG_0_FBIO_SCLK_DELAY 0 +#define NV4_PBUS_DEBUG_0_FBIO_SCLK_DELAY_8 0x8 +#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC 4 +#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC_NORMAL 0x0 +#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC_OVERRIDE 0x1 +#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_DELAY 8 +#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_DELAY_8 0x8 +#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC 12 +#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC_NORMAL 0x0 +#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC_OVERRIDE 0x1 +#define NV4_PBUS_DEBUG_0_FBIO_ACLK_DELAY 16 +#define NV4_PBUS_DEBUG_0_FBIO_ACLK_DELAY_8 0x8 +#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC 20 +#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC_NORMAL 0x0 +#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC_OVERRIDE 0x1 +#define NV4_PBUS_DEBUG_0_FBIO_RCLK_DELAY 24 +#define NV4_PBUS_DEBUG_0_FBIO_RCLK_DELAY_8 0x8 +#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC 28 +#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC_NORMAL 0x0 +#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC_OVERRIDE 0x1 +#define NV4_PBUS_DEBUG_1 0x1084 +#define NV4_PBUS_DEBUG_1_PCIM_THROTTLE 0 +#define NV4_PBUS_DEBUG_1_PCIM_THROTTLE_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIM_CMD 1 +#define NV4_PBUS_DEBUG_1_PCIM_CMD_SIZE_BASED 0x0 +#define NV4_PBUS_DEBUG_1_PCIM_CMD_MRL_ONLY 0x1 +#define NV4_PBUS_DEBUG_1_HASH_DECODE 2 +#define NV4_PBUS_DEBUG_1_HASH_DECODE_1FF 0x0 +#define NV4_PBUS_DEBUG_1_HASH_DECODE_2FF 0x1 +#define NV4_PBUS_DEBUG_1_AGPM_CMD 3 +#define NV4_PBUS_DEBUG_1_AGPM_CMD_HP_ON_1ST 0x0 +#define NV4_PBUS_DEBUG_1_AGPM_CMD_LP_ONLY 0x1 +#define NV4_PBUS_DEBUG_1_AGPM_CMD_HP_ONLY 0x2 +#define NV4_PBUS_DEBUG_1_PCIS_WRITE 5 +#define NV4_PBUS_DEBUG_1_PCIS_WRITE_0_CYCLE 0x0 +#define NV4_PBUS_DEBUG_1_PCIS_WRITE_1_CYCLE 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_2_1 6 +#define NV4_PBUS_DEBUG_1_PCIS_2_1_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_RETRY 7 +#define NV4_PBUS_DEBUG_1_PCIS_RETRY_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_RD_BURST 8 +#define NV4_PBUS_DEBUG_1_PCIS_RD_BURST_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_WR_BURST 9 +#define NV4_PBUS_DEBUG_1_PCIS_WR_BURST_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_EARLY_RTY 10 +#define NV4_PBUS_DEBUG_1_PCIS_EARLY_RTY_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_PCIS_CPUQ 12 +#define NV4_PBUS_DEBUG_1_PCIS_CPUQ_ENABLED 0x1 +#define NV4_PBUS_DEBUG_1_DPSH_DECODE 13 +#define NV4_PBUS_DEBUG_1_DPSH_DECODE_NV4 0x0 +#define NV4_PBUS_DEBUG_1_DPSH_DECODE_NV3 0x1 +#define NV4_PBUS_DEBUG_1_SPARE1 14 +#define NV4_PBUS_DEBUG_1_SPARE2 15 +#define NV4_PBUS_DEBUG_1_SPARE3 16 +#define NV4_PBUS_DEBUG_1_SPARE4 17 +#define NV4_PBUS_DEBUG_1_SPARE5 18 +#define NV4_PBUS_DEBUG_1_SPARE6 19 +#define NV4_PBUS_DEBUG_1_SPARE7 20 +#define NV4_PBUS_DEBUG_1_SPARE8 21 +#define NV4_PBUS_DEBUG_1_SPARE9 22 +#define NV4_PBUS_DEBUG_1_SPARE10 23 +#define NV4_PBUS_DEBUG_2 0x1088 +#define NV4_PBUS_DEBUG_2_AGP_DIFFERENTIAL 0 +#define NV4_PBUS_DEBUG_2_AGP_DIFFERENTIAL_ENABLED 0x1 +#define NV4_PBUS_DEBUG_2_AGP_SB_STB_DELAY 9:4 +#define NV4_PBUS_DEBUG_2_AGP_SB_STB_DELAY_34 0x22 +#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC 12 +#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC_NORMAL 0x0 +#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC_OVERRIDE 0x1 +#define NV4_PBUS_DEBUG_3 0x108C +#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE 0 +#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_UNLIMITED 0x0 +#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_32_BYTES 0x1 +#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_64_BYTES 0x2 +#define NV4_PBUS_DEBUG_CTL 0x1090 +#define NV4_PBUS_DEBUG_CTL_MODE 0 +#define NV4_PBUS_DEBUG_CTL_MODE_ENABLED 0x1 +#define NV4_PBUS_DEBUG_CTL_READ_SELECT 4 +#define NV4_PBUS_DEBUG_CTL_READ_SELECT_0 0x0 +#define NV4_PBUS_DEBUG_CTL_READ_SELECT_1 0x1 +#define NV4_PBUS_DEBUG_READ 0x1094 +#define NV4_PBUS_DEBUG_READ_DATA 0 +#define NV4_PBUS_DEBUG_HOST 0x109C +#define NV4_PBUS_DEBUG_HOST_SEL 0 +#define NV4_PBUS_DEBUG_SEL_0 0x10A0 +#define NV4_PBUS_DEBUG_SEL_0_X 0 +#define NV4_PBUS_DEBUG_SEL_1 0x10A4 +#define NV4_PBUS_DEBUG_SEL_1_X 0 +#define NV4_PBUS_DEBUG_SEL_2 0x10A8 +#define NV4_PBUS_DEBUG_SEL_2_X 0 +#define NV4_PBUS_DEBUG_SEL_3 0x10AC +#define NV4_PBUS_DEBUG_SEL_3_X 0 +#define NV4_PBUS_INTR_0 0x1100 +#define NV4_PBUS_INTR_0_PCI_BUS_ERROR 0 +#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_NOT_PENDING 0x0 +#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_PENDING 0x1 +#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_RESET 0x1 +#define NV4_PBUS_INTR_EN_0 0x1140 +#define NV4_PBUS_INTR_EN_0_PCI_BUS_ERROR 0 +#define NV4_PBUS_INTR_EN_0_PCI_BUS_ERROR_ENABLED 0x1 +#define NV4_PBUS_ROM_CONFIG 0x1200 +#define NV4_PBUS_ROM_CONFIG_TW1 0 +#define NV4_PBUS_ROM_CONFIG_TW1_DEFAULT 0xF +#define NV4_PBUS_ROM_CONFIG_TW0 4 +#define NV4_PBUS_ROM_CONFIG_TW0_DEFAULT 0x3 +#define NV4_PBUS_PCI_NV_0 0x1800 +#define NV4_PBUS_PCI_NV_0_VENDOR_ID 0 +#define NV4_PBUS_PCI_NV_0_VENDOR_ID_NVIDIA_SGS 0x12D2 +#define NV4_PBUS_PCI_NV_0_VENDOR_ID_NVIDIA 0x10DE +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_FUNC 16 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_FUNC_VGA 0x0 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP 3 19 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV0 0x0 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV1 0x1 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV2 0x2 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV3 0x3 +#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV4 0x4 +#define NV4_PBUS_PCI_NV_1 0x1804 +#define NV4_PBUS_PCI_NV_1_IO_SPACE 0 +#define NV4_PBUS_PCI_NV_1_IO_SPACE_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_1_MEMORY_SPACE 1 +#define NV4_PBUS_PCI_NV_1_MEMORY_SPACE_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_1_BUS_MASTER 2 +#define NV4_PBUS_PCI_NV_1_BUS_MASTER_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_1_WRITE_AND_INVAL 4 +#define NV4_PBUS_PCI_NV_1_WRITE_AND_INVAL_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_1_PALETTE_SNOOP 5 +#define NV4_PBUS_PCI_NV_1_PALETTE_SNOOP_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_1_CAPLIST 20 +#define NV4_PBUS_PCI_NV_1_CAPLIST_NOT_PRESENT 0x0 +#define NV4_PBUS_PCI_NV_1_CAPLIST_PRESENT 0x1 +#define NV4_PBUS_PCI_NV_1_66MHZ 21 +#define NV4_PBUS_PCI_NV_1_66MHZ_INCAPABLE 0x0 +#define NV4_PBUS_PCI_NV_1_66MHZ_CAPABLE 0x1 +#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK 23 +#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK_INCAPABLE 0x0 +#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK_CAPABLE 0x1 +#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING 25 +#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_FAST 0x0 +#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_MEDIUM 0x1 +#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_SLOW 0x2 +#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET 27 +#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_NO_ABORT 0x0 +#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_ABORT 0x1 +#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_CLEAR 0x1 +#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET 28 +#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_NO_ABORT 0x0 +#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_ABORT 0x1 +#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_CLEAR 0x1 +#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER 29 +#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_NO_ABORT 0x0 +#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_ABORT 0x1 +#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_CLEAR 0x1 +#define NV4_PBUS_PCI_NV_2 0x1808 +#define NV4_PBUS_PCI_NV_2_REVISION_ID 0 +#define NV4_PBUS_PCI_NV_2_REVISION_ID_A01 0x0 +#define NV4_PBUS_PCI_NV_2_REVISION_ID_B01 0x10 +#define NV4_PBUS_PCI_NV_2_CLASS_CODE 8 +#define NV4_PBUS_PCI_NV_2_CLASS_CODE_VGA 0x30000 +#define NV4_PBUS_PCI_NV_2_CLASS_CODE_MULTIMEDIA 0x48000 +#define NV4_PBUS_PCI_NV_3 0x180C +#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER 11 +#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_0_CLOCKS 0x0 +#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_8_CLOCKS 0x1 +#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_240_CLOCKS 0x1E +#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_248_CLOCKS 0x1F +#define NV4_PBUS_PCI_NV_3_HEADER_TYPE 16 +#define NV4_PBUS_PCI_NV_3_HEADER_TYPE_SINGLEFUNC 0x0 +#define NV4_PBUS_PCI_NV_3_HEADER_TYPE_MULTIFUNC 0x80 +#define NV4_PBUS_PCI_NV_4 0x1810 +#define NV4_PBUS_PCI_NV_4_SPACE_TYPE 0 +#define NV4_PBUS_PCI_NV_4_SPACE_TYPE_MEMORY 0x0 +#define NV4_PBUS_PCI_NV_4_SPACE_TYPE_IO 0x1 +#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE 1 +#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_32_BIT 0x0 +#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_20_BIT 0x1 +#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_64_BIT 0x2 +#define NV4_PBUS_PCI_NV_4_PREFETCHABLE 3 +#define NV4_PBUS_PCI_NV_4_PREFETCHABLE_NOT 0x0 +#define NV4_PBUS_PCI_NV_4_PREFETCHABLE_MERGABLE 0x1 +#define NV4_PBUS_PCI_NV_4_BASE_ADDRESS 24 +#define NV4_PBUS_PCI_NV_5 0x1814 +#define NV4_PBUS_PCI_NV_5_SPACE_TYPE 0 +#define NV4_PBUS_PCI_NV_5_SPACE_TYPE_MEMORY 0x0 +#define NV4_PBUS_PCI_NV_5_SPACE_TYPE_IO 0x1 +#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE 2:1 +#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_32_BIT 0x0 +#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_20_BIT 0x1 +#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_64_BIT 0x2 +#define NV4_PBUS_PCI_NV_5_PREFETCHABLE 3 +#define NV4_PBUS_PCI_NV_5_PREFETCHABLE_NOT 0x0 +#define NV4_PBUS_PCI_NV_5_PREFETCHABLE_MERGABLE 0x1 +#define NV4_PBUS_PCI_NV_5_BASE_ADDRESS 24 +#define NV4_PBUS_PCI_NV_6(i) (0x1818+(i)*4) +#define NV4_PBUS_PCI_NV_6_SIZE_1 5 +#define NV4_PBUS_PCI_NV_6_RESERVED 0 +#define NV4_PBUS_PCI_NV_6_RESERVED_0 0x0 +#define NV4_PBUS_PCI_NV_11 0x182C +#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_VENDOR_ID 0 +#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_VENDOR_ID_NONE 0x0 +#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_ID 16 +#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_ID_NONE 0x0 +#define NV4_PBUS_PCI_NV_12 0x1830 +#define NV4_PBUS_PCI_NV_12_ROM_DECODE 0 +#define NV4_PBUS_PCI_NV_12_ROM_DECODE_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_12_ROM_BASE 16 +#define NV4_PBUS_PCI_NV_13 0x1834 +#define NV4_PBUS_PCI_NV_13_CAP_PTR 0 +#define NV4_PBUS_PCI_NV_13_CAP_PTR_AGP 0x44 +#define NV4_PBUS_PCI_NV_13_CAP_PTR_POWER_MGMT 0x60 +#define NV4_PBUS_PCI_NV_14 0x1838 +#define NV4_PBUS_PCI_NV_14_RESERVED 0 +#define NV4_PBUS_PCI_NV_14_RESERVED_0 0x0 +#define NV4_PBUS_PCI_NV_15 0x183C +#define NV4_PBUS_PCI_NV_15_INTR_LINE 0 +#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ0 0x0 +#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ1 0x1 +#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ15 0xF +#define NV4_PBUS_PCI_NV_15_INTR_LINE_UNKNOWN 0xFF +#define NV4_PBUS_PCI_NV_15_INTR_PIN 8 +#define NV4_PBUS_PCI_NV_15_INTR_PIN_INTA 0x1 +#define NV4_PBUS_PCI_NV_15_MIN_GNT 16 +#define NV4_PBUS_PCI_NV_15_MIN_GNT_NO_REQUIREMENTS 0x0 +#define NV4_PBUS_PCI_NV_15_MIN_GNT_750NS 0x3 +#define NV4_PBUS_PCI_NV_15_MIN_GNT_1250NS 0x5 +#define NV4_PBUS_PCI_NV_15_MAX_LAT 24 +#define NV4_PBUS_PCI_NV_15_MAX_LAT_NO_REQUIREMENTS 0x0 +#define NV4_PBUS_PCI_NV_15_MAX_LAT_250NS 0x1 +#define NV4_PBUS_PCI_NV_16 0x1840 +#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_VENDOR_ID 0 +#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_VENDOR_ID_NONE 0x0 +#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_ID 16 +#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_ID_NONE 0x0 +#define NV4_PBUS_PCI_NV_17 0x1844 +#define NV4_PBUS_PCI_NV_17_AGP_REV_MAJOR 20 +#define NV4_PBUS_PCI_NV_17_AGP_REV_MAJOR_1 0x1 +#define NV4_PBUS_PCI_NV_17_AGP_REV_MINOR 16 +#define NV4_PBUS_PCI_NV_17_AGP_REV_MINOR_0 0x0 +#define NV4_PBUS_PCI_NV_17_NEXT_PTR 8 +#define NV4_PBUS_PCI_NV_17_NEXT_PTR_NULL 0x0 +#define NV4_PBUS_PCI_NV_17_CAP_ID 0 +#define NV4_PBUS_PCI_NV_17_CAP_ID_AGP 0x2 +#define NV4_PBUS_PCI_NV_18 0x1848 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RQ 24 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RQ_16 0xF +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA 9 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA_NONE 0x0 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA_CAPABLE 0x1 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE 0 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_1X 0x1 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_2X 0x2 +#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_1X_AND_2X 0x3 +#define NV4_PBUS_PCI_NV_19 0x184C +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_RQ_DEPTH 24 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_RQ_DEPTH_0 0x0 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE 9 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE_OFF 0x0 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE_ON 0x1 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE 8 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE_OFF 0x0 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE_ON 0x1 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE 0 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_OFF 0x0 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_1X 0x1 +#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_2X 0x2 +#define NV4_PBUS_PCI_NV_20 0x1850 +#define NV4_PBUS_PCI_NV_20_ROM_SHADOW 0 +#define NV4_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_21 0x1854 +#define NV4_PBUS_PCI_NV_21_VGA 0 +#define NV4_PBUS_PCI_NV_21_VGA_ENABLED 0x1 +#define NV4_PBUS_PCI_NV_22 0x1858 +#define NV4_PBUS_PCI_NV_22_SCRATCH 0 +#define NV4_PBUS_PCI_NV_22_SCRATCH_DEFAULT 0x23D6CE +#define NV4_PBUS_PCI_NV_23 0x185C +#define NV4_PBUS_PCI_NV_23_DT_TIMEOUT 0 +#define NV4_PBUS_PCI_NV_23_DT_TIMEOUT_16 0xF +#define NV4_PBUS_PCI_NV_24 0x1860 +#define NV4_PBUS_PCI_NV_24_PME_D3_COLD 31 +#define NV4_PBUS_PCI_NV_24_PME_D3_COLD_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_PME_D3_HOT 30 +#define NV4_PBUS_PCI_NV_24_PME_D3_HOT_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_PME_D2 29 +#define NV4_PBUS_PCI_NV_24_PME_D2_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_PME_D1 28 +#define NV4_PBUS_PCI_NV_24_PME_D1_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_PME_D0 27 +#define NV4_PBUS_PCI_NV_24_PME_D0_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_D2 26 +#define NV4_PBUS_PCI_NV_24_D2_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_D2_NOT_SUPPORTED 0x0 +#define NV4_PBUS_PCI_NV_24_D1 25 +#define NV4_PBUS_PCI_NV_24_D1_SUPPORTED 0x1 +#define NV4_PBUS_PCI_NV_24_D1_NOT_SUPPORTED 0x0 +#define NV4_PBUS_PCI_NV_24_DSI 21 +#define NV4_PBUS_PCI_NV_24_DSI_NOT_REQUIRED 0x0 +#define NV4_PBUS_PCI_NV_24_PME_CLOCK 19 +#define NV4_PBUS_PCI_NV_24_PME_CLOCK_NOT_REQUIRED 0x0 +#define NV4_PBUS_PCI_NV_24_VERSION 16 +#define NV4_PBUS_PCI_NV_24_VERSION_1 0x1 +#define NV4_PBUS_PCI_NV_24_NEXT_PTR 8 +#define NV4_PBUS_PCI_NV_24_NEXT_PTR_NULL 0x0 +#define NV4_PBUS_PCI_NV_24_NEXT_PTR_AGP 0x44 +#define NV4_PBUS_PCI_NV_24_CAP_ID 0 +#define NV4_PBUS_PCI_NV_24_CAP_ID_POWER_MGMT 0x1 +#define NV4_PBUS_PCI_NV_25 0x1864 +#define NV4_PBUS_PCI_NV_25_POWER_STATE 0 +#define NV4_PBUS_PCI_NV_25_POWER_STATE_D3_HOT 0x3 +#define NV4_PBUS_PCI_NV_25_POWER_STATE_D2 0x2 +#define NV4_PBUS_PCI_NV_25_POWER_STATE_D1 0x1 +#define NV4_PBUS_PCI_NV_25_POWER_STATE_D0 0x0 +#define NV4_PBUS_PCI_NV_26(i) (0x1868+(i)*4) +#define NV4_PBUS_PCI_NV_26_SIZE_1 38 +#define NV4_PBUS_PCI_NV_26_RESERVED 0 +#define NV4_PBUS_PCI_NV_26_RESERVED_0 0x0 + +#define NV4_PFIFO_START 0x2000 +#define NV4_PFIFO_END 0x3FFF + +#define NV4_PFIFO_DELAY_0 0x2040 +#define NV4_PFIFO_DELAY_0_WAIT_RETRY 0 +#define NV4_PFIFO_DELAY_0_WAIT_RETRY_0 0x0 +#define NV4_PFIFO_DMA_TIMESLICE 0x2044 +#define NV4_PFIFO_DMA_TIMESLICE_SELECT 0 +#define NV4_PFIFO_DMA_TIMESLICE_SELECT_1 0x0 +#define NV4_PFIFO_DMA_TIMESLICE_SELECT_16K 0x3fff +#define NV4_PFIFO_DMA_TIMESLICE_SELECT_32K 0x7fff +#define NV4_PFIFO_DMA_TIMESLICE_SELECT_64K 0xffff +#define NV4_PFIFO_DMA_TIMESLICE_SELECT_128K 0x1ffff +#define NV4_PFIFO_DMA_TIMESLICE_TIMEOUT 24 +#define NV4_PFIFO_DMA_TIMESLICE_TIMEOUT_ENABLED 0x1 +#define NV4_PFIFO_PIO_TIMESLICE 0x2048 +#define NV4_PFIFO_PIO_TIMESLICE_SELECT 0 +#define NV4_PFIFO_PIO_TIMESLICE_SELECT_1 0x0 +#define NV4_PFIFO_PIO_TIMESLICE_SELECT_16K 0x3fff +#define NV4_PFIFO_PIO_TIMESLICE_SELECT_32K 0x7fff +#define NV4_PFIFO_PIO_TIMESLICE_SELECT_64K 0xffff +#define NV4_PFIFO_PIO_TIMESLICE_SELECT_128K 0x1ffff +#define NV4_PFIFO_PIO_TIMESLICE_TIMEOUT 24 +#define NV4_PFIFO_PIO_TIMESLICE_TIMEOUT_ENABLED 0x1 +#define NV4_PFIFO_TIMESLICE 0x204C +#define NV4_PFIFO_TIMESLICE_TIMER 0 +#define NV4_PFIFO_TIMESLICE_TIMER_EXPIRED 0x3FFFF +#define NV4_PFIFO_NEXT_CHANNEL 0x2050 +#define NV4_PFIFO_NEXT_CHANNEL_CHID 0 +#define NV4_PFIFO_NEXT_CHANNEL_MODE 8 +#define NV4_PFIFO_NEXT_CHANNEL_MODE_PIO 0x0 +#define NV4_PFIFO_NEXT_CHANNEL_MODE_DMA 0x1 +#define NV4_PFIFO_NEXT_CHANNEL_SWITCH 12 +#define NV4_PFIFO_NEXT_CHANNEL_SWITCH_NOT_PENDING 0x0 +#define NV4_PFIFO_NEXT_CHANNEL_SWITCH_PENDING 0x1 +#define NV4_PFIFO_DEBUG_0 0x2080 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0 0 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0_NOT_PENDING 0x0 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0_PENDING 0x1 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1 4 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1_NOT_PENDING 0x0 +#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1_PENDING 0x1 +#define NV4_PFIFO_INTR_0 0x2100 +#define NV4_PFIFO_INTR_0_CACHE_ERROR 0 +#define NV4_PFIFO_INTR_0_CACHE_ERROR_NOT_PENDING 0x0 +#define NV4_PFIFO_INTR_0_CACHE_ERROR_PENDING 0x1 +#define NV4_PFIFO_INTR_0_CACHE_ERROR_RESET 0x1 +#define NV4_PFIFO_INTR_0_RUNOUT 4 +#define NV4_PFIFO_INTR_0_RUNOUT_NOT_PENDING 0x0 +#define NV4_PFIFO_INTR_0_RUNOUT_PENDING 0x1 +#define NV4_PFIFO_INTR_0_RUNOUT_RESET 0x1 +#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW 8 +#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_NOT_PENDING 0x0 +#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_PENDING 0x1 +#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_RESET 0x1 +#define NV4_PFIFO_INTR_0_DMA_PUSHER 12 +#define NV4_PFIFO_INTR_0_DMA_PUSHER_NOT_PENDING 0x0 +#define NV4_PFIFO_INTR_0_DMA_PUSHER_PENDING 0x1 +#define NV4_PFIFO_INTR_0_DMA_PUSHER_RESET 0x1 +#define NV4_PFIFO_INTR_0_DMA_PT 16 +#define NV4_PFIFO_INTR_0_DMA_PT_NOT_PENDING 0x0 +#define NV4_PFIFO_INTR_0_DMA_PT_PENDING 0x1 +#define NV4_PFIFO_INTR_0_DMA_PT_RESET 0x1 +#define NV4_PFIFO_INTR_EN_0 0x2140 +#define NV4_PFIFO_INTR_EN_0_CACHE_ERROR 0 +#define NV4_PFIFO_INTR_EN_0_CACHE_ERROR_ENABLED 0x1 +#define NV4_PFIFO_INTR_EN_0_RUNOUT 4 +#define NV4_PFIFO_INTR_EN_0_RUNOUT_ENABLED 0x1 +#define NV4_PFIFO_INTR_EN_0_RUNOUT_OVERFLOW 8 +#define NV4_PFIFO_INTR_EN_0_RUNOUT_OVERFLOW_ENABLED 0x1 +#define NV4_PFIFO_INTR_EN_0_DMA_PUSHER 12 +#define NV4_PFIFO_INTR_EN_0_DMA_PUSHER_ENABLED 0x1 +#define NV4_PFIFO_INTR_EN_0_DMA_PT 16 +#define NV4_PFIFO_INTR_EN_0_DMA_PT_ENABLED 0x1 +#define NV4_PFIFO_RAMHT 0x2210 +#define NV4_PFIFO_RAMHT_BASE_ADDRESS 4 +#define NV4_PFIFO_RAMHT_BASE_ADDRESS_10000 0x10 +#define NV4_PFIFO_RAMHT_SIZE 16 +#define NV4_PFIFO_RAMHT_SIZE_4K 0x0 +#define NV4_PFIFO_RAMHT_SIZE_8K 0x1 +#define NV4_PFIFO_RAMHT_SIZE_16K 0x2 +#define NV4_PFIFO_RAMHT_SIZE_32K 0x3 +#define NV4_PFIFO_RAMHT_SEARCH 24 +#define NV4_PFIFO_RAMHT_SEARCH_16 0x0 +#define NV4_PFIFO_RAMHT_SEARCH_32 0x1 +#define NV4_PFIFO_RAMHT_SEARCH_64 0x2 +#define NV4_PFIFO_RAMHT_SEARCH_128 0x3 +#define NV4_PFIFO_RAMFC 0x2214 +#define NV4_PFIFO_RAMFC_BASE_ADDRESS 1 +#define NV4_PFIFO_RAMFC_BASE_ADDRESS_11000 0x88 +#define NV4_PFIFO_RAMRO 0x2218 +#define NV4_PFIFO_RAMRO_BASE_ADDRESS 1 +#define NV4_PFIFO_RAMRO_BASE_ADDRESS_11200 0x89 +#define NV4_PFIFO_RAMRO_BASE_ADDRESS_12000 0x90 +#define NV4_PFIFO_RAMRO_SIZE 16 +#define NV4_PFIFO_RAMRO_SIZE_512 0x0 +#define NV4_PFIFO_RAMRO_SIZE_8K 0x1 +#define NV4_PFIFO_CACHES 0x2500 +#define NV4_PFIFO_CACHES_REASSIGN 0 +#define NV4_PFIFO_CACHES_REASSIGN_ENABLED 0x1 +#define NV4_PFIFO_CACHES_DMA_SUSPEND 4 +#define NV4_PFIFO_CACHES_DMA_SUSPEND_IDLE 0x0 +#define NV4_PFIFO_CACHES_DMA_SUSPEND_BUSY 0x1 +#define NV4_PFIFO_MODE 0x2504 +// Valid for all 16 channels. Do we need thees at all? +#define NV4_PFIFO_MODE_CHANNEL_IS_PIO 0x0 +#define NV4_PFIFO_MODE_CHANNEL_IS_DMA 0x1 +#define NV4_PFIFO_MODE_CHANNEL_0 0 +#define NV4_PFIFO_MODE_CHANNEL_1 1 +#define NV4_PFIFO_MODE_CHANNEL_2 2 +#define NV4_PFIFO_MODE_CHANNEL_3 3 +#define NV4_PFIFO_MODE_CHANNEL_4 4 +#define NV4_PFIFO_MODE_CHANNEL_5 5 +#define NV4_PFIFO_MODE_CHANNEL_6 6 +#define NV4_PFIFO_MODE_CHANNEL_7 7 +#define NV4_PFIFO_MODE_CHANNEL_8 8 +#define NV4_PFIFO_MODE_CHANNEL_9 9 +#define NV4_PFIFO_MODE_CHANNEL_10 10 +#define NV4_PFIFO_MODE_CHANNEL_11 11 +#define NV4_PFIFO_MODE_CHANNEL_12 12 +#define NV4_PFIFO_MODE_CHANNEL_13 13 +#define NV4_PFIFO_MODE_CHANNEL_14 14 +#define NV4_PFIFO_MODE_CHANNEL_15 15 +#define NV4_PFIFO_DMA 0x2508 +// 0 = not pending (Valid for all channels) +#define NV4_PFIFO_DMA_CHANNEL_IS_PENDING 0x1 +#define NV4_PFIFO_DMA_CHANNEL_0 0 +#define NV4_PFIFO_DMA_CHANNEL_1 1 +#define NV4_PFIFO_DMA_CHANNEL_2 2 +#define NV4_PFIFO_DMA_CHANNEL_3 3 +#define NV4_PFIFO_DMA_CHANNEL_4 4 +#define NV4_PFIFO_DMA_CHANNEL_5 5 +#define NV4_PFIFO_DMA_CHANNEL_6 6 +#define NV4_PFIFO_DMA_CHANNEL_7 7 +#define NV4_PFIFO_DMA_CHANNEL_8 8 +#define NV4_PFIFO_DMA_CHANNEL_9 9 +#define NV4_PFIFO_DMA_CHANNEL_10 10 +#define NV4_PFIFO_DMA_CHANNEL_11 11 +#define NV4_PFIFO_DMA_CHANNEL_12 12 +#define NV4_PFIFO_DMA_CHANNEL_13 13 +#define NV4_PFIFO_DMA_CHANNEL_14 14 +#define NV4_PFIFO_DMA_CHANNEL_15 15 +#define NV4_PFIFO_SIZE 0x250C +// Valid for all channels +#define NV4_PFIFO_SIZE_CHANNEL_SIZE_IS_124_BYTES 0x0 +#define NV4_PFIFO_SIZE_CHANNEL_SIZE_IS_512_BYTES 0x1 +#define NV4_PFIFO_SIZE_CHANNEL_0 0 +#define NV4_PFIFO_SIZE_CHANNEL_1 1 +#define NV4_PFIFO_SIZE_CHANNEL_2 2 +#define NV4_PFIFO_SIZE_CHANNEL_3 3 +#define NV4_PFIFO_SIZE_CHANNEL_4 4 +#define NV4_PFIFO_SIZE_CHANNEL_5 5 +#define NV4_PFIFO_SIZE_CHANNEL_6 6 +#define NV4_PFIFO_SIZE_CHANNEL_7 7 +#define NV4_PFIFO_SIZE_CHANNEL_8 8 +#define NV4_PFIFO_SIZE_CHANNEL_9 9 +#define NV4_PFIFO_SIZE_CHANNEL_10 10 +#define NV4_PFIFO_SIZE_CHANNEL_11 11 +#define NV4_PFIFO_SIZE_CHANNEL_12 12 +#define NV4_PFIFO_SIZE_CHANNEL_13 13 +#define NV4_PFIFO_SIZE_CHANNEL_14 14 +#define NV4_PFIFO_SIZE_CHANNEL_15 15 +#define NV4_PFIFO_CACHE0_PUSH0 0x3000 +#define NV4_PFIFO_CACHE0_PUSH0_ACCESS 0 +#define NV4_PFIFO_CACHE0_PUSH0_ACCESS_ENABLED 0x1 +#define NV4_PFIFO_CACHE1_PUSH0 0x3200 +#define NV4_PFIFO_CACHE1_PUSH0_ACCESS 0 +#define NV4_PFIFO_CACHE1_PUSH0_ACCESS_ENABLED 0x1 +#define NV4_PFIFO_CACHE0_PUSH1 0x3004 +#define NV4_PFIFO_CACHE0_PUSH1_CHID 0 +#define NV4_PFIFO_CACHE1_PUSH1 0x3204 +#define NV4_PFIFO_CACHE1_PUSH1_CHID 0 +#define NV4_PFIFO_CACHE1_PUSH1_MODE 8 +#define NV4_PFIFO_CACHE1_PUSH1_MODE_PIO 0x0 +#define NV4_PFIFO_CACHE1_PUSH1_MODE_DMA 0x1 +#define NV4_PFIFO_CACHE1_DMA_PUSH 0x3220 +#define NV4_PFIFO_CACHE1_DMA_PUSH_ACCESS 0 +#define NV4_PFIFO_CACHE1_DMA_PUSH_ACCESS_ENABLED 0x1 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE 4 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE_IDLE 0x0 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE_BUSY 0x1 +#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER 8 +#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER_NOT_EMPTY 0x0 +#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER_EMPTY 0x1 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS 12 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS_RUNNING 0x0 +#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS_SUSPENDED 0x1 +#define NV4_PFIFO_CACHE1_DMA_FETCH 0x3224 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG 7:3 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x0 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x1 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x2 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x3 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x4 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x5 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x6 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x7 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x8 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x9 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0xA +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0xB +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0xC +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0xD +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0xE +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0xF +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x10 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x11 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x12 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x13 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x14 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x15 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x16 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x17 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x18 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x19 +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x1A +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x1B +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x1C +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x1D +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x1E +#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x1F +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE 13 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x0 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x1 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x2 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x3 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x4 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x5 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x6 +#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x7 +// This is a count. 0x0-0xF = number of requests +#define NV4_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 16 +#define NV4_PFIFO_CACHE1_DMA_PUT 0x3240 +#define NV4_PFIFO_CACHE1_DMA_PUT_OFFSET 2 +#define NV4_PFIFO_CACHE1_DMA_GET 0x3244 +#define NV4_PFIFO_CACHE1_DMA_GET_OFFSET 2 +#define NV4_PFIFO_CACHE1_DMA_DCOUNT 0x32A0 +#define NV4_PFIFO_CACHE1_DMA_DCOUNT_VALUE 2 +#define NV4_PFIFO_CACHE1_DMA_GET_JMP_SHADOW 0x32A4 +#define NV4_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET 2 +#define NV4_PFIFO_CACHE1_DMA_RSVD_SHADOW 0x32A8 +#define NV4_PFIFO_CACHE1_DMA_RSVD_SHADOW_CMD 0 +#define NV4_PFIFO_CACHE1_DMA_DATA_SHADOW 0x32AC +#define NV4_PFIFO_CACHE1_DMA_DATA_SHADOW_VALUE 0 +#define NV4_PFIFO_CACHE1_DMA_STATE 0x3228 +#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD 2 +#define NV4_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL 13 +#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT 18 +#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT_0 0x0 +#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR 30 +#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_NONE 0x0 +#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_NON_CACHE 0x1 +#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD 0x2 +#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION 0x3 +#define NV4_PFIFO_CACHE1_DMA_INSTANCE 0x322C +#define NV4_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS 0 +#define NV4_PFIFO_CACHE1_DMA_CTL 0x3230 +#define NV4_PFIFO_CACHE1_DMA_CTL_ADJUST 11:2 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE 12 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE_NOT_PRESENT 0x0 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE_PRESENT 0x1 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY 13 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY_NOT_LINEAR 0x0 +#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY_LINEAR 0x1 +#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE 16 +#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE_PCI 0x2 +#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE_AGP 0x3 +#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO 31 +#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO_INVALID 0x0 +#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO_VALID 0x1 +#define NV4_PFIFO_CACHE1_DMA_LIMIT 0x3234 +#define NV4_PFIFO_CACHE1_DMA_LIMIT_OFFSET 2 +#define NV4_PFIFO_CACHE1_DMA_TLB_TAG 0x3238 +#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_ADDRESS 28:12 +#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE 0 +#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE_INVALID 0x0 +#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE_VALID 0x1 +#define NV4_PFIFO_CACHE1_DMA_TLB_PTE 0x323C +#define NV4_PFIFO_CACHE1_DMA_TLB_PTE_FRAME_ADDRESS 12 +#define NV4_PFIFO_CACHE0_PULL0 0x3050 +#define NV4_PFIFO_CACHE0_PULL0_ACCESS 0 +#define NV4_PFIFO_CACHE0_PULL0_ACCESS_ENABLED 0x1 +#define NV4_PFIFO_CACHE0_PULL0_HASH 4 +#define NV4_PFIFO_CACHE0_PULL0_HASH_SUCCEEDED 0x0 +#define NV4_PFIFO_CACHE0_PULL0_HASH_FAILED 0x1 +#define NV4_PFIFO_CACHE0_PULL0_DEVICE 8 +#define NV4_PFIFO_CACHE0_PULL0_DEVICE_HARDWARE 0x0 +#define NV4_PFIFO_CACHE0_PULL0_DEVICE_SOFTWARE 0x1 +#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE 12 +#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE_IDLE 0x0 +#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE_BUSY 0x1 +#define NV4_PFIFO_CACHE1_PULL0 0x3250 +#define NV4_PFIFO_CACHE1_PULL0_ACCESS 0 +#define NV4_PFIFO_CACHE1_PULL0_ACCESS_ENABLED 0x1 +#define NV4_PFIFO_CACHE1_PULL0_HASH 4 +#define NV4_PFIFO_CACHE1_PULL0_HASH_SUCCEEDED 0x0 +#define NV4_PFIFO_CACHE1_PULL0_HASH_FAILED 0x1 +#define NV4_PFIFO_CACHE1_PULL0_DEVICE 8 +#define NV4_PFIFO_CACHE1_PULL0_DEVICE_HARDWARE 0x0 +#define NV4_PFIFO_CACHE1_PULL0_DEVICE_SOFTWARE 0x1 +#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE 12 +#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE_IDLE 0x0 +#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE_BUSY 0x1 +#define NV4_PFIFO_CACHE0_PULL1 0x3054 +#define NV4_PFIFO_CACHE0_PULL1_ENGINE 0 +#define NV4_PFIFO_CACHE0_PULL1_ENGINE_SW 0x0 +#define NV4_PFIFO_CACHE0_PULL1_ENGINE_GRAPHICS 0x1 +#define NV4_PFIFO_CACHE0_PULL1_ENGINE_DVD 0x2 +#define NV4_PFIFO_CACHE1_PULL1 0x3254 +#define NV4_PFIFO_CACHE1_PULL1_ENGINE 0 +#define NV4_PFIFO_CACHE1_PULL1_ENGINE_SW 0x0 +#define NV4_PFIFO_CACHE1_PULL1_ENGINE_GRAPHICS 0x1 +#define NV4_PFIFO_CACHE1_PULL1_ENGINE_DVD 0x2 +#define NV4_PFIFO_CACHE0_HASH 0x3058 +#define NV4_PFIFO_CACHE0_HASH_INSTANCE 0 +#define NV4_PFIFO_CACHE0_HASH_VALID 16 +#define NV4_PFIFO_CACHE1_HASH 0x3258 +#define NV4_PFIFO_CACHE1_HASH_INSTANCE 0 +#define NV4_PFIFO_CACHE1_HASH_VALID 16 +#define NV4_PFIFO_CACHE0_STATUS 0x3014 +#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK 4 +#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK_NOT_EMPTY 0x0 +#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK_EMPTY 0x1 +#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK 8 +#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK_NOT_FULL 0x0 +#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK_FULL 0x1 +#define NV4_PFIFO_CACHE1_STATUS 0x3214 +#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK 4 +#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK_NOT_EMPTY 0x0 +#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK_EMPTY 0x1 +#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK 8 +#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK_NOT_FULL 0x0 +#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK_FULL 0x1 +#define NV4_PFIFO_CACHE1_STATUS1 0x3218 +#define NV4_PFIFO_CACHE1_STATUS1_RANOUT 0 +#define NV4_PFIFO_CACHE1_STATUS1_RANOUT_TRUE 0x1 +#define NV4_PFIFO_CACHE0_PUT 0x3010 +#define NV4_PFIFO_CACHE0_PUT_ADDRESS 2 +#define NV4_PFIFO_CACHE1_PUT 0x3210 +#define NV4_PFIFO_CACHE1_PUT_ADDRESS 2 +#define NV4_PFIFO_CACHE0_GET 0x3070 +#define NV4_PFIFO_CACHE0_GET_ADDRESS 2 +#define NV4_PFIFO_CACHE1_GET 0x3270 +#define NV4_PFIFO_CACHE1_GET_ADDRESS 2 +#define NV4_PFIFO_ENGINE_SW 0x0 +#define NV4_PFIFO_ENGINE_GRAPHICS 0x1 +#define NV4_PFIFO_ENGINE_DVD 0x2 +#define NV4_PFIFO_CACHE0_ENGINE 0x3080 +// For these, see above +#define NV4_PFIFO_CACHE0_SUBCHANNEL_0_ENGINE 0 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_1_ENGINE 4 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_2_ENGINE 8 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_3_ENGINE 12 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_4_ENGINE 16 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_5_ENGINE 20 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_6_ENGINE 24 +#define NV4_PFIFO_CACHE0_SUBCHANNEL_7_ENGINE 28 +#define NV4_PFIFO_CACHE1_ENGINE 0x3280 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_0_ENGINE 0 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_1_ENGINE 4 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_2_ENGINE 8 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_3_ENGINE 12 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_4_ENGINE 16 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_5_ENGINE 20 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_6_ENGINE 24 +#define NV4_PFIFO_CACHE1_SUBCHANNEL_7_ENGINE 28 +#define NV4_PFIFO_CACHE0_METHOD(i) (0x3100+(i)*8) +#define NV4_PFIFO_CACHE0_METHOD_SIZE_1 1 +#define NV4_PFIFO_CACHE0_METHOD_ADDRESS 2 +#define NV4_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 +#define NV4_PFIFO_CACHE1_METHOD(i) (0x3800+(i)*8) +#define NV4_PFIFO_CACHE1_METHOD_SIZE_1 128 +#define NV4_PFIFO_CACHE1_METHOD_ADDRESS 2 +#define NV4_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 +#define NV4_PFIFO_CACHE1_METHOD_ALIAS(i) (0x3C00+(i)*8) +#define NV4_PFIFO_CACHE1_METHOD_ALIAS_SIZE_1 128 +#define NV4_PFIFO_CACHE0_DATA(i) (0x3104+(i)*8) +#define NV4_PFIFO_CACHE0_DATA_SIZE_1 1 +#define NV4_PFIFO_CACHE0_DATA_VALUE 0 +#define NV4_PFIFO_CACHE1_DATA(i) (0x3804+(i)*8) +#define NV4_PFIFO_CACHE1_DATA_SIZE_1 128 +#define NV4_PFIFO_CACHE1_DATA_VALUE 0 +#define NV4_PFIFO_CACHE1_DATA_ALIAS(i) (0x3C04+(i)*8) +#define NV4_PFIFO_CACHE1_DATA_ALIAS_SIZE_1 128 +#define NV4_PFIFO_DEVICE(i) (0x2800+(i)*4) +#define NV4_PFIFO_DEVICE_SIZE_1 128 +#define NV4_PFIFO_DEVICE_CHID 0 +#define NV4_PFIFO_DEVICE_SWITCH 24 +#define NV4_PFIFO_DEVICE_SWITCH_AVAILABLE 0x1 +#define NV4_PFIFO_RUNOUT_STATUS 0x2400 +#define NV4_PFIFO_RUNOUT_STATUS_RANOUT 0 +#define NV4_PFIFO_RUNOUT_STATUS_RANOUT_TRUE 0x1 +#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK 4 +#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK_NOT_EMPTY 0x0 +#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK_EMPTY 0x1 +#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK 8 +#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK_NOT_FULL 0x0 +#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK_FULL 0x1 +#define NV4_PFIFO_RUNOUT_PUT 0x2410 +#define NV4_PFIFO_RUNOUT_PUT_ADDRESS 3 // if size=0, 8:3, otherwise 12:3 +#define NV4_PFIFO_RUNOUT_GET 0x2420 +#define NV4_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 + +#define NV4_PGRAPH_START 0x400000 +#define NV4_PGRAPH_END 0x401FFF + +#define NV4_PGRAPH_DEBUG_0 0x400080 +#define NV4_PGRAPH_DEBUG_0_STATE 0 +#define NV4_PGRAPH_DEBUG_0_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_FE_STATE 1 +#define NV4_PGRAPH_DEBUG_0_FE_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_FE_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_CACHE_STATE 2 +#define NV4_PGRAPH_DEBUG_0_CACHE_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_CACHE_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE 3 +#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_PREROP_STATE 4 +#define NV4_PGRAPH_DEBUG_0_PREROP_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_PREROP_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_ROP_STATE 5 +#define NV4_PGRAPH_DEBUG_0_ROP_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_ROP_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_RSTR_STATE 6 +#define NV4_PGRAPH_DEBUG_0_RSTR_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_RSTR_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE 7 +#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_DMA_STATE 8 +#define NV4_PGRAPH_DEBUG_0_DMA_STATE_NORMAL 0x0 +#define NV4_PGRAPH_DEBUG_0_DMA_STATE_RESET 0x1 +#define NV4_PGRAPH_DEBUG_0_SPARE1 12 +#define NV4_PGRAPH_DEBUG_0_SPARE2 13 +#define NV4_PGRAPH_DEBUG_0_MINUSD5 14 +#define NV4_PGRAPH_DEBUG_0_MINUSD5_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_BLIT_DST_LIMIT 15 +#define NV4_PGRAPH_DEBUG_0_BLIT_DST_LIMIT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_LIMIT_CHECK 16 +#define NV4_PGRAPH_DEBUG_0_LIMIT_CHECK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_LIMIT_INT 17 +#define NV4_PGRAPH_DEBUG_0_LIMIT_INT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_OVRFLW_INT 18 +#define NV4_PGRAPH_DEBUG_0_OVRFLW_INT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D 20 +#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D 21 +#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_DRAWDIR_AUTO 24 +#define NV4_PGRAPH_DEBUG_0_DRAWDIR_AUTO_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y 25 +#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y_DECR 0x0 +#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y_INCR 0x1 +#define NV4_PGRAPH_DEBUG_0_ALPHA_ABORT 28 +#define NV4_PGRAPH_DEBUG_0_ALPHA_ABORT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1 0x400084 +#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET 0 +#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET_NOT_LAST 0x0 +#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET_LAST 0x1 +#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY 4 +#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY_IGNORE 0x0 +#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY_CANCEL 0x1 +#define NV4_PGRAPH_DEBUG_1_PATCH_INV 8 +#define NV4_PGRAPH_DEBUG_1_PATCH_INV_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_ALT_RW_SEQ 10 +#define NV4_PGRAPH_DEBUG_1_ALT_RW_SEQ_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_TRI_OPTS 12 +#define NV4_PGRAPH_DEBUG_1_TRI_OPTS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_TRICLIP_OPTS 13 +#define NV4_PGRAPH_DEBUG_1_TRICLIP_OPTS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_INSTANCE 16 +#define NV4_PGRAPH_DEBUG_1_INSTANCE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_ALOM_BURST 17 +#define NV4_PGRAPH_DEBUG_1_ALOM_BURST_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_BIDIR_DRAIN 18 +#define NV4_PGRAPH_DEBUG_1_BIDIR_DRAIN_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_EARLY_POST 19 +#define NV4_PGRAPH_DEBUG_1_EARLY_POST_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_CTX 20 +#define NV4_PGRAPH_DEBUG_1_CTX_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_FIXED_ADRS 21 +#define NV4_PGRAPH_DEBUG_1_FIXED_ADRS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_2D 22 +#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_2D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_3D 23 +#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_CACHE 24 +#define NV4_PGRAPH_DEBUG_1_CACHE_IGNORE 0x0 +#define NV4_PGRAPH_DEBUG_1_CACHE_FLUSH 0x1 +#define NV4_PGRAPH_DEBUG_1_CACHE_MODE 25 +#define NV4_PGRAPH_DEBUG_1_CACHE_MODE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_ZCLAMP 28 +#define NV4_PGRAPH_DEBUG_1_ZCLAMP_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_UCLAMP 29 +#define NV4_PGRAPH_DEBUG_1_UCLAMP_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_RCLAMP 30 +#define NV4_PGRAPH_DEBUG_1_RCLAMP_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_1_DX6_2PIXMODE 31 +#define NV4_PGRAPH_DEBUG_1_DX6_2PIXMODE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2 0x400088 +#define NV4_PGRAPH_DEBUG_2_HONOR_SRCFMT 0 +#define NV4_PGRAPH_DEBUG_2_HONOR_SRCFMT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_PINGPONG 0 +#define NV4_PGRAPH_DEBUG_2_PINGPONG_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_SPARE1 4 +#define NV4_PGRAPH_DEBUG_2_SPARE2 5 +#define NV4_PGRAPH_DEBUG_2_SPARE3 6 +#define NV4_PGRAPH_DEBUG_2_SPARE4 7 +#define NV4_PGRAPH_DEBUG_2_SPARE5 8 +#define NV4_PGRAPH_DEBUG_2_SPARE6 9 +#define NV4_PGRAPH_DEBUG_2_SPARE7 10 +#define NV4_PGRAPH_DEBUG_2_MCLK_RECTS 11 +#define NV4_PGRAPH_DEBUG_2_MCLK_RECTS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_BILINEAR_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_ANISOTROPIC_3D 13 +#define NV4_PGRAPH_DEBUG_2_ANISOTROPIC_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_FOG_3D 14 +#define NV4_PGRAPH_DEBUG_2_FOG_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_SPECULAR_3D 15 +#define NV4_PGRAPH_DEBUG_2_SPECULAR_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_ALPHA_3D 16 +#define NV4_PGRAPH_DEBUG_2_ALPHA_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ 17 +#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_CRZRWCW 0x0 +#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_ZRWCRW 0x1 +#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_AUTO 0x2 +#define NV4_PGRAPH_DEBUG_2_COELESCE_D3D 20 +#define NV4_PGRAPH_DEBUG_2_COELESCE_D3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_COELESCE_2D 22 +#define NV4_PGRAPH_DEBUG_2_COELESCE_2D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_FAST_VERTEX_LOAD 23 +#define NV4_PGRAPH_DEBUG_2_FAST_VERTEX_LOAD_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_BLIT_MULTILINE 24 +#define NV4_PGRAPH_DEBUG_2_BLIT_MULTILINE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_2_VOLATILE_RESET 28 +#define NV4_PGRAPH_DEBUG_2_VOLATILE_RESET_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3 0x40008C +#define NV4_PGRAPH_DEBUG_3_CULLING 0 +#define NV4_PGRAPH_DEBUG_3_CULLING_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE 1 +#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE_DX3 0x0 +#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE_DX5 0x1 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_STRTCH 4 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_STRTCH_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_D3D 5 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_D3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_IMAGE 6 +#define NV4_PGRAPH_DEBUG_3_FAST_DATA_IMAGE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_ZFLUSH 7 +#define NV4_PGRAPH_DEBUG_3_ZFLUSH_IGNORE 0x0 +#define NV4_PGRAPH_DEBUG_3_ZFLUSH_ACTIVATE 0x1 +#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_PTZ 8 +#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_PTZ_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D 9 +#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_PTZ 10 +#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_PTZ_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D 11 +#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_POSTDITHER_2D 12 +#define NV4_PGRAPH_DEBUG_3_POSTDITHER_2D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_POSTDITHER_3D 13 +#define NV4_PGRAPH_DEBUG_3_POSTDITHER_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_PREDITHER_2D 14 +#define NV4_PGRAPH_DEBUG_3_PREDITHER_2D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_PREDITHER_3D 15 +#define NV4_PGRAPH_DEBUG_3_PREDITHER_3D_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_FORCE_CREAD 16 +#define NV4_PGRAPH_DEBUG_3_FORCE_CREAD_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_FORCE_ZREAD 17 +#define NV4_PGRAPH_DEBUG_3_FORCE_ZREAD_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_EARLYZ_ABORT 18 +#define NV4_PGRAPH_DEBUG_3_EARLYZ_ABORT_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_TRIEND_FLUSH 19 +#define NV4_PGRAPH_DEBUG_3_TRIEND_FLUSH_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_DATA_CHECK 20 +#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_FAIL 21 +#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_FAIL_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_FORMAT_CHECK 22 +#define NV4_PGRAPH_DEBUG_3_FORMAT_CHECK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_DMA_CHECK 23 +#define NV4_PGRAPH_DEBUG_3_DMA_CHECK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_RAMREADBACK 24 +#define NV4_PGRAPH_DEBUG_3_RAMREADBACK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_CLIP_METHODS 25 +#define NV4_PGRAPH_DEBUG_3_CLIP_METHODS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_EXCLUDE_ROP_IN_IDLE 27 +#define NV4_PGRAPH_DEBUG_3_EXCLUDE_ROP_IN_IDLE_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_STATE_CHECK 28 +#define NV4_PGRAPH_DEBUG_3_STATE_CHECK_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_CONTEXT_METHODS 29 +#define NV4_PGRAPH_DEBUG_3_CONTEXT_METHODS_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_OPERATION_METHOD 30 +#define NV4_PGRAPH_DEBUG_3_OPERATION_METHOD_ENABLED 0x1 +#define NV4_PGRAPH_DEBUG_3_IGNORE_PATCHVALID 31 +#define NV4_PGRAPH_DEBUG_3_IGNORE_PATCHVALID_ENABLED 0x1 +#define NV4_PGRAPH_INTR 0x400100 +#define NV4_PGRAPH_INTR_NOTIFY 0 +#define NV4_PGRAPH_INTR_NOTIFY_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_NOTIFY_PENDING 0x1 +#define NV4_PGRAPH_INTR_NOTIFY_RESET 0x1 +#define NV4_PGRAPH_INTR_MISSING_HW 4 +#define NV4_PGRAPH_INTR_MISSING_HW_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_MISSING_HW_PENDING 0x1 +#define NV4_PGRAPH_INTR_MISSING_HW_RESET 0x1 +#define NV4_PGRAPH_INTR_TLB_PRESENT_A 8 +#define NV4_PGRAPH_INTR_TLB_PRESENT_A_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_TLB_PRESENT_A_PENDING 0x1 +#define NV4_PGRAPH_INTR_TLB_PRESENT_A_RESET 0x1 +#define NV4_PGRAPH_INTR_TLB_PRESENT_B 9 +#define NV4_PGRAPH_INTR_TLB_PRESENT_B_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_TLB_PRESENT_B_PENDING 0x1 +#define NV4_PGRAPH_INTR_TLB_PRESENT_B_RESET 0x1 +#define NV4_PGRAPH_INTR_CONTEXT_SWITCH 12 +#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_PENDING 0x1 +#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_RESET 0x1 +#define NV4_PGRAPH_INTR_BUFFER_NOTIFY 16 +#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_NOT_PENDING 0x0 +#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_PENDING 0x1 +#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_RESET 0x1 +#define NV4_PGRAPH_NSTATUS 0x400104 +#define NV4_PGRAPH_NSTATUS_STATE_IN_USE 11 +#define NV4_PGRAPH_NSTATUS_STATE_IN_USE_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSTATUS_STATE_IN_USE_PENDING 0x1 +#define NV4_PGRAPH_NSTATUS_INVALID_STATE 12 +#define NV4_PGRAPH_NSTATUS_INVALID_STATE_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSTATUS_INVALID_STATE_PENDING 0x1 +#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT 13 +#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT_PENDING 0x1 +#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT 14 +#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE 0x400108 +#define NV4_PGRAPH_NSOURCE_NOTIFICATION 0 +#define NV4_PGRAPH_NSOURCE_NOTIFICATION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_NOTIFICATION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_DATA_ERROR 1 +#define NV4_PGRAPH_NSOURCE_DATA_ERROR_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_DATA_ERROR_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR 2 +#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION 3 +#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR 4 +#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_ 5 +#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD 6 +#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION 7 +#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION 8 +#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION 9 +#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION 10 +#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_STATE_INVALID 11 +#define NV4_PGRAPH_NSOURCE_STATE_INVALID_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_STATE_INVALID_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY 12 +#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE 13 +#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_METHOD_CNT 14 +#define NV4_PGRAPH_NSOURCE_METHOD_CNT_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_METHOD_CNT_PENDING 0x1 +#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION 15 +#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION_NOT_PENDING 0x0 +#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION_PENDING 0x1 +#define NV4_PGRAPH_INTR_EN 0x400140 +#define NV4_PGRAPH_INTR_EN_NOTIFY 0 +#define NV4_PGRAPH_INTR_EN_NOTIFY_ENABLED 0x1 +#define NV4_PGRAPH_INTR_EN_MISSING_HW 4 +#define NV4_PGRAPH_INTR_EN_MISSING_HW_ENABLED 0x1 +#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_A 8 +#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_A_ENABLED 0x1 +#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_B 9 +#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_B_ENABLED 0x1 +#define NV4_PGRAPH_INTR_EN_CONTEXT_SWITCH 12 +#define NV4_PGRAPH_INTR_EN_CONTEXT_SWITCH_ENABLED 0x1 +#define NV4_PGRAPH_INTR_EN_BUFFER_NOTIFY 16 +#define NV4_PGRAPH_INTR_EN_BUFFER_NOTIFY_ENABLED 0x1 +#define NV4_PGRAPH_CTX_SWITCH1 0x400160 +#define NV4_PGRAPH_CTX_SWITCH1_GRCLASS 0 +#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY 12 +#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY_DISABLE 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY_ENABLE 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP 13 +#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP_DISABLE 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP_ENABLE 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE 14 +#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE_DISABLE 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE_ENABLE 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG 17:15 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY_AND 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_ROP_AND 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_BLEND_AND 0x2 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY 0x3 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY_PRE 0x4 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_BLEND_PRE 0x5 +#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE 20 +#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_COMPATIBILITY 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_DITHER 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_TRUNCATE 0x2 +#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_MS 0x3 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS 24 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS_INVALID 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS_VALID 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE 25 +#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE_INVALID 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE_VALID 0x1 +#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET 31 +#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET_IGNORE 0x0 +#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET_ENABLED 0x1 +#define NV4_PGRAPH_CTX_SWITCH2 0x400164 +#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT 0 +#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_INVALID 0x00 +#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_CGA6_M1 0x01 +#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_LE_M1 0x02 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT 13:8 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_INVALID 0x00 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y8 0x01 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16A8Y8 0x02 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X24Y8 0x03 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A1R5G5B5 0x06 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X1R5G5B5 0x07 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16A1R5G5B5 0x08 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X17R5G5B5 0x09 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_R5G6B5 0x0A +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A16R5G6B5 0x0B +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16R5G6B5 0x0C +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A8R8G8B8 0x0D +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X8R8G8B8 0x0E +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y16 0x0F +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A16Y16 0x10 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16Y16 0x11 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_V8YB8U8YA8 0x12 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_YB8V8YA8U8 0x13 +#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y32 0x14 +#define NV4_PGRAPH_CTX_SWITCH2_NOTIFY_INSTANCE 16 +#define NV4_PGRAPH_CTX_SWITCH2_NOTIFY_INSTANCE_INVALID 0x00 +#define NV4_PGRAPH_CTX_SWITCH3 0x400168 +#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_0 0 +#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_0_INVALID 0x00 +#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_1 16 +#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_1_INVALID 0x00 +#define NV4_PGRAPH_CTX_SWITCH4 0x40016C +#define NV4_PGRAPH_CTX_SWITCH4_USER_INSTANCE 0 +#define NV4_PGRAPH_CTX_SWITCH4_USER_INSTANCE_INVALID 0x00 +#define NV4_PGRAPH_CTX_CACHE1(i) (0x400180+(i)*4) +#define NV4_PGRAPH_CTX_CACHE1_SIZE_1 8 +#define NV4_PGRAPH_CTX_CACHE1_GRCLASS 0 +#define NV4_PGRAPH_CTX_CACHE1_CHROMA_KEY 12 +#define NV4_PGRAPH_CTX_CACHE1_USER_CLIP 13 +#define NV4_PGRAPH_CTX_CACHE1_SWIZZLE 14 +#define NV4_PGRAPH_CTX_CACHE1_PATCH_CONFIG 19:15 +#define NV4_PGRAPH_CTX_CACHE1_SPARE1 20 +#define NV4_PGRAPH_CTX_CACHE1_PATCH_STATUS 24 +#define NV4_PGRAPH_CTX_CACHE1_CONTEXT_SURFACE 25 +#define NV4_PGRAPH_CTX_CACHE2(i) (0x4001a0+(i)*4) +#define NV4_PGRAPH_CTX_CACHE2_SIZE_1 8 +#define NV4_PGRAPH_CTX_CACHE2_MONO_FORMAT 0 +#define NV4_PGRAPH_CTX_CACHE2_COLOR_FORMAT 13:8 +#define NV4_PGRAPH_CTX_CACHE2_NOTIFY_INSTANCE 16 +#define NV4_PGRAPH_CTX_CACHE3(i) (0x4001c0+(i)*4) +#define NV4_PGRAPH_CTX_CACHE3_SIZE_1 8 +#define NV4_PGRAPH_CTX_CACHE3_DMA_INSTANCE_0 0 +#define NV4_PGRAPH_CTX_CACHE3_DMA_INSTANCE_1 16 +#define NV4_PGRAPH_CTX_CACHE4(i) (0x4001e0+(i)*4) +#define NV4_PGRAPH_CTX_CACHE4_SIZE_1 8 +#define NV4_PGRAPH_CTX_CACHE4_USER_INSTANCE 0 +#define NV4_PGRAPH_CTX_CONTROL 0x400170 +#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME 0 +#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_33US 0x0 +#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_262US 0x1 +#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_2MS 0x2 +#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_17MS 0x3 +#define NV4_PGRAPH_CTX_CONTROL_TIME 8 +#define NV4_PGRAPH_CTX_CONTROL_TIME_EXPIRED 0x0 +#define NV4_PGRAPH_CTX_CONTROL_TIME_NOT_EXPIRED 0x1 +#define NV4_PGRAPH_CTX_CONTROL_CHID 16 +#define NV4_PGRAPH_CTX_CONTROL_CHID_INVALID 0x0 +#define NV4_PGRAPH_CTX_CONTROL_CHID_VALID 0x1 +#define NV4_PGRAPH_CTX_CONTROL_CHANGE 20 +#define NV4_PGRAPH_CTX_CONTROL_CHANGE_UNAVAILABLE 0x0 +#define NV4_PGRAPH_CTX_CONTROL_CHANGE_AVAILABLE 0x1 +#define NV4_PGRAPH_CTX_CONTROL_SWITCHING 24 +#define NV4_PGRAPH_CTX_CONTROL_SWITCHING_IDLE 0x0 +#define NV4_PGRAPH_CTX_CONTROL_SWITCHING_BUSY 0x1 +#define NV4_PGRAPH_CTX_CONTROL_DEVICE 28 +#define NV4_PGRAPH_CTX_CONTROL_DEVICE_DISABLED 0x0 +#define NV4_PGRAPH_CTX_CONTROL_DEVICE_ENABLED 0x1 +#define NV4_PGRAPH_CTX_USER 0x400174 +#define NV4_PGRAPH_CTX_USER_SUBCH 13 +#define NV4_PGRAPH_CTX_USER_SUBCH_0 0x0 +#define NV4_PGRAPH_CTX_USER_CHID 24 +#define NV4_PGRAPH_CTX_USER_CHID_0 0x0 +#define NV4_PGRAPH_FIFO 0x400720 +#define NV4_PGRAPH_FIFO_ACCESS 0 +#define NV4_PGRAPH_FIFO_ACCESS_DISABLED 0x0 +#define NV4_PGRAPH_FIFO_ACCESS_ENABLED 0x1 +#define NV4_PGRAPH_FFINTFC_FIFO_0(i) (0x400730+(i)*4) +#define NV4_PGRAPH_FFINTFC_FIFO_0_SIZE_1 4 +#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG 0 +#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG_MTHD 0x0 +#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG_CHSW 0x1 +// Note: shift left by 1 (3:1). Subch number = 0-7 +#define NV4_PGRAPH_FFINTFC_FIFO_0_SUBCH 1 +#define NV4_PGRAPH_FFINTFC_FIFO_0_MTHD 4 +#define NV4_PGRAPH_FFINTFC_FIFO_0_MTHD_CTX_SWITCH 0x0 +#define NV4_PGRAPH_FFINTFC_FIFO_1(i) (0x400740+(i)*4) +#define NV4_PGRAPH_FFINTFC_FIFO_1_SIZE_1 4 +#define NV4_PGRAPH_FFINTFC_FIFO_1_ARGUMENT 0 +#define NV4_PGRAPH_FFINTFC_FIFO_PTR 0x400750 +#define NV4_PGRAPH_FFINTFC_FIFO_PTR_WRITE 0 +#define NV4_PGRAPH_FFINTFC_FIFO_PTR_WRITE_0 0x0 +#define NV4_PGRAPH_FFINTFC_FIFO_PTR_READ 4 +#define NV4_PGRAPH_FFINTFC_FIFO_PTR_READ_0 0x0 +#define NV4_PGRAPH_FFINTFC_ST2 0x400754 +#define NV4_PGRAPH_FFINTFC_ST2_STATUS 0 +#define NV4_PGRAPH_FFINTFC_ST2_STATUS_INVALID 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_STATUS_VALID 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_MTHD 1 +#define NV4_PGRAPH_FFINTFC_ST2_MTHD_CTX_SWITCH 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH 12 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_0 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_1 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_2 0x2 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_3 0x3 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_4 0x4 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_5 0x5 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_6 0x6 +#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_7 0x7 +#define NV4_PGRAPH_FFINTFC_ST2_CHID 15 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_0 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_1 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_2 0x2 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_3 0x3 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_4 0x4 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_5 0x5 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_6 0x6 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_7 0x7 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_8 0x8 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_9 0x9 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_10 0xA +#define NV4_PGRAPH_FFINTFC_ST2_CHID_11 0xB +#define NV4_PGRAPH_FFINTFC_ST2_CHID_12 0xC +#define NV4_PGRAPH_FFINTFC_ST2_CHID_13 0xD +#define NV4_PGRAPH_FFINTFC_ST2_CHID_14 0xE +#define NV4_PGRAPH_FFINTFC_ST2_CHID_15 0xF +#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS 19 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS_INVALID 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS_VALID 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT 20 +#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT_CLEAR 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT_SET 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD 21 +#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD_CLEAR 0x0 +#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD_SET 0x1 +#define NV4_PGRAPH_FFINTFC_ST2_D 0x400758 +#define NV4_PGRAPH_FFINTFC_ST2_D_ARGUMENT 0 +#define NV4_PGRAPH_FFINTFC_ST2_D_ARGUMENT_0 0x0 +#define NV4_PGRAPH_STATUS 0x400700 +#define NV4_PGRAPH_STATUS_STATE 0 +#define NV4_PGRAPH_STATUS_STATE_IDLE 0x0 +#define NV4_PGRAPH_STATUS_STATE_BUSY 0x1 +#define NV4_PGRAPH_STATUS_XY_LOGIC 4 +#define NV4_PGRAPH_STATUS_XY_LOGIC_IDLE 0x0 +#define NV4_PGRAPH_STATUS_XY_LOGIC_BUSY 0x1 +#define NV4_PGRAPH_STATUS_FE 5 +#define NV4_PGRAPH_STATUS_FE_IDLE 0x0 +#define NV4_PGRAPH_STATUS_FE_BUSY 0x1 +#define NV4_PGRAPH_STATUS_RASTERIZER 6 +#define NV4_PGRAPH_STATUS_RASTERIZER_IDLE 0x0 +#define NV4_PGRAPH_STATUS_RASTERIZER_BUSY 0x1 +#define NV4_PGRAPH_STATUS_PORT_NOTIFY 8 +#define NV4_PGRAPH_STATUS_PORT_NOTIFY_IDLE 0x0 +#define NV4_PGRAPH_STATUS_PORT_NOTIFY_BUSY 0x1 +#define NV4_PGRAPH_STATUS_PORT_REGISTER 12 +#define NV4_PGRAPH_STATUS_PORT_REGISTER_IDLE 0x0 +#define NV4_PGRAPH_STATUS_PORT_REGISTER_BUSY 0x1 +#define NV4_PGRAPH_STATUS_PORT_DMA 16 +#define NV4_PGRAPH_STATUS_PORT_DMA_IDLE 0x0 +#define NV4_PGRAPH_STATUS_PORT_DMA_BUSY 0x1 +#define NV4_PGRAPH_STATUS_DMA_ENGINE 17 +#define NV4_PGRAPH_STATUS_DMA_ENGINE_IDLE 0x0 +#define NV4_PGRAPH_STATUS_DMA_ENGINE_BUSY 0x1 +#define NV4_PGRAPH_STATUS_DMA_NOTIFY 20 +#define NV4_PGRAPH_STATUS_DMA_NOTIFY_IDLE 0x0 +#define NV4_PGRAPH_STATUS_DMA_NOTIFY_BUSY 0x1 +#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY 21 +#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY_IDLE 0x0 +#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY_BUSY 0x1 +#define NV4_PGRAPH_STATUS_D3D 24 +#define NV4_PGRAPH_STATUS_D3D_IDLE 0x0 +#define NV4_PGRAPH_STATUS_D3D_BUSY 0x1 +#define NV4_PGRAPH_STATUS_CACHE 25 +#define NV4_PGRAPH_STATUS_CACHE_IDLE 0x0 +#define NV4_PGRAPH_STATUS_CACHE_BUSY 0x1 +#define NV4_PGRAPH_STATUS_LIGHTING 26 +#define NV4_PGRAPH_STATUS_LIGHTING_IDLE 0x0 +#define NV4_PGRAPH_STATUS_LIGHTING_BUSY 0x1 +#define NV4_PGRAPH_STATUS_PREROP 27 +#define NV4_PGRAPH_STATUS_PREROP_IDLE 0x0 +#define NV4_PGRAPH_STATUS_PREROP_BUSY 0x1 +#define NV4_PGRAPH_STATUS_ROP 28 +#define NV4_PGRAPH_STATUS_ROP_IDLE 0x0 +#define NV4_PGRAPH_STATUS_ROP_BUSY 0x1 +#define NV4_PGRAPH_STATUS_PORT_USER 29 +#define NV4_PGRAPH_STATUS_PORT_USER_IDLE 0x0 +#define NV4_PGRAPH_STATUS_PORT_USER_BUSY 0x1 +#define NV4_PGRAPH_TRAPPED_ADDR 0x400704 +#define NV4_PGRAPH_TRAPPED_ADDR_MTHD 2 +#define NV4_PGRAPH_TRAPPED_ADDR_SUBCH 13 +#define NV4_PGRAPH_TRAPPED_ADDR_CHID 24 +#define NV4_PGRAPH_TRAPPED_DATA 0x400708 +#define NV4_PGRAPH_TRAPPED_DATA_VALUE 0 +#define NV4_PGRAPH_SURFACE 0x40070C +#define NV4_PGRAPH_SURFACE_TYPE 0 +#define NV4_PGRAPH_SURFACE_TYPE_INVALID 0x0 +#define NV4_PGRAPH_SURFACE_TYPE_NON_SWIZZLE 0x1 +#define NV4_PGRAPH_SURFACE_TYPE_SWIZZLE 0x2 +#define NV4_PGRAPH_NOTIFY 0x400714 +#define NV4_PGRAPH_NOTIFY_BUFFER_REQ 0 +#define NV4_PGRAPH_NOTIFY_BUFFER_REQ_NOT_PENDING 0x0 +#define NV4_PGRAPH_NOTIFY_BUFFER_REQ_PENDING 0x1 +#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE 8 +#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE_WRITE_ONLY 0x0 +#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE_WRITE_THEN_AWAKEN 0x1 +#define NV4_PGRAPH_NOTIFY_REQ 16 +#define NV4_PGRAPH_NOTIFY_REQ_NOT_PENDING 0x0 +#define NV4_PGRAPH_NOTIFY_REQ_PENDING 0x1 +#define NV4_PGRAPH_NOTIFY_STYLE 20 +#define NV4_PGRAPH_NOTIFY_STYLE_WRITE_ONLY 0x0 +#define NV4_PGRAPH_NOTIFY_STYLE_WRITE_THEN_AWAKEN 0x1 +#define NV4_PGRAPH_BOFFSET(i) (0x400640+(i)*4) +//used for all +#define NV4_PGRAPH_BOFFSET_LINADRS_DEFAULT 0x0 +#define NV4_PGRAPH_BOFFSET_SIZE_1 6 +#define NV4_PGRAPH_BOFFSET_LINADRS 0 +#define NV4_PGRAPH_BOFFSET_LINADRS_0 0x0 +#define NV4_PGRAPH_BOFFSET0 0x400640 +#define NV4_PGRAPH_BOFFSET0_ALIAS_1 NV_PGRAPH_BOFFSET(0) +#define NV4_PGRAPH_BOFFSET0_LINADRS 0 +#define NV4_PGRAPH_BOFFSET1 0x400644 +#define NV4_PGRAPH_BOFFSET1_ALIAS_1 NV_PGRAPH_BOFFSET(1) +#define NV4_PGRAPH_BOFFSET1_LINADRS 0 +#define NV4_PGRAPH_BOFFSET2 0x400648 +#define NV4_PGRAPH_BOFFSET2_ALIAS_1 NV_PGRAPH_BOFFSET(2) +#define NV4_PGRAPH_BOFFSET2_LINADRS 0 +#define NV4_PGRAPH_BOFFSET3 0x40064C +#define NV4_PGRAPH_BOFFSET3_ALIAS_1 NV_PGRAPH_BOFFSET(3) +#define NV4_PGRAPH_BOFFSET3_LINADRS 0 +#define NV4_PGRAPH_BOFFSET4 0x400650 +#define NV4_PGRAPH_BOFFSET4_ALIAS_1 NV_PGRAPH_BOFFSET(4) +#define NV4_PGRAPH_BOFFSET4_LINADRS 0 +#define NV4_PGRAPH_BOFFSET5 0x400654 +#define NV4_PGRAPH_BOFFSET5_ALIAS_1 NV_PGRAPH_BOFFSET(5) +#define NV4_PGRAPH_BOFFSET5_LINADRS 0 +#define NV4_PGRAPH_BBASE(i) (0x400658+(i)*4) +//used for all +#define NV4_PGRAPH_BBASE_LINADRS_DEFAULT 0x0 +#define NV4_PGRAPH_BBASE_SIZE_1 6 +#define NV4_PGRAPH_BBASE_LINADRS 0 +#define NV4_PGRAPH_BBASE0 0x400658 +#define NV4_PGRAPH_BBASE0_ALIAS_1 NV_PGRAPH_BBASE(0) +#define NV4_PGRAPH_BBASE0_LINADRS 0 +#define NV4_PGRAPH_BBASE1 0x40065c +#define NV4_PGRAPH_BBASE1_ALIAS_1 NV_PGRAPH_BBASE(1) +#define NV4_PGRAPH_BBASE1_LINADRS 0 +#define NV4_PGRAPH_BBASE2 0x400660 +#define NV4_PGRAPH_BBASE2_ALIAS_1 NV_PGRAPH_BBASE(2) +#define NV4_PGRAPH_BBASE2_LINADRS 0 +#define NV4_PGRAPH_BBASE3 0x400664 +#define NV4_PGRAPH_BBASE3_ALIAS_1 NV_PGRAPH_BBASE(3) +#define NV4_PGRAPH_BBASE3_LINADRS 0 +#define NV4_PGRAPH_BBASE4 0x400668 +#define NV4_PGRAPH_BBASE4_ALIAS_1 NV_PGRAPH_BBASE(4) +#define NV4_PGRAPH_BBASE4_LINADRS 0 +#define NV4_PGRAPH_BBASE5 0x40066C +#define NV4_PGRAPH_BBASE5_ALIAS_1 NV_PGRAPH_BBASE(5) +#define NV4_PGRAPH_BBASE5_LINADRS 0 +#define NV4_PGRAPH_BPITCH(i) (0x400670+(i)*4) +//used for all +#define NV4_PGRAPH_BPITCH_LINADRS_DEFAULT 0x0 +#define NV4_PGRAPH_BPITCH_SIZE_1 5 +#define NV4_PGRAPH_BPITCH_VALUE 0 // use bit 0 for all +#define NV4_PGRAPH_BPITCH_VALUE_0 0x0 +#define NV4_PGRAPH_BPITCH0 0x400670 +#define NV4_PGRAPH_BPITCH0_ALIAS_1 NV_PGRAPH_BPITCH(0) +#define NV4_PGRAPH_BPITCH1 0x400674 +#define NV4_PGRAPH_BPITCH1_ALIAS_1 NV_PGRAPH_BPITCH(1) +#define NV4_PGRAPH_BPITCH2 0x400678 +#define NV4_PGRAPH_BPITCH2_ALIAS_1 NV_PGRAPH_BPITCH(2) +#define NV4_PGRAPH_BPITCH3 0x40067C +#define NV4_PGRAPH_BPITCH3_ALIAS_1 NV_PGRAPH_BPITCH(3) +#define NV4_PGRAPH_BPITCH4 0x400680 +#define NV4_PGRAPH_BPITCH4_ALIAS_1 NV_PGRAPH_BPITCH(4) +#define NV4_PGRAPH_BLIMIT(i) (0x400684+(i)*4) +//following two used for all +#define NV4_PGRAPH_BLIMIT_SIZE_1 6 +#define NV4_PGRAPH_BLIMIT_VALUE 0 +#define NV4_PGRAPH_BLIMIT_TYPE 31 +#define NV4_PGRAPH_BLIMIT_TYPE_IN_MEMORY 0x0 +#define NV4_PGRAPH_BLIMIT_TYPE_NULL 0x1 +#define NV4_PGRAPH_BLIMIT0 0x400684 +#define NV4_PGRAPH_BLIMIT0_ALIAS_1 NV_PGRAPH_BLIMIT(0) +#define NV4_PGRAPH_BLIMIT1 0x400688 +#define NV4_PGRAPH_BLIMIT1_ALIAS_1 NV_PGRAPH_BLIMIT(1) +#define NV4_PGRAPH_BLIMIT2 0x40068c +#define NV4_PGRAPH_BLIMIT2_ALIAS_1 NV_PGRAPH_BLIMIT(2) +#define NV4_PGRAPH_BLIMIT3 0x400690 +#define NV4_PGRAPH_BLIMIT3_ALIAS_1 NV_PGRAPH_BLIMIT(3) +#define NV4_PGRAPH_BLIMIT4 0x400694 +#define NV4_PGRAPH_BLIMIT4_ALIAS_1 NV_PGRAPH_BLIMIT(4) +#define NV4_PGRAPH_BLIMIT5 0x400698 +#define NV4_PGRAPH_BLIMIT5_ALIAS_1 NV_PGRAPH_BLIMIT(5) +#define NV4_PGRAPH_BSWIZZLE2 0x40069c +#define NV4_PGRAPH_BSWIZZLE2_WIDTH 16 +#define NV4_PGRAPH_BSWIZZLE2_WIDTH_0 0x0 +#define NV4_PGRAPH_BSWIZZLE2_HEIGHT 24 +#define NV4_PGRAPH_BSWIZZLE2_HEIGHT_0 0x0 +#define NV4_PGRAPH_BSWIZZLE5 0x4006a0 +#define NV4_PGRAPH_BSWIZZLE5_WIDTH 16 +#define NV4_PGRAPH_BSWIZZLE5_WIDTH_0 0x0 +#define NV4_PGRAPH_BSWIZZLE5_HEIGHT 24 +#define NV4_PGRAPH_BSWIZZLE5_HEIGHT_0 0x0 +#define NV4_PGRAPH_BPIXEL 0x400724 +#define NV4_PGRAPH_BPIXEL_DEPTH0 0 +#define NV4_PGRAPH_BPIXEL_DEPTH1 4 +#define NV4_PGRAPH_BPIXEL_DEPTH2 8 +#define NV4_PGRAPH_BPIXEL_DEPTH3 12 +#define NV4_PGRAPH_BPIXEL_DEPTH4 16 +#define NV4_PGRAPH_BPIXEL_DEPTH5 20 + +// valid for depth0-depth5 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_INVALID 0x0 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y8 0x1 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1R5G5B5_Z1R5G5B5 0x2 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1R5G5B5_O1R5G5B5 0x3 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_A1R5G5B5 0x4 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_R5G6B5 0x5 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y16 0x6 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_Z8R8G8B8 0x7 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_O1Z7R8G8B8 0x8 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1A7R8G8B8_Z1A7R8G8B8 0x9 +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1A7R8G8B8_O1A7R8G8B8 0xA +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_O8R8G8B8 0xB +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_A8R8G8B8 0xC +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y32 0xD +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_V8YB8U8YA8 0xE +#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_YB8V8YA8U8 0xF + +#define NV4_PGRAPH_LIMIT_VIOL_PIX 0x400610 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_ADRS 0 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_ADRS_0 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT 29 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT_NO_VIOL 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT_VIOL 0x1 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT 30 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT_NO_VIOL 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT_VIOL 0x1 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW 31 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW_NO_VIOL 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW_VIOL 0x1 +#define NV4_PGRAPH_LIMIT_VIOL_Z 0x400614 +#define NV4_PGRAPH_LIMIT_VIOL_Z_ADRS 0 +#define NV4_PGRAPH_LIMIT_VIOL_Z_ADRS_0 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT 30 +#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT_NO_VIOL 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT_VIOL 0x1 +#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW 31 +#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW_NO_VIOL 0x0 +#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW_VIOL 0x1 +#define NV4_PGRAPH_STATE 0x400710 +#define NV4_PGRAPH_STATE_BUFFER_0 0 +#define NV4_PGRAPH_STATE_BUFFER_0_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_0_VALID 0x1 +#define NV4_PGRAPH_STATE_BUFFER_1 1 +#define NV4_PGRAPH_STATE_BUFFER_1_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_1_VALID 0x1 +#define NV4_PGRAPH_STATE_BUFFER_2 2 +#define NV4_PGRAPH_STATE_BUFFER_2_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_2_VALID 0x1 +#define NV4_PGRAPH_STATE_BUFFER_3 3 +#define NV4_PGRAPH_STATE_BUFFER_3_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_3_VALID 0x1 +#define NV4_PGRAPH_STATE_BUFFER_4 4 +#define NV4_PGRAPH_STATE_BUFFER_4_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_4_VALID 0x1 +#define NV4_PGRAPH_STATE_BUFFER_5 5 +#define NV4_PGRAPH_STATE_BUFFER_5_INVALID 0x0 +#define NV4_PGRAPH_STATE_BUFFER_5_VALID 0x1 +#define NV4_PGRAPH_STATE_PITCH_0 8 +#define NV4_PGRAPH_STATE_PITCH_0_INVALID 0x0 +#define NV4_PGRAPH_STATE_PITCH_0_VALID 0x1 +#define NV4_PGRAPH_STATE_PITCH_1 9 +#define NV4_PGRAPH_STATE_PITCH_1_INVALID 0x0 +#define NV4_PGRAPH_STATE_PITCH_1_VALID 0x1 +#define NV4_PGRAPH_STATE_PITCH_2 10 +#define NV4_PGRAPH_STATE_PITCH_2_INVALID 0x0 +#define NV4_PGRAPH_STATE_PITCH_2_VALID 0x1 +#define NV4_PGRAPH_STATE_PITCH_3 11 +#define NV4_PGRAPH_STATE_PITCH_3_INVALID 0x0 +#define NV4_PGRAPH_STATE_PITCH_3_VALID 0x1 +#define NV4_PGRAPH_STATE_PITCH_4 12 +#define NV4_PGRAPH_STATE_PITCH_4_INVALID 0x0 +#define NV4_PGRAPH_STATE_PITCH_4_VALID 0x1 +#define NV4_PGRAPH_STATE_CHROMA_COLOR 16 +#define NV4_PGRAPH_STATE_CHROMA_COLOR_INVALID 0x0 +#define NV4_PGRAPH_STATE_CHROMA_COLOR_VALID 0x1 +#define NV4_PGRAPH_STATE_CHROMA_COLORFMT 17 +#define NV4_PGRAPH_STATE_CHROMA_COLORFMT_INVALID 0x0 +#define NV4_PGRAPH_STATE_CHROMA_COLORFMT_VALID 0x1 +#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT 20 +#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT_INVALID 0x0 +#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT_VALID 0x1 +#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT 21 +#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT_INVALID 0x0 +#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT_VALID 0x1 +#define NV4_PGRAPH_STATE_CPATTERN_SELECT 22 +#define NV4_PGRAPH_STATE_CPATTERN_SELECT_INVALID 0x0 +#define NV4_PGRAPH_STATE_CPATTERN_SELECT_VALID 0x1 +#define NV4_PGRAPH_STATE_PATTERN_COLOR0 24 +#define NV4_PGRAPH_STATE_PATTERN_COLOR0_INVALID 0x0 +#define NV4_PGRAPH_STATE_PATTERN_COLOR0_VALID 0x1 +#define NV4_PGRAPH_STATE_PATTERN_COLOR1 25 +#define NV4_PGRAPH_STATE_PATTERN_COLOR1_INVALID 0x0 +#define NV4_PGRAPH_STATE_PATTERN_COLOR1_VALID 0x1 +#define NV4_PGRAPH_STATE_PATTERN_PATT0 26 +#define NV4_PGRAPH_STATE_PATTERN_PATT0_INVALID 0x0 +#define NV4_PGRAPH_STATE_PATTERN_PATT0_VALID 0x1 +#define NV4_PGRAPH_STATE_PATTERN_PATT1 27 +#define NV4_PGRAPH_STATE_PATTERN_PATT1_INVALID 0x0 +#define NV4_PGRAPH_STATE_PATTERN_PATT1_VALID 0x1 +#define NV4_PGRAPH_CACHE_INDEX 0x400728 +#define NV4_PGRAPH_CACHE_INDEX_BANK 2 +#define NV4_PGRAPH_CACHE_INDEX_BANK_10 0x0 +#define NV4_PGRAPH_CACHE_INDEX_BANK_32 0x1 +#define NV4_PGRAPH_CACHE_INDEX_ADRS 3 +#define NV4_PGRAPH_CACHE_INDEX_ADRS_0 0x0 +#define NV4_PGRAPH_CACHE_INDEX_ADRS_1024 0x400 +#define NV4_PGRAPH_CACHE_INDEX_OP 13 +#define NV4_PGRAPH_CACHE_INDEX_OP_WR_CACHE 0x0 +#define NV4_PGRAPH_CACHE_INDEX_OP_RD_CACHE 0x1 +#define NV4_PGRAPH_CACHE_INDEX_OP_RD_INDEX 0x2 +#define NV4_PGRAPH_CACHE_RAM 0x40072c +#define NV4_PGRAPH_CACHE_RAM_VALUE 0 +#define NV4_PGRAPH_DMA_PITCH 0x400760 +#define NV4_PGRAPH_DMA_PITCH_S0 0 +#define NV4_PGRAPH_DMA_PITCH_S1 16 +#define NV4_PGRAPH_DVD_COLORFMT 0x400764 +#define NV4_PGRAPH_DVD_COLORFMT_IMAGE 0 +#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_INVALID 0x00 +#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_LE_V8YB8U8YA8 0x12 +#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_LE_YB8V8YA8U8 0x13 +#define NV4_PGRAPH_DVD_COLORFMT_OVLY 8 +#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_INVALID 0x00 +#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_LE_A8Y8U8V8 0x01 +#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_LE_A4V6YB6A4U6YA6 0x02 +#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_TRANSPARENT 0x03 +#define NV4_PGRAPH_SCALED_FORMAT 0x400768 +#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN 16 +#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_INVALID 0x0 +#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_CENTER 0x1 +#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_CORNER 0x2 +#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR 24 +#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR_ZOH 0x0 +#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR_FOH 0x1 +#define NV4_PGRAPH_PATT_COLOR0 0x400800 +#define NV4_PGRAPH_PATT_COLOR0_VALUE 0 +#define NV4_PGRAPH_PATT_COLOR1 0x400804 +#define NV4_PGRAPH_PATT_COLOR1_VALUE 0 +#define NV4_PGRAPH_PATT_COLORRAM(i) (0x400900+(i)*4) +#define NV4_PGRAPH_PATT_COLORRAM_SIZE_1 64 +#define NV4_PGRAPH_PATT_COLORRAM_VALUE 0 +#define NV4_PGRAPH_PATTERN(i) (0x400808+(i)*4) +#define NV4_PGRAPH_PATTERN_SIZE_1 2 +#define NV4_PGRAPH_PATTERN_BITMAP 0 +#define NV4_PGRAPH_PATTERN_SHAPE 0x400810 +#define NV4_PGRAPH_PATTERN_SHAPE_VALUE 0 +#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_8X_8Y 0x0 +#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_64X_1Y 0x1 +#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_1X_64Y 0x2 +#define NV4_PGRAPH_PATTERN_SHAPE_SELECT 4 +#define NV4_PGRAPH_PATTERN_SHAPE_SELECT_2COLOR 0x0 +#define NV4_PGRAPH_PATTERN_SHAPE_SELECT_FULLCOLOR 0x1 +#define NV4_PGRAPH_MONO_COLOR0 0x400600 +#define NV4_PGRAPH_MONO_COLOR0_VALUE 0 +#define NV4_PGRAPH_ROP3 0x400604 +#define NV4_PGRAPH_ROP3_VALUE 0 +#define NV4_PGRAPH_CHROMA 0x400814 +#define NV4_PGRAPH_CHROMA_VALUE 0 +#define NV4_PGRAPH_BETA_AND 0x400608 +#define NV4_PGRAPH_BETA_AND_VALUE_FRACTION 23 +#define NV4_PGRAPH_BETA_PREMULT 0x40060c +#define NV4_PGRAPH_BETA_PREMULT_VALUE 0 +#define NV4_PGRAPH_CONTROL0 0x400818 +#define NV4_PGRAPH_CONTROL0_ALPHAREF 0 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC 8 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_NEVER 0x1 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_LESS 0x2 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_EQUAL 0x3 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_LESSEQUAL 0x4 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_GREATER 0x5 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_NOTEQUAL 0x6 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_GREATEREQUAL 0x7 +#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_ALWAYS 0x8 +#define NV4_PGRAPH_CONTROL0_ALPHATESTENABLE 12 +#define NV4_PGRAPH_CONTROL0_ALPHATESTENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_ZENABLE 14 +#define NV4_PGRAPH_CONTROL0_ZENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_ZENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_ZFUNC 16 +#define NV4_PGRAPH_CONTROL0_ZFUNC_NEVER 0x1 +#define NV4_PGRAPH_CONTROL0_ZFUNC_LESS 0x2 +#define NV4_PGRAPH_CONTROL0_ZFUNC_EQUAL 0x3 +#define NV4_PGRAPH_CONTROL0_ZFUNC_LESSEQUAL 0x4 +#define NV4_PGRAPH_CONTROL0_ZFUNC_GREATER 0x5 +#define NV4_PGRAPH_CONTROL0_ZFUNC_NOTEQUAL 0x6 +#define NV4_PGRAPH_CONTROL0_ZFUNC_GREATEREQUAL 0x7 +#define NV4_PGRAPH_CONTROL0_ZFUNC_ALWAYS 0x8 +#define NV4_PGRAPH_CONTROL0_CULLMODE 20 +#define NV4_PGRAPH_CONTROL0_CULLMODE_NONE 0x1 +#define NV4_PGRAPH_CONTROL0_CULLMODE_CW 0x2 +#define NV4_PGRAPH_CONTROL0_CULLMODE_CCW 0x3 +#define NV4_PGRAPH_CONTROL0_DITHERENABLE 22 +#define NV4_PGRAPH_CONTROL0_DITHERENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_DITHERENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE 23 +#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE 24 +#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE 25 +#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE 26 +#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE 27 +#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE 28 +#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE 29 +#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL0_Z_FORMAT 30 +#define NV4_PGRAPH_CONTROL0_Z_FORMAT_FIXED 0x1 +#define NV4_PGRAPH_CONTROL0_Z_FORMAT_FLOAT 0x2 +#define NV4_PGRAPH_CONTROL1 0x40081c +#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE 0 +#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC 4 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_NEVER 0x1 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_LESS 0x2 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_EQUAL 0x3 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_LESSEQUAL 0x4 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_GREATER 0x5 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_NOTEQUAL 0x6 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_GREATEREQUAL 0x7 +#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_ALWAYS 0x8 +#define NV4_PGRAPH_CONTROL1_STENCIL_REF 8 +#define NV4_PGRAPH_CONTROL1_STENCIL_MASK_READ 16 +#define NV4_PGRAPH_CONTROL1_STENCIL_MASK_WRITE 24 +#define NV4_PGRAPH_CONTROL2 0x400820 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL 0 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_KEEP 0x1 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_ZERO 0x2 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_REPLACE 0x3 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INCRSAT 0x4 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_DECRSAT 0x5 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INVERT 0x6 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INCR 0x7 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_DECR 0x8 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL 4 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_KEEP 0x1 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_ZERO 0x2 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_REPLACE 0x3 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INCRSAT 0x4 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_DECRSAT 0x5 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INVERT 0x6 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INCR 0x7 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_DECR 0x8 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS 8 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_KEEP 0x1 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_ZERO 0x2 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_REPLACE 0x3 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INCRSAT 0x4 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_DECRSAT 0x5 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INVERT 0x6 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INCR 0x7 +#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_DECR 0x8 +#define NV4_PGRAPH_BLEND 0x400824 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND 0 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECAL 0x1 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATE 0x2 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECALALPHA 0x3 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATEALPHA 0x4 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECALMASK 0x5 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATEMASK 0x6 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_COPY 0x7 +#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_ADD 0x8 +#define NV4_PGRAPH_BLEND_MASK_BIT 4 +#define NV4_PGRAPH_BLEND_MASK_BIT_LSB 0x1 +#define NV4_PGRAPH_BLEND_MASK_BIT_MSB 0x2 +#define NV4_PGRAPH_BLEND_SHADEMODE 6 +#define NV4_PGRAPH_BLEND_SHADEMODE_FLAT 0x1 +#define NV4_PGRAPH_BLEND_SHADEMODE_GOURAUD 0x2 +#define NV4_PGRAPH_BLEND_SHADEMODE_PHONG 0x3 +#define NV4_PGRAPH_BLEND_TEXTUREPERSPECTIVE 8 +#define NV4_PGRAPH_BLEND_TEXTUREPERSPECTIVE_TRUE 0x1 +#define NV4_PGRAPH_BLEND_SPECULARENABLE 12 +#define NV4_PGRAPH_BLEND_SPECULARENABLE_TRUE 0x1 +#define NV4_PGRAPH_BLEND_FOGENABLE 16 +#define NV4_PGRAPH_BLEND_FOGENABLE_TRUE 0x1 +#define NV4_PGRAPH_BLEND_ALPHABLENDENABLE 20 +#define NV4_PGRAPH_BLEND_ALPHABLENDENABLE_TRUE 0x1 +#define NV4_PGRAPH_BLEND_SRCBLEND 24 +#define NV4_PGRAPH_BLEND_SRCBLEND_ZERO 0x1 +#define NV4_PGRAPH_BLEND_SRCBLEND_ONE 0x2 +#define NV4_PGRAPH_BLEND_SRCBLEND_SRCCOLOR 0x3 +#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCCOLOR 0x4 +#define NV4_PGRAPH_BLEND_SRCBLEND_SRCALPHA 0x5 +#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCALPHA 0x6 +#define NV4_PGRAPH_BLEND_SRCBLEND_DESTALPHA 0x7 +#define NV4_PGRAPH_BLEND_SRCBLEND_INVDESTALPHA 0x8 +#define NV4_PGRAPH_BLEND_SRCBLEND_DESTCOLOR 0x9 +#define NV4_PGRAPH_BLEND_SRCBLEND_INVDESTCOLOR 0xA +#define NV4_PGRAPH_BLEND_SRCBLEND_SRCALPHASAT 0xB +#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCALPHASAT 0xC +#define NV4_PGRAPH_BLEND_SRCBLEND_BETA 0xD +#define NV4_PGRAPH_BLEND_DESTBLEND 28 +#define NV4_PGRAPH_BLEND_DESTBLEND_ZERO 0x1 +#define NV4_PGRAPH_BLEND_DESTBLEND_ONE 0x2 +#define NV4_PGRAPH_BLEND_DESTBLEND_SRCCOLOR 0x3 +#define NV4_PGRAPH_BLEND_DESTBLEND_INVSRCCOLOR 0x4 +#define NV4_PGRAPH_BLEND_DESTBLEND_SRCALPHA 0x5 +#define NV4_PGRAPH_BLEND_DESTBLEND_INVSRCALPHA 0x6 +#define NV4_PGRAPH_BLEND_DESTBLEND_DESTALPHA 0x7 +#define NV4_PGRAPH_BLEND_DESTBLEND_INVDESTALPHA 0x8 +#define NV4_PGRAPH_BLEND_DESTBLEND_DESTCOLOR 0x9 +#define NV4_PGRAPH_BLEND_DESTBLEND_INVDESTCOLOR 0xA +#define NV4_PGRAPH_BLEND_DESTBLEND_SRCALPHASAT 0xB +#define NV4_PGRAPH_DPRAM_INDEX 0x400828 +#define NV4_PGRAPH_DPRAM_INDEX_ADRS 0 +#define NV4_PGRAPH_DPRAM_INDEX_ADRS_0 0x0 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT 8 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ADRS_0 0x0 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ADRS_1 0x1 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_DATA_0 0x2 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_DATA_1 0x3 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_WE_0 0x4 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_WE_1 0x5 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ALPHA_0 0x6 +#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ALPHA_1 0x7 +#define NV4_PGRAPH_DPRAM_DATA 0x40082c +#define NV4_PGRAPH_DPRAM_DATA_VALUE 0 +#define NV4_PGRAPH_DPRAM_ADRS_0 0x40082c +#define NV4_PGRAPH_DPRAM_ADRS_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_ADRS_0_VALUE 0 +#define NV4_PGRAPH_DPRAM_ADRS_1 0x40082c +#define NV4_PGRAPH_DPRAM_ADRS_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_ADRS_1_VALUE 0 +#define NV4_PGRAPH_DPRAM_DATA_0 0x40082c +#define NV4_PGRAPH_DPRAM_DATA_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_DATA_0_VALUE 0 +#define NV4_PGRAPH_DPRAM_DATA_1 0x40082c +#define NV4_PGRAPH_DPRAM_DATA_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_DATA_1_VALUE 0 +#define NV4_PGRAPH_DPRAM_WE_0 0x40082c +#define NV4_PGRAPH_DPRAM_WE_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_WE_0_VALUE 0 +#define NV4_PGRAPH_DPRAM_WE_1 0x40082c +#define NV4_PGRAPH_DPRAM_WE_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_WE_1_VALUE 0 +#define NV4_PGRAPH_DPRAM_ALPHA_0 0x40082c +#define NV4_PGRAPH_DPRAM_ALPHA_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_ALPHA_0_VALUE 0 +#define NV4_PGRAPH_DPRAM_ALPHA_1 0x40082c +#define NV4_PGRAPH_DPRAM_ALPHA_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA +#define NV4_PGRAPH_DPRAM_ALPHA_1_VALUE 0 +#define NV4_PGRAPH_STORED_FMT 0x400830 +#define NV4_PGRAPH_STORED_FMT_MONO0 0 +#define NV4_PGRAPH_STORED_FMT_PATT0 8 +#define NV4_PGRAPH_STORED_FMT_PATT1 16 +#define NV4_PGRAPH_STORED_FMT_CHROMA 24 +#define NV4_PGRAPH_FORMATS 0x400618 +#define NV4_PGRAPH_FORMATS_ROP 0 +#define NV4_PGRAPH_FORMATS_ROP_Y8 0x0 +#define NV4_PGRAPH_FORMATS_ROP_RGB15 0x1 +#define NV4_PGRAPH_FORMATS_ROP_RGB16 0x2 +#define NV4_PGRAPH_FORMATS_ROP_Y16 0x3 +#define NV4_PGRAPH_FORMATS_ROP_INVALID 0x4 +#define NV4_PGRAPH_FORMATS_ROP_RGB24 0x5 +#define NV4_PGRAPH_FORMATS_ROP_RGB30 0x6 +#define NV4_PGRAPH_FORMATS_ROP_Y32 0x7 +#define NV4_PGRAPH_FORMATS_SRC 4 +#define NV4_PGRAPH_FORMATS_SRC_INVALID 0x0 +#define NV4_PGRAPH_FORMATS_SRC_LE_Y8 0x1 +#define NV4_PGRAPH_FORMATS_SRC_LE_X16A8Y8 0x2 +#define NV4_PGRAPH_FORMATS_SRC_LE_X24Y8 0x3 +#define NV4_PGRAPH_FORMATS_SRC_LE_A1R5G5B5 0x6 +#define NV4_PGRAPH_FORMATS_SRC_LE_X1R5G5B5 0x7 +#define NV4_PGRAPH_FORMATS_SRC_LE_X16A1R5G5B5 0x8 +#define NV4_PGRAPH_FORMATS_SRC_LE_X17R5G5B5 0x9 +#define NV4_PGRAPH_FORMATS_SRC_LE_R5G6B5 0xA +#define NV4_PGRAPH_FORMATS_SRC_LE_A16R5G6B5 0xB +#define NV4_PGRAPH_FORMATS_SRC_LE_X16R5G6B5 0xC +#define NV4_PGRAPH_FORMATS_SRC_LE_A8R8G8B8 0xD +#define NV4_PGRAPH_FORMATS_SRC_LE_X8R8G8B8 0xE +#define NV4_PGRAPH_FORMATS_SRC_LE_Y16 0xF +#define NV4_PGRAPH_FORMATS_SRC_LE_A16Y16 0x10 +#define NV4_PGRAPH_FORMATS_SRC_LE_X16Y16 0x11 +#define NV4_PGRAPH_FORMATS_SRC_LE_V8YB8U8YA8 0x12 +#define NV4_PGRAPH_FORMATS_SRC_LE_YB8V8YA8U8 0x13 +#define NV4_PGRAPH_FORMATS_SRC_LE_Y32 0x14 +#define NV4_PGRAPH_FORMATS_FB 12 +#define NV4_PGRAPH_FORMATS_FB_INVALID 0x0 +#define NV4_PGRAPH_FORMATS_FB_Y8 0x1 +#define NV4_PGRAPH_FORMATS_FB_X1R5G5B5_Z1R5G5B5 0x2 +#define NV4_PGRAPH_FORMATS_FB_X1R5G5B5_O1R5G5B5 0x3 +#define NV4_PGRAPH_FORMATS_FB_A1R5G5B5 0x4 +#define NV4_PGRAPH_FORMATS_FB_R5G6B5 0x5 +#define NV4_PGRAPH_FORMATS_FB_Y16 0x6 +#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_Z8R8G8B8 0x7 +#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_O1Z7R8G8B8 0x8 +#define NV4_PGRAPH_FORMATS_FB_X1A7R8G8B8_Z1A7R8G8B8 0x9 +#define NV4_PGRAPH_FORMATS_FB_X1A7R8G8B8_O1A7R8G8B8 0xA +#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_O8R8G8B8 0xB +#define NV4_PGRAPH_FORMATS_FB_A8R8G8B8 0xC +#define NV4_PGRAPH_FORMATS_FB_Y32 0xD +#define NV4_PGRAPH_FORMATS_FB_V8YB8U8YA8 0xE +#define NV4_PGRAPH_FORMATS_FB_YB8V8YA8U8 0xF +#define NV4_PGRAPH_ABS_X_RAM(i) (0x400400+(i)*4) +#define NV4_PGRAPH_ABS_X_RAM_SIZE_1 32 +#define NV4_PGRAPH_ABS_X_RAM_VALUE 0 +#define NV4_PGRAPH_X_RAM_BPORT(i) (0x400c00+(i)*4) +#define NV4_PGRAPH_X_RAM_BPORT_SIZE_1 32 +#define NV4_PGRAPH_X_RAM_BPORT_VALUE 0 +#define NV4_PGRAPH_ABS_Y_RAM(i) (0x400480+(i)*4) +#define NV4_PGRAPH_ABS_Y_RAM_SIZE_1 32 +#define NV4_PGRAPH_ABS_Y_RAM_VALUE 0 +#define NV4_PGRAPH_Y_RAM_BPORT(i) (0x400c80+(i)*4) +#define NV4_PGRAPH_Y_RAM_BPORT_SIZE_1 32 +#define NV4_PGRAPH_Y_RAM_BPORT_VALUE 0 +#define NV4_PGRAPH_XY_LOGIC_MISC0 0x400514 +#define NV4_PGRAPH_XY_LOGIC_MISC0_COUNTER 0 +#define NV4_PGRAPH_XY_LOGIC_MISC0_COUNTER_0 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION 20 +#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION_NONZERO 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION_ZERO 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC0_INDEX 28 +#define NV4_PGRAPH_XY_LOGIC_MISC0_INDEX_0 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1 0x400518 +#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL 0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL_NEEDED 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL_DONE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX 4 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX_NOTNULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX_NULL 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY 5 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY_NOTNULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY_NULL 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX 12 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX_UUMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX 16 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX_UUMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA 20 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA_CLIPMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2 0x40051C +#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF 0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF_DISABLE 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF_ENABLE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX 4 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX_NOTNULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX_NULL 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY 5 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY_NOTNULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY_NULL 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX 12 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX_UCMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX 16 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX_UCMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA 20 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA_CLIPMAX 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA_IMAGEMAX 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC3 0x400520 +#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0 0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0_NULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0_TRUE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY 4 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY_NULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY_TRUE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX 8 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX_NULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX_TRUE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG 12 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG_NULL 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG_TRUE 0x1 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_DIMX 16 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_DIMX_0 0x0 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_WDIMX 24 +#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_WDIMX_0 0x0 +#define NV4_PGRAPH_X_MISC 0x400500 +#define NV4_PGRAPH_X_MISC_BIT33_0 0 +#define NV4_PGRAPH_X_MISC_BIT33_0_0 0x0 +#define NV4_PGRAPH_X_MISC_BIT33_1 1 +#define NV4_PGRAPH_X_MISC_BIT33_1_0 0x0 +#define NV4_PGRAPH_X_MISC_BIT33_2 2 +#define NV4_PGRAPH_X_MISC_BIT33_2_0 0x0 +#define NV4_PGRAPH_X_MISC_BIT33_3 3 +#define NV4_PGRAPH_X_MISC_BIT33_3_0 0x0 +#define NV4_PGRAPH_X_MISC_RANGE_0 4 +#define NV4_PGRAPH_X_MISC_RANGE_0_0 0x0 +#define NV4_PGRAPH_X_MISC_RANGE_1 5 +#define NV4_PGRAPH_X_MISC_RANGE_1_0 0x0 +#define NV4_PGRAPH_X_MISC_RANGE_2 6 +#define NV4_PGRAPH_X_MISC_RANGE_2_0 0x0 +#define NV4_PGRAPH_X_MISC_RANGE_3 7 +#define NV4_PGRAPH_X_MISC_RANGE_3_0 0x0 +#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT 28 +#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_EQ_0 0x0 +#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_LT_0 0x1 +#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_GT_0 0x2 +#define NV4_PGRAPH_Y_MISC 0x400504 +#define NV4_PGRAPH_Y_MISC_BIT33_0 0 +#define NV4_PGRAPH_Y_MISC_BIT33_0_0 0x0 +#define NV4_PGRAPH_Y_MISC_BIT33_1 1 +#define NV4_PGRAPH_Y_MISC_BIT33_1_0 0x0 +#define NV4_PGRAPH_Y_MISC_BIT33_2 2 +#define NV4_PGRAPH_Y_MISC_BIT33_2_0 0x0 +#define NV4_PGRAPH_Y_MISC_BIT33_3 3 +#define NV4_PGRAPH_Y_MISC_BIT33_3_0 0x0 +#define NV4_PGRAPH_Y_MISC_RANGE_0 4 +#define NV4_PGRAPH_Y_MISC_RANGE_0_0 0x0 +#define NV4_PGRAPH_Y_MISC_RANGE_1 5 +#define NV4_PGRAPH_Y_MISC_RANGE_1_0 0x0 +#define NV4_PGRAPH_Y_MISC_RANGE_2 6 +#define NV4_PGRAPH_Y_MISC_RANGE_2_0 0x0 +#define NV4_PGRAPH_Y_MISC_RANGE_3 7 +#define NV4_PGRAPH_Y_MISC_RANGE_3_0 0x0 +#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT 28 +#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_EQ_0 0x0 +#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_LT_0 0x1 +#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_GT_0 0x2 +#define NV4_PGRAPH_ABS_UCLIP_XMIN 0x40053C +#define NV4_PGRAPH_ABS_UCLIP_XMIN_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIP_XMAX 0x400544 +#define NV4_PGRAPH_ABS_UCLIP_XMAX_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIP_YMIN 0x400540 +#define NV4_PGRAPH_ABS_UCLIP_YMIN_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIP_YMAX 0x400548 +#define NV4_PGRAPH_ABS_UCLIP_YMAX_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIPA_XMIN 0x400560 +#define NV4_PGRAPH_ABS_UCLIPA_XMIN_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIPA_XMAX 0x400568 +#define NV4_PGRAPH_ABS_UCLIPA_XMAX_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIPA_YMIN 0x400564 +#define NV4_PGRAPH_ABS_UCLIPA_YMIN_VALUE 0 +#define NV4_PGRAPH_ABS_UCLIPA_YMAX 0x40056C +#define NV4_PGRAPH_ABS_UCLIPA_YMAX_VALUE 0 +#define NV4_PGRAPH_SOURCE_COLOR 0x40050C +#define NV4_PGRAPH_SOURCE_COLOR_VALUE 0 +#define NV4_PGRAPH_SOURCE_COLOR_VALUE_0 0x0 +#define NV4_PGRAPH_VALID1 0x400508 +#define NV4_PGRAPH_VALID1_VLD 0 +#define NV4_PGRAPH_VALID1_VLD_0 0x0 +#define NV4_PGRAPH_VALID1_VLD_NOCLIP (0x1<<19) +#define NV4_PGRAPH_VALID1_VLD_SRCCOLOR (0x1<<16) +#define NV4_PGRAPH_VALID1_VLD_GOTMOVE (0x1<<21) +#define NV4_PGRAPH_VALID1_VLD_GOTX01 (0x3<<0) +#define NV4_PGRAPH_VALID1_VLD_GOTX02 (0x7<<0) +#define NV4_PGRAPH_VALID1_VLD_GOTX03 (0xF<<0) +#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN01 (0x3<<4) +#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN02 (0x7<<4) +#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN03 (0xF<<4) +#define NV4_PGRAPH_VALID1_VLD_GOTY01 (0x3<<8) +#define NV4_PGRAPH_VALID1_VLD_GOTY02 (0x7<<8) +#define NV4_PGRAPH_VALID1_VLD_GOTY03 (0xF<<8) +#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN01 (0x3<<12) +#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN02 (0x7<<12) +#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN03 (0xF<<12) +#define NV4_PGRAPH_VALID1_VLD_X_OFFSET (0x1<<0) +#define NV4_PGRAPH_VALID1_VLD_XCHAIN_OFFSET (0x1<<4) +#define NV4_PGRAPH_VALID1_VLD_Y_OFFSET (0x1<<8) +#define NV4_PGRAPH_VALID1_VLD_YCHAIN_OFFSET (0x1<<12) +#define NV4_PGRAPH_VALID1_VLD_GOTCOLOR0 (0x1<<17) +#define NV4_PGRAPH_VALID1_VLD_GOTCOLOR1 (0x1<<18) +#define NV4_PGRAPH_VALID1_VLD_GOTCLIP (0x1<<20) +#define NV4_PGRAPH_VALID1_VLD_GOTFONT (0x1<<22) +#define NV4_PGRAPH_VALID1_VLD_GOTOFFSET (0x1<<22) +#define NV4_PGRAPH_VALID1_VLD_GOTBPITCH (0x1<<2) +#define NV4_PGRAPH_VALID1_VLD_GOTBOFFSET (0x1<<3) +#define NV4_PGRAPH_VALID1_VLD_GOTDUDX (0x1<<4) +#define NV4_PGRAPH_VALID1_VLD_GOTDVDY (0x1<<5) +#define NV4_PGRAPH_VALID1_VLD_GOTPOINT (0x1<<8) +#define NV4_PGRAPH_VALID1_VLD_GOTSIZE (0x1<<9) +#define NV4_PGRAPH_VALID1_VLD_GOTPITCH (0x1<<10) +#define NV4_PGRAPH_VALID1_VLD_GOTSTART (0x1<<11) +#define NV4_PGRAPH_VALID1_VLD_GOTDUDX2 (0x1<<12) +#define NV4_PGRAPH_VALID1_VLD_GOTDVDY2 (0x1<<13) +#define NV4_PGRAPH_VALID1_VLD_GOTPOINT2 (0x1<<14) +#define NV4_PGRAPH_VALID1_VLD_GOTSIZE2 (0x1<<15) +#define NV4_PGRAPH_VALID1_VLD_GOTPITCH2 (0x1<<16) +#define NV4_PGRAPH_VALID1_VLD_GOTSTART2 (0x1<<17) +#define NV4_PGRAPH_VALID1_VLD_GOTOFFSIN (0x1<<0) +#define NV4_PGRAPH_VALID1_VLD_GOTOFFSOUT (0x1<<1) +#define NV4_PGRAPH_VALID1_VLD_GOTPITCHIN (0x1<<2) +#define NV4_PGRAPH_VALID1_VLD_GOTPITCHOUT (0x1<<3) +#define NV4_PGRAPH_VALID1_VLD_GOTLENGTH (0x1<<4) +#define NV4_PGRAPH_VALID1_VLD_GOTCOUNT (0x1<<5) +#define NV4_PGRAPH_VALID1_VLD_GOTFORMAT (0x1<<6) +#define NV4_PGRAPH_VALID1_VLD_GOTNOTIFY (0x1<<7) +#define NV4_PGRAPH_VALID1_CLIP_MIN 28 +#define NV4_PGRAPH_VALID1_CLIP_MIN_NO_ERROR 0x0 +#define NV4_PGRAPH_VALID1_CLIP_MIN_ONLY 0x1 +#define NV4_PGRAPH_VALID1_CLIPA_MIN 29 +#define NV4_PGRAPH_VALID1_CLIPA_MIN_NO_ERROR 0x0 +#define NV4_PGRAPH_VALID1_CLIPA_MIN_ONLY 0x1 +#define NV4_PGRAPH_VALID1_CLIP_MAX 30 +#define NV4_PGRAPH_VALID1_CLIP_MAX_NO_ERROR 0x0 +#define NV4_PGRAPH_VALID1_CLIP_MAX_ONLY 0x1 +#define NV4_PGRAPH_VALID1_CLIPA_MAX 31 +#define NV4_PGRAPH_VALID1_CLIPA_MAX_NO_ERROR 0x0 +#define NV4_PGRAPH_VALID1_CLIPA_MAX_ONLY 0x1 +#define NV4_PGRAPH_VALID2 0x400578 +#define NV4_PGRAPH_VALID2_VLD2 0 +#define NV4_PGRAPH_VALID2_VLD2_0 0x0 +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE0A (1<<28) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE0C (1<<27) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE1A (1<<26) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE1C (1<<25) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBFACTOR (1<<24) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FILTER1 (1<<23) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_OFFSET1 (1<<22) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FORMAT1 (1<<21) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_BLEND (1<<20) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL2 (1<<19) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL1 (1<<18) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL0 (1<<17) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FILTER0 (1<<16) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FORMAT0 (1<<15) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_OFFSET0 (1<<14) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FOGCOLOR (1<<13) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COLORKEY (1<<12) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_V1 (1<<9) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_U1 (1<<8) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_V0 (1<<7) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_U0 (1<<6) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_X (1<<5) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_Y (1<<4) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_ZETA (1<<3) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_M (1<<2) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COLOR (1<<1) +#define NV4_PGRAPH_VALID2_VLD2_GOT3D_SPECULAR (1<<0) +#define NV4_PGRAPH_VALID2_VLD2_DX3FULLVERTEX (0x7f<<0) +#define NV4_PGRAPH_VALID2_VLD2_DX5FULLVERTEX (0x7f<<0) +#define NV4_PGRAPH_VALID2_VLD2_DX6FULLVERTEX (0x1ff<<0) +#define NV4_PGRAPH_VALID2_VLD2_DX3FULLSTATE (0x3f<<13) +#define NV4_PGRAPH_VALID2_VLD2_DX5FULLSTATE (0x1ff<<12) +#define NV4_PGRAPH_VALID2_VLD2_DX6FULLSTATE (0xFfff<<13) +#define NV4_PGRAPH_ABS_ICLIP_XMAX 0x400534 +#define NV4_PGRAPH_ABS_ICLIP_XMAX_VALUE 0 +#define NV4_PGRAPH_ABS_ICLIP_YMAX 0x400538 +#define NV4_PGRAPH_ABS_ICLIP_YMAX_VALUE 0 +#define NV4_PGRAPH_CLIPX_0 0x400524 +// Valid for all clips! +#define NV4_PGRAPH_CLIP_CONDITION_IS_GT 0x0 +#define NV4_PGRAPH_CLIP_CONDITION_IS_LT 0x1 +#define NV4_PGRAPH_CLIP_CONDITION_IS_EQ 0x2 +#define NV4_PGRAPH_CLIPX_0_CLIP0_MIN 0 +#define NV4_PGRAPH_CLIPX_0_CLIP0_MAX 2 +#define NV4_PGRAPH_CLIPX_0_CLIP1_MIN 4 +#define NV4_PGRAPH_CLIPX_0_CLIP1_MAX 6 +#define NV4_PGRAPH_CLIPX_0_CLIP2_MIN 8 +#define NV4_PGRAPH_CLIPX_0_CLIP2_MAX 10 +#define NV4_PGRAPH_CLIPX_0_CLIP3_MIN 12 +#define NV4_PGRAPH_CLIPX_0_CLIP3_MAX 14 +#define NV4_PGRAPH_CLIPX_0_CLIP4_MIN 16 +#define NV4_PGRAPH_CLIPX_0_CLIP4_MAX 18 +#define NV4_PGRAPH_CLIPX_0_CLIP5_MIN 20 +#define NV4_PGRAPH_CLIPX_0_CLIP5_MAX 22 +#define NV4_PGRAPH_CLIPX_0_CLIP6_MIN 24 +#define NV4_PGRAPH_CLIPX_0_CLIP6_MAX 26 +#define NV4_PGRAPH_CLIPX_0_CLIP7_MIN 28 +#define NV4_PGRAPH_CLIPX_0_CLIP7_MAX 30 +#define NV4_PGRAPH_CLIPX_1 0x400528 +#define NV4_PGRAPH_CLIPX_1_CLIP8_MIN 0 +#define NV4_PGRAPH_CLIPX_1_CLIP8_MAX 2 +#define NV4_PGRAPH_CLIPX_1_CLIP9_MIN 4 +#define NV4_PGRAPH_CLIPX_1_CLIP9_MAX 6 +#define NV4_PGRAPH_CLIPX_1_CLIP10_MIN 8 +#define NV4_PGRAPH_CLIPX_1_CLIP10_MAX 10 +#define NV4_PGRAPH_CLIPX_1_CLIP11_MIN 12 +#define NV4_PGRAPH_CLIPX_1_CLIP11_MAX 14 +#define NV4_PGRAPH_CLIPX_1_CLIP12_MIN 16 +#define NV4_PGRAPH_CLIPX_1_CLIP12_MAX 18 +#define NV4_PGRAPH_CLIPX_1_CLIP13_MIN 20 +#define NV4_PGRAPH_CLIPX_1_CLIP13_MAX 22 +#define NV4_PGRAPH_CLIPX_1_CLIP14_MIN 24 +#define NV4_PGRAPH_CLIPX_1_CLIP14_MAX 26 +#define NV4_PGRAPH_CLIPX_1_CLIP15_MIN 28 +#define NV4_PGRAPH_CLIPX_1_CLIP15_MAX 30 +#define NV4_PGRAPH_CLIPY_0 0x40052c +#define NV4_PGRAPH_CLIPY_0_CLIP0_MIN 0 +#define NV4_PGRAPH_CLIPY_0_CLIP0_MAX 2 +#define NV4_PGRAPH_CLIPY_0_CLIP1_MIN 4 +#define NV4_PGRAPH_CLIPY_0_CLIP1_MAX 6 +#define NV4_PGRAPH_CLIPY_0_CLIP2_MIN 8 +#define NV4_PGRAPH_CLIPY_0_CLIP2_MAX 10 +#define NV4_PGRAPH_CLIPY_0_CLIP3_MIN 12 +#define NV4_PGRAPH_CLIPY_0_CLIP3_MAX 14 +#define NV4_PGRAPH_CLIPY_0_CLIP4_MIN 16 +#define NV4_PGRAPH_CLIPY_0_CLIP4_MAX 18 +#define NV4_PGRAPH_CLIPY_0_CLIP5_MIN 20 +#define NV4_PGRAPH_CLIPY_0_CLIP5_MAX 22 +#define NV4_PGRAPH_CLIPY_0_CLIP6_MIN 24 +#define NV4_PGRAPH_CLIPY_0_CLIP6_MAX 26 +#define NV4_PGRAPH_CLIPY_0_CLIP7_MIN 28 +#define NV4_PGRAPH_CLIPY_0_CLIP7_MAX 30 +#define NV4_PGRAPH_CLIPY_1 0x400530 +#define NV4_PGRAPH_CLIPY_1_CLIP8_MIN 0 +#define NV4_PGRAPH_CLIPY_1_CLIP8_MAX 2 +#define NV4_PGRAPH_CLIPY_1_CLIP9_MIN 4 +#define NV4_PGRAPH_CLIPY_1_CLIP9_MAX 6 +#define NV4_PGRAPH_CLIPY_1_CLIP10_MIN 8 +#define NV4_PGRAPH_CLIPY_1_CLIP10_MAX 10 +#define NV4_PGRAPH_CLIPY_1_CLIP11_MIN 12 +#define NV4_PGRAPH_CLIPY_1_CLIP11_MAX 14 +#define NV4_PGRAPH_CLIPY_1_CLIP12_MIN 16 +#define NV4_PGRAPH_CLIPY_1_CLIP12_MAX 18 +#define NV4_PGRAPH_CLIPY_1_CLIP13_MIN 20 +#define NV4_PGRAPH_CLIPY_1_CLIP13_MAX 22 +#define NV4_PGRAPH_CLIPY_1_CLIP14_MIN 24 +#define NV4_PGRAPH_CLIPY_1_CLIP14_MAX 26 +#define NV4_PGRAPH_CLIPY_1_CLIP15_MIN 28 +#define NV4_PGRAPH_CLIPY_1_CLIP15_MAX 30 +#define NV4_PGRAPH_MISC24_0 0x400510 +#define NV4_PGRAPH_MISC24_0_VALUE 0 +#define NV4_PGRAPH_MISC24_1 0x400570 +#define NV4_PGRAPH_MISC24_1_VALUE 0 +#define NV4_PGRAPH_MISC24_2 0x400574 +#define NV4_PGRAPH_MISC24_2_VALUE 0 +#define NV4_PGRAPH_PASSTHRU_0 0x40057C +#define NV4_PGRAPH_PASSTHRU_0_VALUE 0 +#define NV4_PGRAPH_PASSTHRU_1 0x400580 +#define NV4_PGRAPH_PASSTHRU_1_VALUE 0 +#define NV4_PGRAPH_PASSTHRU_2 0x400584 +#define NV4_PGRAPH_PASSTHRU_2_VALUE 0 +#define NV4_PGRAPH_U_RAM(i) (0x400d00+(i)*4) +#define NV4_PGRAPH_U_RAM_SIZE_1 16 +#define NV4_PGRAPH_U_RAM_VALUE 6 +#define NV4_PGRAPH_V_RAM(i) (0x400d40+(i)*4) +#define NV4_PGRAPH_V_RAM_SIZE_1 16 +#define NV4_PGRAPH_V_RAM_VALUE 6 +#define NV4_PGRAPH_M_RAM(i) (0x400d80+(i)*4) +#define NV4_PGRAPH_M_RAM_SIZE_1 16 +#define NV4_PGRAPH_M_RAM_VALUE 6 +#define NV4_PGRAPH_D3D_XY 0x4005c0 +#define NV4_PGRAPH_D3D_XY_X_VALUE 0 +#define NV4_PGRAPH_D3D_XY_Y_VALUE 16 +#define NV4_PGRAPH_D3D_U0 0x4005c4 +#define NV4_PGRAPH_D3D_U0_VALUE 6 +#define NV4_PGRAPH_D3D_V0 0x4005c8 +#define NV4_PGRAPH_D3D_V0_VALUE 6 +#define NV4_PGRAPH_D3D_U1 0x4005cc +#define NV4_PGRAPH_D3D_U1_VALUE 6 +#define NV4_PGRAPH_D3D_V1 0x4005d0 +#define NV4_PGRAPH_D3D_V1_VALUE 6 +#define NV4_PGRAPH_D3D_ZETA 0x4005d4 +#define NV4_PGRAPH_D3D_ZETA_VALUE 0 +#define NV4_PGRAPH_D3D_RGB 0x4005d8 +#define NV4_PGRAPH_D3D_RGB_VALUE 0 +#define NV4_PGRAPH_D3D_S 0x4005dc +#define NV4_PGRAPH_D3D_S_VALUE 0 +#define NV4_PGRAPH_D3D_M 0x4005e0 +#define NV4_PGRAPH_D3D_M_VALUE 6 +#define NV4_PGRAPH_FORMAT0 0x4005A8 +#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA 1 +#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA_A 0x0 +#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA_B 0x1 +#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE 2 +#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE_FALSE 0x0 +#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE_TRUE 0x1 +#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH 5 +#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH_CENTER 0x0 +#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH_CORNER 0x1 +#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH 7 +#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH_CENTER 0x0 +#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH_CORNER 0x1 +#define NV4_PGRAPH_FORMAT0_COLOR 8 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_Y8 0x0 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_AY8 0x1 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_A1R5G5B5 0x2 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_X1R5G5B5 0x3 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_A4R4G4B4 0x4 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_R5G6B5 0x5 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_A8R8G8B8 0x6 +#define NV4_PGRAPH_FORMAT0_COLOR_LE_X8R8G8B8 0x7 +// 0x1-0Xf used for 1-16 mipmap levels +#define NV4_PGRAPH_FORMAT0_MIPMAP_LEVELS 12 +#define NV4_PGRAPH_FORMAT0_MIPMAP_LEVELS_INVALID 0x0 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U 16 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_1 0x0 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_2 0x1 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_4 0x2 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_8 0x3 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_16 0x4 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_32 0x5 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_64 0x6 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_128 0x7 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_256 0x8 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_512 0x9 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_1024 0xA +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_2048 0xB +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V 20 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_1 0x0 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_2 0x1 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_4 0x2 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_8 0x3 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_16 0x4 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_32 0x5 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_64 0x6 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_128 0x7 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_256 0x8 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_512 0x9 +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_1024 0xA +#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_2048 0xB +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU 24 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_WRAP 0x1 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_MIRROR 0x2 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_CLAMP 0x3 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_BORDER 0x4 +#define NV4_PGRAPH_FORMAT0_WRAPU 27 +#define NV4_PGRAPH_FORMAT0_WRAPU_FALSE 0x0 +#define NV4_PGRAPH_FORMAT0_WRAPU_TRUE 0x1 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV 28 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_WRAP 0x1 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_MIRROR 0x2 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_CLAMP 0x3 +#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_BORDER 0x4 +#define NV4_PGRAPH_FORMAT0_WRAPV 31 +#define NV4_PGRAPH_FORMAT0_WRAPV_FALSE 0x0 +#define NV4_PGRAPH_FORMAT0_WRAPV_TRUE 0x1 +#define NV4_PGRAPH_FORMAT1 0x4005AC +#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA 1 +#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA_A 0x0 +#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA_B 0x1 +#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE 2 +#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE_FALSE 0x0 +#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE_TRUE 0x1 +#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH 5 +#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH_CENTER 0x0 +#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH_CORNER 0x1 +#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH 7 +#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH_CENTER 0x0 +#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH_CORNER 0x1 +#define NV4_PGRAPH_FORMAT1_COLOR 8 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_Y8 0x0 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_AY8 0x1 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_A1R5G5B5 0x2 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_X1R5G5B5 0x3 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_A4R4G4B4 0x4 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_R5G6B5 0x5 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_A8R8G8B8 0x6 +#define NV4_PGRAPH_FORMAT1_COLOR_LE_X8R8G8B8 0x7 +// 15:12 = number of mipmap levels (1-15. 0 = invalid). Stupid defines removed here +#define NV4_PGRAPH_FORMAT1_MIPMAP_LEVELS 12 +#define NV4_PGRAPH_FORMAT1_MIPMAP_LEVELS_INVALID 0x0 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U 16 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_1 0x0 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_2 0x1 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_4 0x2 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_8 0x3 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_16 0x4 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_32 0x5 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_64 0x6 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_128 0x7 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_256 0x8 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_512 0x9 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_1024 0xA +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_2048 0xB +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V 20 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_1 0x0 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_2 0x1 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_4 0x2 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_8 0x3 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_16 0x4 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_32 0x5 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_64 0x6 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_128 0x7 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_256 0x8 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_512 0x9 +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_1024 0xA +#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_2048 0xB +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU 24 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_WRAP 0x1 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_MIRROR 0x2 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_CLAMP 0x3 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_BORDER 0x4 +#define NV4_PGRAPH_FORMAT1_WRAPU 27 +#define NV4_PGRAPH_FORMAT1_WRAPU_TRUE 0x1 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV 28 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_WRAP 0x1 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_MIRROR 0x2 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_CLAMP 0x3 +#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_BORDER 0x4 +#define NV4_PGRAPH_FORMAT1_WRAPV 31 +#define NV4_PGRAPH_FORMAT1_WRAPV_TRUE 0x1 +#define NV4_PGRAPH_FILTER0 0x4005B0 +#define NV4_PGRAPH_FILTER0_KERNEL_SIZE_X 1 +#define NV4_PGRAPH_FILTER0_KERNEL_SIZE_Y 9 +#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE 15 +#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE_FALSE 0x0 +#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_FILTER0_MIPMAPLODBIAS 16 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN 24 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_NEAREST 0x1 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEAR 0x2 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_MIPNEAREST 0x3 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_MIPLINEAR 0x4 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEARMIPNEAREST 0x5 +#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEARMIPLINEAR 0x6 +#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MIN_ENABLE 27 +#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MIN_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG 28 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_NEAREST 0x1 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEAR 0x2 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_MIPNEAREST 0x3 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_MIPLINEAR 0x4 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEARMIPNEAREST 0x5 +#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEARMIPLINEAR 0x6 +#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MAG_ENABLE 31 +#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MAG_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_FILTER1 0x4005B4 +#define NV4_PGRAPH_FILTER1_KERNEL_SIZE_X 1 +#define NV4_PGRAPH_FILTER1_KERNEL_SIZE_Y 9 +#define NV4_PGRAPH_FILTER1_MIPMAP_DITHER_ENABLE 15 +#define NV4_PGRAPH_FILTER1_MIPMAP_DITHER_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_FILTER1_MIPMAPLODBIAS 16 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN 24 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_NEAREST 0x1 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEAR 0x2 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_MIPNEAREST 0x3 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_MIPLINEAR 0x4 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEARMIPNEAREST 0x5 +#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEARMIPLINEAR 0x6 +#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MIN_ENABLE 27 +#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MIN_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG 28 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_NEAREST 0x1 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEAR 0x2 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_MIPNEAREST 0x3 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_MIPLINEAR 0x4 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEARMIPNEAREST 0x5 +#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEARMIPLINEAR 0x6 +#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MAG_ENABLE 31 +#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MAG_ENABLE_TRUE 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA 0x400590 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0 0 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1 8 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1 10 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2 16 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2 18 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3 24 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3 26 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION 29 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD 0x1 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD2 0x2 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD4 0x3 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDSIGNED 0x4 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_MUX 0x5 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDCOMPLEMENT 0x6 +#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDSIGNED2 0x7 +#define NV4_PGRAPH_COMBINE0COLOR 0x400594 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0 0 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0 1 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_COLOR 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1 8 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1 9 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1_COLOR 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1 10 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2 16 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2 17 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2_COLOR 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2 18 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3 24 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3 25 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3_COLOR 0x0 +#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3 26 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_ZERO 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_INPUT 0x4 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURELOD 0x7 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION 29 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD 0x1 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD2 0x2 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD4 0x3 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDSIGNED 0x4 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_MUX 0x5 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDCOMPLEMENT 0x6 +#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDSIGNED2 0x7 +#define NV4_PGRAPH_COMBINE1ALPHA 0x400598 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0 0 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1 8 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1 10 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2 16 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2 18 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3 24 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3 26 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION 29 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD 0x1 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD2 0x2 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD4 0x3 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDSIGNED 0x4 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_MUX 0x5 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDCOMPLEMENT 0x6 +#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDSIGNED2 0x7 +#define NV4_PGRAPH_COMBINE1COLOR 0x40059C +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0 0 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0 1 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0_COLOR 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1 8 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1 9 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1_COLOR 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1 10 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2 16 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2 17 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2_COLOR 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2 18 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3 24 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3_NORMAL 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3_INVERSE 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3 25 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3_COLOR 0x0 +#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3_ALPHA 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3 26 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_ZERO 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_FACTOR 0x2 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_DIFFUSE 0x3 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_INPUT 0x4 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_TEXTURE0 0x5 +#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_TEXTURE1 0x6 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION 29 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD 0x1 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD2 0x2 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD4 0x3 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDSIGNED 0x4 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_MUX 0x5 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDCOMPLEMENT 0x6 +#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDSIGNED2 0x7 +#define NV4_PGRAPH_DMA_START_0 0x401000 +#define NV4_PGRAPH_DMA_START_0_VALUE 0 +#define NV4_PGRAPH_DMA_START_1 0x401004 +#define NV4_PGRAPH_DMA_START_1_VALUE 0 +#define NV4_PGRAPH_DMA_LENGTH 0x401008 +#define NV4_PGRAPH_DMA_LENGTH_VALUE 0 +#define NV4_PGRAPH_DMA_MISC 0x40100C +#define NV4_PGRAPH_DMA_MISC_COUNT 0 +#define NV4_PGRAPH_DMA_MISC_FMT_SRC 16 +#define NV4_PGRAPH_DMA_MISC_FMT_DST 20 +#define NV4_PGRAPH_DMA_DATA_0 0x401020 +#define NV4_PGRAPH_DMA_DATA_0_VALUE 0 +#define NV4_PGRAPH_DMA_DATA_1 0x401024 +#define NV4_PGRAPH_DMA_DATA_1_VALUE 0 +#define NV4_PGRAPH_DMA_RM 0x401030 +#define NV4_PGRAPH_DMA_RM_ASSIST_A 0 +#define NV4_PGRAPH_DMA_RM_ASSIST_A_NOT_PENDING 0x0 +#define NV4_PGRAPH_DMA_RM_ASSIST_A_PENDING 0x1 +#define NV4_PGRAPH_DMA_RM_ASSIST_A_RESET 0x1 +#define NV4_PGRAPH_DMA_RM_ASSIST_B 1 +#define NV4_PGRAPH_DMA_RM_ASSIST_B_NOT_PENDING 0x0 +#define NV4_PGRAPH_DMA_RM_ASSIST_B_PENDING 0x1 +#define NV4_PGRAPH_DMA_RM_ASSIST_B_RESET 0x1 +#define NV4_PGRAPH_DMA_RM_WRITE_REQ 4 +#define NV4_PGRAPH_DMA_RM_WRITE_REQ_NOT_PENDING 0x0 +#define NV4_PGRAPH_DMA_RM_WRITE_REQ_PENDING 0x1 +#define NV4_PGRAPH_DMA_A_XLATE_INST 0x401040 +#define NV4_PGRAPH_DMA_A_XLATE_INST_VALUE 0 +#define NV4_PGRAPH_DMA_A_CONTROL 0x401044 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE 12 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE_NOT_PRESENT 0x0 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE_PRESENT 0x1 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY 13 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY_NOT_LINEAR 0x0 +#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY_LINEAR 0x1 +#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE 16 +#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_NVM 0x0 +#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_PCI 0x2 +#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_AGP 0x3 +#define NV4_PGRAPH_DMA_A_CONTROL_ADJUST 20 +#define NV4_PGRAPH_DMA_A_LIMIT 0x401048 +#define NV4_PGRAPH_DMA_A_LIMIT_OFFSET 0 +#define NV4_PGRAPH_DMA_A_TLB_PTE 0x40104C +#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS 1 +#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS_READ_ONLY 0x0 +#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS_READ_WRITE 0x1 +#define NV4_PGRAPH_DMA_A_TLB_PTE_FRAME_ADDRESS 12 +#define NV4_PGRAPH_DMA_A_TLB_TAG 0x401050 +#define NV4_PGRAPH_DMA_A_TLB_TAG_ADDRESS 12 +#define NV4_PGRAPH_DMA_A_ADJ_OFFSET 0x401054 +#define NV4_PGRAPH_DMA_A_ADJ_OFFSET_VALUE 0 +#define NV4_PGRAPH_DMA_A_OFFSET 0x401058 +#define NV4_PGRAPH_DMA_A_OFFSET_VALUE 0 +#define NV4_PGRAPH_DMA_A_SIZE 0x40105C +#define NV4_PGRAPH_DMA_A_SIZE_VALUE 0 +#define NV4_PGRAPH_DMA_A_Y_SIZE 0x401060 +#define NV4_PGRAPH_DMA_A_Y_SIZE_VALUE 0 +#define NV4_PGRAPH_DMA_B_XLATE_INST 0x401080 +#define NV4_PGRAPH_DMA_B_XLATE_INST_VALUE 0 +#define NV4_PGRAPH_DMA_B_CONTROL 0x401084 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE 12 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE_NOT_PRESENT 0x0 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE_PRESENT 0x1 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY 13 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY_NOT_LINEAR 0x0 +#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY_LINEAR 0x1 +#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE 16 +#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_NVM 0x0 +#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_PCI 0x2 +#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_AGP 0x3 +#define NV4_PGRAPH_DMA_B_CONTROL_ADJUST 20 +#define NV4_PGRAPH_DMA_B_LIMIT 0x401088 +#define NV4_PGRAPH_DMA_B_LIMIT_OFFSET 0 +#define NV4_PGRAPH_DMA_B_TLB_PTE 0x40108C +#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS 1 +#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS_READ_ONLY 0x0 +#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS_READ_WRITE 0x1 +#define NV4_PGRAPH_DMA_B_TLB_PTE_FRAME_ADDRESS 12 +#define NV4_PGRAPH_DMA_B_TLB_TAG 0x401090 +#define NV4_PGRAPH_DMA_B_TLB_TAG_ADDRESS 12 +#define NV4_PGRAPH_DMA_B_ADJ_OFFSET 0x401094 +#define NV4_PGRAPH_DMA_B_ADJ_OFFSET_VALUE 0 +#define NV4_PGRAPH_DMA_B_OFFSET 0x401098 +#define NV4_PGRAPH_DMA_B_OFFSET_VALUE 0 +#define NV4_PGRAPH_DMA_B_SIZE 0x40109C +#define NV4_PGRAPH_DMA_B_SIZE_VALUE 0 +#define NV4_PGRAPH_DMA_B_Y_SIZE 0x4010A0 +#define NV4_PGRAPH_DMA_B_Y_SIZE_VALUE 0 +#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR 0 +#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_ZOH_MS 0x0 +#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_ZOH 0x1 +#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_FOH 0x2 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_U 4 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_CYLINDRICAL 0x0 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_WRAP 0x1 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_MIRROR 0x2 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_CLAMP 0x3 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_V 6 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_CYLINDRICAL 0x0 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_WRAP 0x1 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_MIRROR 0x2 +#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_CLAMP 0x3 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT 8 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT_LE_X8R8G8B8 0x0 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT_LE_A8R8G8B8 0x1 +#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR 10 +#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_NORMAL 0x0 +#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_COLOR_INVERSE 0x1 +#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_ALPHA_INVERSE 0x2 +#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_ALPHA_ONE 0x3 +#define NV4_PGRAPH_CONTROL_OUT_CULLING 12 +#define NV4_PGRAPH_CONTROL_OUT_CULLING_ILLEGAL 0x0 +#define NV4_PGRAPH_CONTROL_OUT_CULLING_NONE 0x1 +#define NV4_PGRAPH_CONTROL_OUT_CULLING_COUNTERCLOCKWISE 0x2 +#define NV4_PGRAPH_CONTROL_OUT_CULLING_CLOCKWISE 0x3 +#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER 15 +#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER_SCREEN 0x0 +#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER_LINEAR 0x1 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE 16 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_ILLEGAL 0x0 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_FALSE 0x1 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_LT 0x2 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_EQ 0x3 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_LE 0x4 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_GT 0x5 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_NE 0x6 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_GE 0x7 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_TRUE 0x8 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE 20 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_NEVER 0x0 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALPHA 0x1 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALPHA_ZETA 0x2 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ZETA 0x3 +#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALWAYS 0x4 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE 24 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_NEVER 0x0 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ALPHA 0x1 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ALPHA_ZETA 0x2 +#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ZETA 0x3 +#define NV4_PGRAPH_CONTROL_OUT_ROP 28 +#define NV4_PGRAPH_CONTROL_OUT_ROP_BLEND_AND 0x0 +#define NV4_PGRAPH_CONTROL_OUT_ROP_ADD_WITH_SATURATION 0x1 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA 29 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA_SRCALPHA 0x0 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA_DESTCOLOR 0x1 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0 30 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0_DESTCOLOR 0x0 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0_ZERO 0x1 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1 31 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1_SRCCOLOR 0x0 +#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1_ZERO 0x1 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_KEY 0 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE 8 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_ILLEGAL 0x0 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_FALSE 0x1 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_LT 0x2 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_EQ 0x3 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_LE 0x4 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_GT 0x5 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_NE 0x6 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_GE 0x7 +#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_TRUE 0x8 + +#define NV4_PVIDEO_START 0x680000 +#define NV4_PVIDEO_END 0x6802FF + +#define NV4_PVIDEO_INTR_0 0x680100 +#define NV4_PVIDEO_INTR_0_NOTIFY 0 +#define NV4_PVIDEO_INTR_0_NOTIFY_NOT_PENDING 0x0 +#define NV4_PVIDEO_INTR_0_NOTIFY_PENDING 0x1 +#define NV4_PVIDEO_INTR_0_NOTIFY_RESET 0x1 +#define NV4_PVIDEO_INTR_EN_0 0x680140 +#define NV4_PVIDEO_INTR_EN_0_NOTIFY 0 +#define NV4_PVIDEO_INTR_EN_0_NOTIFY_ENABLED 0x1 +#define NV4_PVIDEO_STEP_SIZE 0x680200 +#define NV4_PVIDEO_STEP_SIZE_X 0 +#define NV4_PVIDEO_STEP_SIZE_Y 16 +#define NV4_PVIDEO_CONTROL_Y 0x680204 +#define NV4_PVIDEO_CONTROL_Y_BLUR 0 +#define NV4_PVIDEO_CONTROL_Y_BLUR_OFF 0x0 +#define NV4_PVIDEO_CONTROL_Y_BLUR_ON 0x1 +#define NV4_PVIDEO_CONTROL_Y_LINE 4 +#define NV4_PVIDEO_CONTROL_Y_LINE_HALF 0x0 +#define NV4_PVIDEO_CONTROL_Y_LINE_FULL 0x1 +#define NV4_PVIDEO_CONTROL_X 0x680208 +#define NV4_PVIDEO_CONTROL_X_WEIGHT 0 +#define NV4_PVIDEO_CONTROL_X_WEIGHT_LIGHT 0x0 +#define NV4_PVIDEO_CONTROL_X_WEIGHT_HEAVY 0x1 +#define NV4_PVIDEO_CONTROL_X_SHARPENING 4 +#define NV4_PVIDEO_CONTROL_X_SHARPENING_OFF 0x0 +#define NV4_PVIDEO_CONTROL_X_SHARPENING_ON 0x1 +#define NV4_PVIDEO_CONTROL_X_SMOOTHING 8 +#define NV4_PVIDEO_CONTROL_X_SMOOTHING_OFF 0x0 +#define NV4_PVIDEO_CONTROL_X_SMOOTHING_ON 0x1 +#define NV4_PVIDEO_BUFF0_START 0x68020c +#define NV4_PVIDEO_BUFF0_START_ADDRESS 2 +#define NV4_PVIDEO_BUFF1_START 0x680210 +#define NV4_PVIDEO_BUFF1_START_ADDRESS 2 +#define NV4_PVIDEO_BUFF0_PITCH 0x680214 +#define NV4_PVIDEO_BUFF0_PITCH_LENGTH 4 +#define NV4_PVIDEO_BUFF1_PITCH 0x680218 +#define NV4_PVIDEO_BUFF1_PITCH_LENGTH 4 +#define NV4_PVIDEO_BUFF0_OFFSET 0x68021c +#define NV4_PVIDEO_BUFF0_OFFSET_X 0 +#define NV4_PVIDEO_BUFF0_OFFSET_Y 4 +#define NV4_PVIDEO_BUFF0_OFFSET_Y_OFF 0x0 +#define NV4_PVIDEO_BUFF0_OFFSET_Y_QUARTER 0x1 +#define NV4_PVIDEO_BUFF0_OFFSET_Y_HALF 0x2 +#define NV4_PVIDEO_BUFF1_OFFSET 0x680220 +#define NV4_PVIDEO_BUFF1_OFFSET_X 0 +#define NV4_PVIDEO_BUFF1_OFFSET_Y 4 +#define NV4_PVIDEO_BUFF1_OFFSET_Y_OFF 0x0 +#define NV4_PVIDEO_BUFF1_OFFSET_Y_QUARTER 0x1 +#define NV4_PVIDEO_BUFF1_OFFSET_Y_HALF 0x2 +#define NV4_PVIDEO_OE_STATE 0x680224 +#define NV4_PVIDEO_OE_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PVIDEO_OE_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PVIDEO_OE_STATE_BUFF0_ERROR 8 +#define NV4_PVIDEO_OE_STATE_BUFF1_ERROR 12 +#define NV4_PVIDEO_OE_STATE_BUFF0_IN_USE 16 +#define NV4_PVIDEO_OE_STATE_BUFF1_IN_USE 20 +#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER 24 +#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER_0 0x0 +#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER_1 0x1 +#define NV4_PVIDEO_SU_STATE 0x680228 +#define NV4_PVIDEO_SU_STATE_BUFF0_IN_USE 16 +#define NV4_PVIDEO_SU_STATE_BUFF1_IN_USE 20 +#define NV4_PVIDEO_RM_STATE 0x68022c +#define NV4_PVIDEO_RM_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PVIDEO_RM_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PVIDEO_WINDOW_START 0x680230 +#define NV4_PVIDEO_WINDOW_START_X 0 +#define NV4_PVIDEO_WINDOW_START_Y 16 +#define NV4_PVIDEO_WINDOW_SIZE 0x680234 +#define NV4_PVIDEO_WINDOW_SIZE_X 0 +#define NV4_PVIDEO_WINDOW_SIZE_Y 16 +#define NV4_PVIDEO_FIFO_THRES 0x680238 +#define NV4_PVIDEO_FIFO_THRES_SIZE 3 +#define NV4_PVIDEO_FIFO_BURST 0x68023c +#define NV4_PVIDEO_FIFO_BURST_LENGTH 0 +#define NV4_PVIDEO_FIFO_BURST_LENGTH_32 0x1 +#define NV4_PVIDEO_FIFO_BURST_LENGTH_64 0x2 +#define NV4_PVIDEO_FIFO_BURST_LENGTH_128 0x3 +#define NV4_PVIDEO_KEY 0x680240 +#define NV4_PVIDEO_KEY_INDEX 0 +#define NV4_PVIDEO_KEY_565 0 +#define NV4_PVIDEO_KEY_555 0 +#define NV4_PVIDEO_KEY_888 0 +#define NV4_PVIDEO_KEY_PACK 24 +#define NV4_PVIDEO_OVERLAY 0x680244 +#define NV4_PVIDEO_OVERLAY_VIDEO 0 +#define NV4_PVIDEO_OVERLAY_VIDEO_OFF 0x0 +#define NV4_PVIDEO_OVERLAY_VIDEO_ON 0x1 +#define NV4_PVIDEO_OVERLAY_KEY 4 +#define NV4_PVIDEO_OVERLAY_KEY_OFF 0x0 +#define NV4_PVIDEO_OVERLAY_KEY_ON 0x1 +#define NV4_PVIDEO_OVERLAY_FORMAT 8 +#define NV4_PVIDEO_OVERLAY_FORMAT_CCIR 0x0 +#define NV4_PVIDEO_OVERLAY_FORMAT_YUY2 0x1 +#define NV4_PVIDEO_RED_CSC 0x680280 +#define NV4_PVIDEO_RED_CSC_OFFSET 0 +#define NV4_PVIDEO_GREEN_CSC 0x680284 +#define NV4_PVIDEO_GREEN_CSC_OFFSET 0 +#define NV4_PVIDEO_BLUE_CSC 0x680288 +#define NV4_PVIDEO_BLUE_CSC_OFFSET 0 +#define NV4_PVIDEO_CSC_ADJUST 0x68028c +#define NV4_PVIDEO_CSC_ADJUST_B_FLAG 0 +#define NV4_PVIDEO_CSC_ADJUST_B_FLAG_OFF 0x0 +#define NV4_PVIDEO_CSC_ADJUST_B_FLAG_ON 0x1 +#define NV4_PVIDEO_CSC_ADJUST_G_FLAG 4 +#define NV4_PVIDEO_CSC_ADJUST_G_FLAG_OFF 0x0 +#define NV4_PVIDEO_CSC_ADJUST_G_FLAG_ON 0x1 +#define NV4_PVIDEO_CSC_ADJUST_R_FLAG 8 +#define NV4_PVIDEO_CSC_ADJUST_R_FLAG_OFF 0x0 +#define NV4_PVIDEO_CSC_ADJUST_R_FLAG_ON 0x1 +#define NV4_PVIDEO_CSC_ADJUST_L_FLAG 12 +#define NV4_PVIDEO_CSC_ADJUST_L_FLAG_OFF 0x0 +#define NV4_PVIDEO_CSC_ADJUST_L_FLAG_ON 0x1 +#define NV4_PVIDEO_CSC_ADJUST_CHROMA 16 +#define NV4_PVIDEO_CSC_ADJUST_CHROMA_OFF 0x0 +#define NV4_PVIDEO_CSC_ADJUST_CHROMA_ON 0x1 + +#define NV4_PRMCIO_START 0x601000 +#define NV4_PRMCIO_END 0x601FFF + +#define NV4_PRMCIO_INP0 0x6013c2 +#define NV4_PRMCIO_INP0_MONO 0x6013ba +#define NV4_PRMCIO_INP0_COLOR 0x6013da +#define NV4_PRMCIO_INP0_READ_MONO 0x6013ca +#define NV4_PRMCIO_INP0_WRITE_MONO 0x6013ba +#define NV4_PRMCIO_INP0_WRITE_COLOR 0x6013da +// DEFAULT = Palette +#define NV4_PRMCIO_ARX 0x6013c0 +#define NV4_PRMCIO_AR_WRITE 0x6013c0 // After ARX +#define NV4_PRMCIO_AR_READ 0x6013c1 +#define NV4_PRMCIO_AR_PALETTE_WRITE 0x6013c0 +#define NV4_PRMCIO_AR_PALETTE_READ 0x6013c1 +#define NV4_PRMCIO_AR_MODE_INDEX 0x10 +#define NV4_PRMCIO_AR_OSCAN_INDEX 0x11 +#define NV4_PRMCIO_AR_PLANE_INDEX 0x12 +#define NV4_PRMCIO_AR_HPP_INDEX 0x13 +#define NV4_PRMCIO_AR_CSEL_INDEX 0x14 +#define NV4_PRMCIO_CRX_MONO 0x6013b4 +#define NV4_PRMCIO_CRX_COLOR 0x6013d4 +#define NV4_PRMCIO_CR_MONO 0x6013b5 +#define NV4_PRMCIO_CR_COLOR 0x6013d5 +#define NV4_PRMCIO_CRE_MONO 0x6013b5 +#define NV4_PRMCIO_CRE_COLOR 0x6013d5 + +#define NV4_PCRTC_INTR_0 0x600100 +#define NV4_PCRTC_INTR_0_VBLANK 0 +#define NV4_PCRTC_INTR_0_VBLANK_NOT_PENDING 0x0 +#define NV4_PCRTC_INTR_0_VBLANK_PENDING 0x1 +#define NV4_PCRTC_INTR_0_VBLANK_RESET 0x1 +#define NV4_PCRTC_INTR_EN_0 0x600140 +#define NV4_PCRTC_INTR_EN_0_VBLANK 0 +#define NV4_PCRTC_INTR_EN_0_VBLANK_ENABLED 0x1 +#define NV4_PCRTC_START 0x600800 +#define NV4_PCRTC_START_ADDRESS 2 +#define NV4_PCRTC_CONFIG 0x600804 +#define NV4_PCRTC_CONFIG_START_ADDRESS 0 +#define NV4_PCRTC_CONFIG_START_ADDRESS_VGA 0x0 +#define NV4_PCRTC_CONFIG_START_ADDRESS_NON_VGA 0x1 +#define NV4_PCRTC_CONFIG_START_ADDRESS_HSYNC 0x2 +#define NV4_PCRTC_RASTER 0x600808 +#define NV4_PCRTC_RASTER_POSITION 0 +#define NV4_PCRTC_RASTER_SA_LOAD 12 +#define NV4_PCRTC_RASTER_SA_LOAD_DISPLAY 0x0 +#define NV4_PCRTC_RASTER_SA_LOAD_BEFORE 0x1 +#define NV4_PCRTC_RASTER_SA_LOAD_AFTER 0x2 +#define NV4_PCRTC_RASTER_VERT_BLANK 16 +#define NV4_PCRTC_RASTER_VERT_BLANK_ACTIVE 0x1 +#define NV4_PCRTC_RASTER_VERT_BLANK_INACTIVE 0x0 + +#define NV4_CIO_START 0x3B0 +#define NV4_CIO_END 0x3DF + + +#define NV4_CIO_INP0 0x3c2 +#define NV4_CIO_INP0_MONO 0x3ba +#define NV4_CIO_INP0_COLOR 0x3da +#define NV4_CIO_INP0_READ_MONO 0x3ca +#define NV4_CIO_INP0_WRITE_MONO 0x3ba +#define NV4_CIO_INP0_WRITE_COLOR 0x3da +#define NV4_CIO_ARX 0x3c0 // if index is not 0x10-0x13 write palette +#define NV4_CIO_AR_PALETTE_WRITE 0x3c0 +#define NV4_CIO_AR_PALETTE_READ 0x3c1 +#define NV4_CIO_AR_MODE_WRITE 0x3c0 +#define NV4_CIO_AR_MODE_READ 0x3c1 +#define NV4_CIO_AR_MODE_INDEX 0x10 +#define NV4_CIO_AR_OSCAN_WRITE 0x3c0 +#define NV4_CIO_AR_OSCAN_READ 0x3c1 +#define NV4_CIO_AR_OSCAN_INDEX 0x11 +#define NV4_CIO_AR_PLANE_WRITE 0x3c0 +#define NV4_CIO_AR_PLANE_READ 0x3c1 +#define NV4_CIO_AR_PLANE_INDEX 0x12 +#define NV4_CIO_AR_HPP_WRITE 0x3c0 +#define NV4_CIO_AR_HPP_READ 0x3c1 +#define NV4_CIO_AR_HPP_INDEX 0x13 +#define NV4_CIO_AR_CSEL_WRITE 0x3c0 +#define NV4_CIO_AR_CSEL_READ 0x3c1 +#define NV4_CIO_AR_CSEL_INDEX 0x14 +#define NV4_CIO_CRX_MONO 0x3b4 +#define NV4_CIO_CRX_COLOR 0x3d4 +#define NV4_CIO_CR_MONO 0x3b5 +#define NV4_CIO_CR_COLOR 0x3d5 +#define NV4_CIO_CR_HDT_INDEX 0x0 +#define NV4_CIO_CR_HDE_INDEX 0x1 +#define NV4_CIO_CR_HBS_INDEX 0x2 +#define NV4_CIO_CR_HBE_INDEX 0x3 +#define NV4_CIO_CR_HBE_4_0 0 +#define NV4_CIO_CR_HRS_INDEX 0x4 +#define NV4_CIO_CR_HRE_INDEX 0x5 +#define NV4_CIO_CR_HRE_HBE_5 7 +#define NV4_CIO_CR_HRE_4_0 0 +#define NV4_CIO_CR_VDT_INDEX 0x6 +#define NV4_CIO_CR_OVL_INDEX 0x7 +#define NV4_CIO_CR_OVL_VDE_8 1 +#define NV4_CIO_CR_OVL_VDE_9 6 +#define NV4_CIO_CR_OVL_VDT_8 0 +#define NV4_CIO_CR_OVL_VDT_9 5 +#define NV4_CIO_CR_OVL_VBS_8 3 +#define NV4_CIO_CR_OVL_VRS_8 2 +#define NV4_CIO_CR_OVL_VRS_9 7 +#define NV4_CIO_CR_RSAL_INDEX 0x8 +#define NV4_CIO_CR_RSAL_PANNING 5 +#define NV4_CIO_CR_CELL_HT_INDEX 0x9 +#define NV4_CIO_CR_CELL_HT_SCANDBL 7 +#define NV4_CIO_CR_CELL_HT_VBS_9 5 +#define NV4_CIO_CR_CURS_ST_INDEX 0xA +#define NV4_CIO_CR_CURS_END_INDEX 0xB +#define NV4_CIO_CR_SA_HI_INDEX 0xC +#define NV4_CIO_CR_SA_LO_INDEX 0xD +#define NV4_CIO_CR_TCOFF_HI_INDEX 0xE +#define NV4_CIO_CR_TCOFF_LO_INDEX 0xF +#define NV4_CIO_CR_VRS_INDEX 0x10 +#define NV4_CIO_CR_VRE_INDEX 0x11 +#define NV4_CIO_CR_VRE_3_0 0 +#define NV4_CIO_CR_VDE_INDEX 0x12 +#define NV4_CIO_CR_OFFSET_INDEX 0x13 +#define NV4_CIO_CR_ULINE_INDEX 0x14 +#define NV4_CIO_CR_VBS_INDEX 0x15 +#define NV4_CIO_CR_VBE_INDEX 0x16 +#define NV4_CIO_CR_MODE_INDEX 0x17 +#define NV4_CIO_CR_LCOMP_INDEX 0x18 +#define NV4_CIO_CR_GDATA_INDEX 0x22 +#define NV4_CIO_CR_ARFF_INDEX 0x24 +#define NV4_CIO_CR_ARX_INDEX 0x26 +#define NV4_CIO_CRE_MONO 0x3b5 +#define NV4_CIO_CRE_COLOR 0x3d5 +#define NV4_CIO_CRE_RPC0_INDEX 0x19 +#define NV4_CIO_CRE_RPC0_START 0 +#define NV4_CIO_CRE_RPC0_OFFSET_10_8 5 +#define NV4_CIO_CRE_RPC1_INDEX 0x1A +#define NV4_CIO_CRE_RPC1_LARGE 2 +#define NV4_CIO_CRE_FF_INDEX 0x1B +#define NV4_CIO_CRE_FF_BURST 0 +#define NV4_CIO_CRE_FF_BURST_8 0x0 +#define NV4_CIO_CRE_FF_BURST_32 0x1 +#define NV4_CIO_CRE_FF_BURST_64 0x2 +#define NV4_CIO_CRE_FF_BURST_128 0x3 +#define NV4_CIO_CRE_FF_BURST_256 0x4 +#define NV4_CIO_CRE_ENH_INDEX 0x1C +#define NV4_CIO_CRE_PAGE0_INDEX 0x1D +#define NV4_CIO_CRE_PAGE1_INDEX 0x1E +#define NV4_CIO_SR_LOCK_INDEX 0x1F +#define NV4_CIO_SR_UNLOCK_RW_VALUE 0x57 +#define NV4_CIO_SR_UNLOCK_RO_VALUE 0x75 +#define NV4_CIO_SR_LOCK_VALUE 0x99 +#define NV4_SR_UNLOCK_RW_VALUE 0x57 +#define NV4_SR_UNLOCK_RO_VALUE 0x75 +#define NV4_SR_LOCK_VALUE 0x99 +#define NV4_CIO_CRE_FFLWM_INDEX 0x20 +#define NV4_CIO_CRE_FFLWM_LWM 0 +#define NV4_CIO_CRE_FABID_INDEX 0x25 +#define NV4_CIO_CRE_LSR_INDEX 0x25 +#define NV4_CIO_CRE_LSR_VDE_10 1 +#define NV4_CIO_CRE_LSR_VDT_10 0 +#define NV4_CIO_CRE_LSR_HBE_6 4 +#define NV4_CIO_CRE_LSR_VBS_10 3 +#define NV4_CIO_CRE_LSR_VRS_10 2 +#define NV4_CIO_CRE_CHIP_ID_INDEX 0x27 +#define NV4_CIO_CRE_PIXEL_INDEX 0x28 +#define NV4_CIO_CRE_PIXEL_TV_ADJ 3 +#define NV4_CIO_CRE_PIXEL_FORMAT 0 +#define NV4_CIO_CRE_PIXEL_FORMAT_VGA 0x0 +#define NV4_CIO_CRE_PIXEL_FORMAT_8BPP 0x1 +#define NV4_CIO_CRE_PIXEL_FORMAT_16BPP 0x2 +#define NV4_CIO_CRE_PIXEL_FORMAT_32BPP 0x3 +#define NV4_CIO_CRE_PAGE_OVFL_INDEX 0x29 +#define NV4_CIO_CRE_OSCOL_INDEX 0x2A +#define NV4_CIO_CRE_SCRATCH0_INDEX 0x2B +#define NV4_CIO_CRE_SCRATCH1_INDEX 0x2C +#define NV4_CIO_CRE_HEB_INDEX 0x2D +#define NV4_CIO_CRE_HEB_SA_23 5 +#define NV4_CIO_CRE_HEB_ILC_8 4 +#define NV4_CIO_CRE_HEB_HRS_8 3 +#define NV4_CIO_CRE_HEB_HBS_8 2 +#define NV4_CIO_CRE_HEB_HDE_8 1 +#define NV4_CIO_CRE_HEB_HDT_8 0 +#define NV4_CIO_CRE_HCUR_ADDR2_INDEX 0x2f +#define NV4_CIO_CRE_HCUR_ADDR0_INDEX 0x30 +#define NV4_CIO_CRE_HCUR_ASI 7 +#define NV4_CIO_CRE_HCUR_ASI_FRAMEBUFFER 0x1 +#define NV4_CIO_CRE_HCUR_ASI_INSTMEM 0x0 +#define NV4_CIO_CRE_HCUR_ADDR0_ADR 0 +#define NV4_CIO_CRE_HCUR_ADDR1_INDEX 0x31 +#define NV4_CIO_CRE_HCUR_ADDR1_ADR 2 +#define NV4_CIO_CRE_HCUR_ADDR1_CUR_DBL 1 +#define NV4_CIO_CRE_HCUR_ADDR1_ENABLE 0 +#define NV4_CIO_CRE_VID_END0_INDEX 0x32 +#define NV4_CIO_CRE_VID_END_7_0 0 +#define NV4_CIO_CRE_VID_END1_INDEX 0x33 +#define NV4_CIO_CRE_VID_END_ENABLE 4 +#define NV4_CIO_CRE_VID_END_10_8 0 +#define NV4_CIO_CRE_RL0_INDEX 0x34 +#define NV4_CIO_CRE_RL1_INDEX 0x35 +#define NV4_CIO_CRE_RMA_INDEX 0x38 +#define NV4_CIO_CRE_ILACE_INDEX 0x39 +#define NV4_CIO_CRE_SCRATCH2_INDEX 0x3A +#define NV4_CIO_CRE_SCRATCH3_INDEX 0x3B +#define NV4_CIO_CRE_SCRATCH4_INDEX 0x3C +#define NV4_CIO_CRE_TREG_INDEX 0x3D +#define NV4_CIO_CRE_TREG_HCNT 6 +#define NV4_CIO_CRE_TREG_VCNT 4 +#define NV4_CIO_CRE_TREG_SHADOW 0 +#define NV4_CIO_CRE_TREG_HCNT_INDEX 0x0 +#define NV4_CIO_CRE_TREG_VCNTA_INDEX 0x6 +#define NV4_CIO_CRE_TREG_VCNTB_INDEX 0x7 +#define NV4_CIO_CRE_DDC_STATUS_INDEX 0x3E +#define NV4_CIO_CRE_DDC_WR_INDEX 0x3F +#define NV4_CIO_CRE_PCI_TO_INDEX 0x40 +#define NV4_CIO_CRE_PCI_TO_DELAY 0 + +#define NV4_VIO_MBEN 0x94 +#define NV4_VIO_ADDEN 0x46e8 +#define NV4_VIO_VSE1 0x102 +#define NV4_VIO_VSE2 0x3c3 +#define NV4_VIO_MISC_READ 0x3cc +#define NV4_VIO_MISC_WRITE 0x3c2 +#define NV4_VIO_SRX 0x3c4 +#define NV4_VIO_SR 0x3c5 +#define NV4_VIO_SR_RESET_INDEX 0x0 +#define NV4_VIO_SR_CLOCK_INDEX 0x1 +#define NV4_VIO_SR_PLANE_MASK_INDEX 0x2 +#define NV4_VIO_SR_CHAR_MAP_INDEX 0x3 +#define NV4_VIO_SR_MEM_MODE_INDEX 0x4 +#define NV4_VIO_GRX 0x3ce +#define NV4_VIO_GX_SR 0x3cf +#define NV4_VIO_GX_SR_INDEX 0x0 +#define NV4_VIO_GX_SREN_INDEX 0x1 +#define NV4_VIO_GX_CCOMP_INDEX 0x2 +#define NV4_VIO_GX_ROP_INDEX 0x3 +#define NV4_VIO_GX_READ_MAP_INDEX 0x4 +#define NV4_VIO_GX_MODE_INDEX 0x5 +#define NV4_VIO_GX_MISC_INDEX 0x6 +#define NV4_VIO_GX_DONT_CARE_INDEX 0x7 +#define NV4_VIO_GX_BIT_MASK_INDEX 0x8 + +#define NV4_PRMVIO_START 0xC0000 +#define NV4_PRMVIO_END 0xC7FFF + +#define NV4_PRMVIO_MBEN 0xC0094 +#define NV4_PRMVIO_ADDEN 0xC46e8 +#define NV4_PRMVIO_VSE1 0xC0102 +#define NV4_PRMVIO_VSE2 0xC03c3 +#define NV4_PRMVIO_MISC_READ 0xC03cc +#define NV4_PRMVIO_MISC_WRITE 0xC03c2 +#define NV4_PRMVIO_SRX 0xC03c4 +#define NV4_PRMVIO_SR_RESET 0xC03c5 +#define NV4_PRMVIO_SR_RESET_INDEX 0x0 +#define NV4_PRMVIO_SR_CLOCK_INDEX 0x1 +#define NV4_PRMVIO_SR_PLANE_MASK_INDEX 0x2 +#define NV4_PRMVIO_SR_CHAR_MAP_INDEX 0x3 +#define NV4_PRMVIO_SR_MEM_MODE_INDEX 0x4 +#define NV4_PRMVIO_GX_SR 0xC03cf +#define NV4_PRMVIO_GX_SR_INDEX 0x0 +#define NV4_PRMVIO_GX_SREN_INDEX 0x1 +#define NV4_PRMVIO_GX_CCOMP_INDEX 0x2 +#define NV4_PRMVIO_GX_ROP_INDEX 0x3 +#define NV4_PRMVIO_GX_READ_MAP_INDEX 0x4 +#define NV4_PRMVIO_GX_MODE_INDEX 0x5 +#define NV4_PRMVIO_GX_MISC_INDEX 0x6 +#define NV4_PRMVIO_GX_DONT_CARE_INDEX 0x7 +#define NV4_PRMVIO_GX_BIT_MASK_INDEX 0x8 + +#define NV4_PRMVGA 0xA0000 +#define NV4_PRMVGA_END 0xBFFFF + +#define NV4_PME 0x200FFF:0x200000 +#define NV4_PME_DEBUG_0 0x200080 +#define NV4_PME_DEBUG_0_DET_FIELD_SWITCH 0 +#define NV4_PME_DEBUG_0_DET_FIELD_SWITCH_ENABLED 0x1 +#define NV4_PME_DEBUG_0_CAPTURE_00_FF 4 +#define NV4_PME_DEBUG_0_CAPTURE_00_FF_ENABLED 0x1 +#define NV4_PME_DEBUG_1 0x200084 +#define NV4_PME_DEBUG_1_SEL 0 +#define NV4_PME_DEBUG_1_SEL_VIPCLK 0x0 +#define NV4_PME_DEBUG_1_SEL_MCLK 0x1 +#define NV4_PME_DEBUG_1_SEL_GLOB 0x2 +#define NV4_PME_DEBUG_1_VIPCLK_SEL 4 +#define NV4_PME_DEBUG_1_VIPCLK_SEL_DEFAULT 0x0 +#define NV4_PME_DEBUG_1_MCLK_SEL 8 +#define NV4_PME_DEBUG_1_MCLK_SEL_DEFAULT 0x0 +#define NV4_PME_INTR_0 0x200100 +#define NV4_PME_INTR_0_IMAGE_NOTIFY 0 +#define NV4_PME_INTR_0_IMAGE_NOTIFY_NOT_PENDING 0x0 +#define NV4_PME_INTR_0_IMAGE_NOTIFY_PENDING 0x1 +#define NV4_PME_INTR_0_IMAGE_NOTIFY_RESET 0x1 +#define NV4_PME_INTR_0_VBI_NOTIFY 4 +#define NV4_PME_INTR_0_VBI_NOTIFY_NOT_PENDING 0x0 +#define NV4_PME_INTR_0_VBI_NOTIFY_PENDING 0x1 +#define NV4_PME_INTR_0_VBI_NOTIFY_RESET 0x1 +#define NV4_PME_INTR_0_VID_NOTIFY 8 +#define NV4_PME_INTR_0_VID_NOTIFY_NOT_PENDING 0x0 +#define NV4_PME_INTR_0_VID_NOTIFY_PENDING 0x1 +#define NV4_PME_INTR_0_VID_NOTIFY_RESET 0x1 +#define NV4_PME_INTR_0_AUD_NOTIFY 12 +#define NV4_PME_INTR_0_AUD_NOTIFY_NOT_PENDING 0x0 +#define NV4_PME_INTR_0_AUD_NOTIFY_PENDING 0x1 +#define NV4_PME_INTR_0_AUD_NOTIFY_RESET 0x1 +#define NV4_PME_INTR_0_VMI 16 +#define NV4_PME_INTR_0_VMI_NOT_PENDING 0x0 +#define NV4_PME_INTR_0_VMI_PENDING 0x1 +#define NV4_PME_INTR_0_VMI_RESET 0x1 +#define NV4_PME_INTR_EN_0 0x200140 +#define NV4_PME_INTR_EN_0_IMAGE_NOTIFY 0 +#define NV4_PME_INTR_EN_0_IMAGE_NOTIFY_ENABLED 0x1 +#define NV4_PME_INTR_EN_0_VBI_NOTIFY 4 +#define NV4_PME_INTR_EN_0_VBI_NOTIFY_ENABLED 0x1 +#define NV4_PME_INTR_EN_0_VID_NOTIFY 8 +#define NV4_PME_INTR_EN_0_VID_NOTIFY_ENABLED 0x1 +#define NV4_PME_INTR_EN_0_AUD_NOTIFY 12 +#define NV4_PME_INTR_EN_0_AUD_NOTIFY_ENABLED 0x1 +#define NV4_PME_INTR_EN_0_VMI 16 +#define NV4_PME_INTR_EN_0_VMI_ENABLED 0x1 +#define NV4_PME_CONFIG_0 0x200200 +#define NV4_PME_CONFIG_0_BUS_MODE 0 +#define NV4_PME_CONFIG_0_BUS_MODE_DISABLED 0x0 +#define NV4_PME_CONFIG_0_BUS_MODE_VMI 0x1 +#define NV4_PME_CONFIG_0_BUS_MODE_CCIR656 0x2 +#define NV4_PME_CONFIG_0_IMAGE 4 +#define NV4_PME_CONFIG_0_IMAGE_ENABLED 0x1 +#define NV4_PME_CONFIG_0_VBI_MODE 8 +#define NV4_PME_CONFIG_0_VBI_MODE_DISABLED 0x0 +#define NV4_PME_CONFIG_0_VBI_MODE_1 0x1 +#define NV4_PME_CONFIG_0_VBI_MODE_2 0x2 +#define NV4_PME_CONFIG_0_VID_CD 12 +#define NV4_PME_CONFIG_0_VID_CD_ENABLED 0x1 +#define NV4_PME_CONFIG_0_AUD_CD 16 +#define NV4_PME_CONFIG_0_AUD_CD_ENABLED 0x1 +#define NV4_PME_CONFIG_1 0x200204 +#define NV4_PME_CONFIG_1_BUFFS 0 +#define NV4_PME_CONFIG_1_BUFFS_PNVM 0x0 +#define NV4_PME_CONFIG_1_BUFFS_SYS 0x1 +#define NV4_PME_CONFIG_1_HOST 4 +#define NV4_PME_CONFIG_1_HOST_PCI 0x0 +#define NV4_PME_CONFIG_1_HOST_AGP 0x1 +#define NV4_PME_NULL_DATA 0x200208 +#define NV4_PME_NULL_DATA_COMPARE 0 +#define NV4_PME_NULL_DATA_COMPARE_ENABLED 0x1 +#define NV4_PME_NULL_DATA_LINE_DETECT 4 +#define NV4_PME_NULL_DATA_LINE_DETECT_ENABLED 0x1 +#define NV4_PME_NULL_DATA_BYTE 24 +#define NV4_PME_VID_BUFF0_START_SYS 0x200300 +#define NV4_PME_VID_BUFF0_START_SYS_ADDRESS 4 +#define NV4_PME_VID_BUFF1_START_SYS 0x200304 +#define NV4_PME_VID_BUFF1_START_SYS_ADDRESS 4 +#define NV4_PME_VID_BUFF0_START_PNVM 0x200308 +#define NV4_PME_VID_BUFF0_START_PNVM_ADDRESS 4 +#define NV4_PME_VID_BUFF1_START_PNVM 0x20030c +#define NV4_PME_VID_BUFF1_START_PNVM_ADDRESS 4 +#define NV4_PME_VID_BUFF0_LENGTH 0x200310 +#define NV4_PME_VID_BUFF0_LENGTH_BITS 12 +#define NV4_PME_VID_BUFF1_LENGTH 0x200314 +#define NV4_PME_VID_BUFF1_LENGTH_BITS 12 +#define NV4_PME_VID_ME_STATE 0x200318 +#define NV4_PME_VID_ME_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_VID_ME_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_VID_ME_STATE_BUFF0_INTR_CHAINGAP 8 +#define NV4_PME_VID_ME_STATE_BUFF1_INTR_CHAINGAP 12 +#define NV4_PME_VID_ME_STATE_BUFF0_IN_USE 16 +#define NV4_PME_VID_ME_STATE_BUFF1_IN_USE 20 +#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER 24 +#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER_0 0x0 +#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER_1 0x1 +#define NV4_PME_VID_SU_STATE 0x20031c +#define NV4_PME_VID_SU_STATE_BUFF0_IN_USE 16 +#define NV4_PME_VID_SU_STATE_BUFF1_IN_USE 20 +#define NV4_PME_VID_RM_STATE 0x200320 +#define NV4_PME_VID_RM_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_VID_RM_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_VID_RM_STATE_BUFF0_INTR_CHAINGAP 8 +#define NV4_PME_VID_RM_STATE_BUFF1_INTR_CHAINGAP 12 +#define NV4_PME_VID_CURRENT 0x200324 +#define NV4_PME_VID_CURRENT_POS 2 +#define NV4_PME_AUD_BUFF0_START_SYS 0x200340 +#define NV4_PME_AUD_BUFF0_START_SYS_ADDRESS 4 +#define NV4_PME_AUD_BUFF1_START_SYS 0x200344 +#define NV4_PME_AUD_BUFF1_START_SYS_ADDRESS 4 +#define NV4_PME_AUD_BUFF0_START_PNVM 0x200348 +#define NV4_PME_AUD_BUFF0_START_PNVM_ADDRESS 4 +#define NV4_PME_AUD_BUFF1_START_PNVM 0x20034c +#define NV4_PME_AUD_BUFF1_START_PNVM_ADDRESS 4 +#define NV4_PME_AUD_BUFF0_LENGTH 0x200350 +#define NV4_PME_AUD_BUFF0_LENGTH_BITS 10 +#define NV4_PME_AUD_BUFF1_LENGTH 0x200354 +#define NV4_PME_AUD_BUFF1_LENGTH_BITS 10 +#define NV4_PME_AUD_ME_STATE 0x200358 +#define NV4_PME_AUD_ME_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_AUD_ME_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_AUD_ME_STATE_BUFF0_INTR_CHAINGAP 8 +#define NV4_PME_AUD_ME_STATE_BUFF1_INTR_CHAINGAP 12 +#define NV4_PME_AUD_ME_STATE_BUFF0_IN_USE 16 +#define NV4_PME_AUD_ME_STATE_BUFF1_IN_USE 20 +#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER 24 +#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER_0 0x0 +#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER_1 0x1 +#define NV4_PME_AUD_SU_STATE 0x20035c +#define NV4_PME_AUD_SU_STATE_BUFF0_IN_USE 16 +#define NV4_PME_AUD_SU_STATE_BUFF1_IN_USE 20 +#define NV4_PME_AUD_RM_STATE 0x200360 +#define NV4_PME_AUD_RM_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_AUD_RM_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_AUD_RM_STATE_BUFF0_INTR_CHAINGAP 8 +#define NV4_PME_AUD_RM_STATE_BUFF1_INTR_CHAINGAP 12 +#define NV4_PME_AUD_CURRENT 0x200364 +#define NV4_PME_AUD_CURRENT_POS 2 +#define NV4_PME_VBI_BUFF0_START 0x200380 +#define NV4_PME_VBI_BUFF0_START_ADDRESS 4 +#define NV4_PME_VBI_BUFF1_START 0x200384 +#define NV4_PME_VBI_BUFF1_START_ADDRESS 4 +#define NV4_PME_VBI_BUFF0_PITCH 0x200388 +#define NV4_PME_VBI_BUFF0_PITCH_VALUE 4 +#define NV4_PME_VBI_BUFF1_PITCH 0x20038c +#define NV4_PME_VBI_BUFF1_PITCH_VALUE 4 +#define NV4_PME_VBI_BUFF0_LENGTH 0x200390 +#define NV4_PME_VBI_BUFF0_LENGTH_BITS 4 +#define NV4_PME_VBI_BUFF1_LENGTH 0x200394 +#define NV4_PME_VBI_BUFF1_LENGTH_BITS 4 +#define NV4_PME_VBI_ME_STATE 0x200398 +#define NV4_PME_VBI_ME_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_VBI_ME_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_VBI_ME_STATE_BUFF0_ERROR_CODE 8 +#define NV4_PME_VBI_ME_STATE_BUFF1_ERROR_CODE 12 +#define NV4_PME_VBI_ME_STATE_BUFF0_IN_USE 16 +#define NV4_PME_VBI_ME_STATE_BUFF1_IN_USE 20 +#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER 24 +#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER_0 0x0 +#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER_1 0x1 +#define NV4_PME_VBI_SU_STATE 0x20039c +#define NV4_PME_VBI_SU_STATE_BUFF0_FIELD 8 +#define NV4_PME_VBI_SU_STATE_BUFF1_FIELD 12 +#define NV4_PME_VBI_SU_STATE_BUFF0_IN_USE 16 +#define NV4_PME_VBI_SU_STATE_BUFF1_IN_USE 20 +#define NV4_PME_VBI_RM_STATE 0x2003a0 +#define NV4_PME_VBI_RM_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_VBI_RM_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_VBI 0x2003a4 +#define NV4_PME_VBI_START_LINE 0 +#define NV4_PME_VBI_NUM_LINES 16 +#define NV4_PME_IMAGE_BUFF0_START 0x200400 +#define NV4_PME_IMAGE_BUFF0_START_ADDRESS 4 +#define NV4_PME_IMAGE_BUFF1_START 0x200404 +#define NV4_PME_IMAGE_BUFF1_START_ADDRESS 4 +#define NV4_PME_IMAGE_BUFF0_PITCH 0x200408 +#define NV4_PME_IMAGE_BUFF0_PITCH_VALUE 4 +#define NV4_PME_IMAGE_BUFF1_PITCH 0x20040c +#define NV4_PME_IMAGE_BUFF1_PITCH_VALUE 4 +#define NV4_PME_IMAGE_BUFF0_LENGTH 0x200410 +#define NV4_PME_IMAGE_BUFF0_LENGTH_BITS 4 +#define NV4_PME_IMAGE_BUFF1_LENGTH 0x200414 +#define NV4_PME_IMAGE_BUFF1_LENGTH_BITS 4 +#define NV4_PME_IMAGE_ME_STATE 0x200418 +#define NV4_PME_IMAGE_ME_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_IMAGE_ME_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_IMAGE_ME_STATE_BUFF0_ERROR_CODE 8 +#define NV4_PME_IMAGE_ME_STATE_BUFF1_ERROR_CODE 12 +#define NV4_PME_IMAGE_ME_STATE_BUFF0_IN_USE 16 +#define NV4_PME_IMAGE_ME_STATE_BUFF1_IN_USE 20 +#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER 24 +#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER_0 0x0 +#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER_1 0x1 +#define NV4_PME_IMAGE_SU_STATE 0x20041c +#define NV4_PME_IMAGE_SU_STATE_BUFF0_FIELD 8 +#define NV4_PME_IMAGE_SU_STATE_BUFF1_FIELD 12 +#define NV4_PME_IMAGE_SU_STATE_BUFF0_IN_USE 16 +#define NV4_PME_IMAGE_SU_STATE_BUFF1_IN_USE 20 +#define NV4_PME_IMAGE_RM_STATE 0x200420 +#define NV4_PME_IMAGE_RM_STATE_BUFF0_INTR_NOTIFY 0 +#define NV4_PME_IMAGE_RM_STATE_BUFF1_INTR_NOTIFY 4 +#define NV4_PME_IMAGE_BUFF0_SCALE_INCR 0x200424 +#define NV4_PME_IMAGE_BUFF0_SCALE_INCR_Y 16 +#define NV4_PME_IMAGE_BUFF0_SCALE_INCR_X 0 +#define NV4_PME_IMAGE_BUFF1_SCALE_INCR 0x200428 +#define NV4_PME_IMAGE_BUFF1_SCALE_INCR_Y 16 +#define NV4_PME_IMAGE_BUFF1_SCALE_INCR_X 0 +#define NV4_PME_IMAGE_Y_CROP 0x20042c +#define NV4_PME_IMAGE_Y_CROP_STARTLINE 0 +#define NV4_PME_FIFO_LINE_START 0x200480 +#define NV4_PME_FIFO_LINE_START_ADDRESS 4 +#define NV4_PME_FIFO_CURRENT 0x200484 +#define NV4_PME_FIFO_CURRENT_ADDRESS 2 +#define NV4_PME_VMI_POLL 0x200488 +#define NV4_PME_VMI_POLL_UNCD 0 +#define NV4_PME_VMI_POLL_UNCD_NOT_PENDING 0x0 +#define NV4_PME_VMI_POLL_UNCD_PENDING 0x1 +#define NV4_PME_VMI_POLL_VIDCD 1 +#define NV4_PME_VMI_POLL_VIDCD_NOT_PENDING 0x0 +#define NV4_PME_VMI_POLL_VIDCD_PENDING 0x1 +#define NV4_PME_VMI_POLL_AUDCD 2 +#define NV4_PME_VMI_POLL_AUDCD_NOT_PENDING 0x0 +#define NV4_PME_VMI_POLL_AUDCD_PENDING 0x1 +#define NV4_PME_VMI_POLL_INT 3 +#define NV4_PME_VMI_POLL_INT_NOT_PENDING 0x0 +#define NV4_PME_VMI_POLL_INT_PENDING 0x1 +#define NV4_PME_VMI_POLL_CPURDREC 4 +#define NV4_PME_VMI_POLL_CPURDREC_NOT_PENDING 0x0 +#define NV4_PME_VMI_POLL_CPURDREC_PENDING 0x1 +#define NV4_PME_EXTERNAL(i) (0x200600+(i)*4) +#define NV4_PME_EXTERNAL_SIZE_1 256 +#define NV4_PME_EXTERNAL_DATA 0 + +#define NV4_PFB_START 0x100000 +#define NV4_PFB_END 0x100FFF + +#define NV4_PFB_BOOT_0 0x100000 +#define NV4_PFB_BOOT_0_RAM_AMOUNT 0 +#define NV4_PFB_BOOT_0_RAM_AMOUNT_2MB 0x0 +#define NV4_PFB_BOOT_0_RAM_AMOUNT_4MB 0x1 +#define NV4_PFB_BOOT_0_RAM_AMOUNT_8MB 0x2 +#define NV4_PFB_BOOT_0_RAM_AMOUNT_16MB 0x3 +#define NV4_PFB_BOOT_0_RAM_WIDTH_128 2 +#define NV4_PFB_BOOT_0_RAM_WIDTH_128_OFF 0x0 +#define NV4_PFB_BOOT_0_RAM_WIDTH_128_ON 0x1 +#define NV4_PFB_BOOT_0_RAM_TYPE 3 +#define NV4_PFB_BOOT_0_RAM_TYPE_256K 0x0 +#define NV4_PFB_BOOT_0_RAM_TYPE_512K_2BANK 0x1 +#define NV4_PFB_BOOT_0_RAM_TYPE_512K_4BANK 0x2 +#define NV4_PFB_BOOT_0_RAM_TYPE_1024K_2BANK 0x3 +#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x0 +#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x1 +#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x2 +#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x3 +#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x4 +#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x5 +#define NV4_PFB_BOOT_0_UMA 8 +#define NV4_PFB_BOOT_0_UMA_DISABLE 0x0 +#define NV4_PFB_BOOT_0_UMA_ENABLE 0x1 +#define NV4_PFB_BOOT_0_UMA_SIZE 12 +#define NV4_PFB_BOOT_0_UMA_SIZE_DEFAULT 0x7 +#define NV4_PFB_BOOT_0_UMA_SIZE_2M 0x0 +#define NV4_PFB_BOOT_0_UMA_SIZE_4M 0x1 +#define NV4_PFB_BOOT_0_UMA_SIZE_6M 0x2 +#define NV4_PFB_BOOT_0_UMA_SIZE_8M 0x3 +#define NV4_PFB_BOOT_0_UMA_SIZE_10M 0x4 +#define NV4_PFB_BOOT_0_UMA_SIZE_12M 0x5 +#define NV4_PFB_BOOT_0_UMA_SIZE_14M 0x6 +#define NV4_PFB_BOOT_0_UMA_SIZE_16M 0x7 +#define NV4_PFB_BOOT_0_UMA_SIZE_18M 0x8 +#define NV4_PFB_BOOT_0_UMA_SIZE_20M 0x9 +#define NV4_PFB_BOOT_0_UMA_SIZE_22M 0xA +#define NV4_PFB_BOOT_0_UMA_SIZE_24M 0xB +#define NV4_PFB_BOOT_0_UMA_SIZE_26M 0xC +#define NV4_PFB_BOOT_0_UMA_SIZE_28M 0xD +#define NV4_PFB_BOOT_0_UMA_SIZE_30M 0xE +#define NV4_PFB_BOOT_0_UMA_SIZE_32M 0xF +#define NV4_PFB_DEBUG_0 0x100080 +#define NV4_PFB_DEBUG_0_PAGE_MODE 0 +#define NV4_PFB_DEBUG_0_PAGE_MODE_ENABLED 0x0 +#define NV4_PFB_DEBUG_0_PAGE_MODE_DISABLED 0x1 +#define NV4_PFB_DEBUG_0_REFRESH 4 +#define NV4_PFB_DEBUG_0_REFRESH_ENABLED 0x0 +#define NV4_PFB_DEBUG_0_REFRESH_DISABLED 0x1 +#define NV4_PFB_DEBUG_0_REFRESH_COUNTX64 8 +#define NV4_PFB_DEBUG_0_REFRESH_COUNTX64_DEFAULT 0x10 +#define NV4_PFB_DEBUG_0_REFRESH_SLOW_CLK 14 +#define NV4_PFB_DEBUG_0_REFRESH_SLOW_CLK_ENABLED 0x1 +#define NV4_PFB_DEBUG_0_CASOE 20 +#define NV4_PFB_DEBUG_0_CASOE_ENABLED 0x0 +#define NV4_PFB_DEBUG_0_CASOE_DISABLED 0x1 +#define NV4_PFB_DEBUG_0_CKE_INVERT 28 +#define NV4_PFB_DEBUG_0_CKE_INVERT_OFF 0x0 +#define NV4_PFB_DEBUG_0_CKE_INVERT_ON 0x1 +#define NV4_PFB_DEBUG_0_REFINC 29 +#define NV4_PFB_DEBUG_0_REFINC_DISABLED 0x0 +#define NV4_PFB_DEBUG_0_REFINC_ENABLED 0x1 +#define NV4_PFB_DEBUG_0_SAVE_POWER 30 +#define NV4_PFB_DEBUG_0_SAVE_POWER_ON 0x0 +#define NV4_PFB_DEBUG_0_SAVE_POWER_OFF 0x1 +#define NV4_PFB_GREEN_0 0x1000C0 +#define NV4_PFB_GREEN_0_LEVEL 0 +#define NV4_PFB_GREEN_0_LEVEL_VIDEO_ENABLED 0x0 +#define NV4_PFB_GREEN_0_LEVEL_VIDEO_DISABLED 0x1 +#define NV4_PFB_GREEN_0_LEVEL_TIMING_DISABLED 0x2 +#define NV4_PFB_GREEN_0_LEVEL_MEMORY_DISABLED 0x3 +#define NV4_PFB_CONFIG_0 0x100200 +#define NV4_PFB_CONFIG_0_TYPE 0 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_8BPP 0x120 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_16BPP 0x220 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_32BPP 0x320 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_8BPP 0x4120 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_16BPP 0x4220 +#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_32BPP 0x4320 +#define NV4_PFB_CONFIG_0_TYPE_TETRIS 0x2000 +#define NV4_PFB_CONFIG_0_TYPE_NOTILING 0x1114 +#define NV4_PFB_CONFIG_0_TETRIS_MODE 15 +// 18:15 = tetris mode # +#define NV4_PFB_CONFIG_0_TETRIS_MODE_PASS 0x0 +// 18 = shift # +#define NV4_PFB_CONFIG_0_TETRIS_SHIFT 18 +#define NV4_PFB_CONFIG_0_BANK_SWAP 20 +#define NV4_PFB_CONFIG_0_BANK_SWAP_OFF 0x0 +#define NV4_PFB_CONFIG_0_BANK_SWAP_1M 0x1 +#define NV4_PFB_CONFIG_0_BANK_SWAP_2M 0x5 +#define NV4_PFB_CONFIG_0_BANK_SWAP_4M 0x7 +#define NV4_PFB_CONFIG_0_UNUSED 23 +#define NV4_PFB_CONFIG_0_SCRAMBLE_EN 29 +#define NV4_PFB_CONFIG_0_SCRAMBLE_EN_INIT 0x0 +#define NV4_PFB_CONFIG_0_SCRAMBLE_ACTIVE 0x1 +#define NV4_PFB_CONFIG_0_PRAMIN_WR 28 +#define NV4_PFB_CONFIG_0_PRAMIN_WR_INIT 0x0 +#define NV4_PFB_CONFIG_0_PRAMIN_WR_DISABLED 0x1 +#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK 24 +#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK_INIT 0x0 +#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK_CLEAR 0xF +#define NV4_PFB_CONFIG_1 0x100204 +#define NV4_PFB_CONFIG_1_CAS_LATENCY 0 +#define NV4_PFB_CONFIG_1_CAS_LATENCY_3 0x3 +#define NV4_PFB_CONFIG_1_CAS_LATENCY_2 0x2 +#define NV4_PFB_CONFIG_1_CAS_LATENCY_4 0x4 +#define NV4_PFB_CONFIG_1_RAS_RAS 4 +#define NV4_PFB_CONFIG_1_RAS_RAS_DEFAULT 0x9 +#define NV4_PFB_CONFIG_1_RAS_RAS_9CYCLES 0x8 +#define NV4_PFB_CONFIG_1_RAS_RAS_8CYCLES 0x7 +#define NV4_PFB_CONFIG_1_RAS_RAS_7CYCLES 0x6 +#define NV4_PFB_CONFIG_1_RAS_PCHG 8 +#define NV4_PFB_CONFIG_1_RAS_PCHG_DEFAULT 0x2 +#define NV4_PFB_CONFIG_1_RAS_PCHG_2CYCLES 0x1 +#define NV4_PFB_CONFIG_1_RAS_LOW 12 +#define NV4_PFB_CONFIG_1_RAS_LOW_DEFAULT 0x6 +#define NV4_PFB_CONFIG_1_RAS_LOW_7CYCLES 0x7 +#define NV4_PFB_CONFIG_1_RAS_LOW_5CYCLES 0x5 +#define NV4_PFB_CONFIG_1_RAS_LOW_4CYCLES 0x4 +#define NV4_PFB_CONFIG_1_MRS_TO_RAS 16 +#define NV4_PFB_CONFIG_1_MRS_TO_RAS_DEFAULT 0x1 +#define NV4_PFB_CONFIG_1_MRS_TO_RAS_2CYCLES 0x2 +#define NV4_PFB_CONFIG_1_MRS_TO_RAS_0CYCLES 0x0 +#define NV4_PFB_CONFIG_1_WRITE_TO_READ 20 +#define NV4_PFB_CONFIG_1_WRITE_TO_READ_DEFAULT 0x0 +#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1 24 +#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_DEFAULT 0x1 +#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_2CYCLES 0x2 +#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_0CYCLES 0x0 +#define NV4_PFB_CONFIG_1_READ_TO_WRITE 28 +#define NV4_PFB_CONFIG_1_READ_TO_WRITE_DEFAULT 0x4 +#define NV4_PFB_CONFIG_1_READ_TO_WRITE_5CYCLES 0x5 +#define NV4_PFB_CONFIG_1_READ_TO_WRITE_3CYCLES 0x3 +#define NV4_PFB_CONFIG_1_READ_TO_WRITE_2CYCLES 0x2 +#define NV4_PFB_CONFIG_1_READ_TO_PCHG 31 +#define NV4_PFB_CONFIG_1_READ_TO_PCHG_ON 0x1 +#define NV4_PFB_CONFIG_1_READ_TO_PCHG_OFF 0x0 +#define NV4_PFB_RTL 0x100300 +#define NV4_PFB_RTL_H 0 +#define NV4_PFB_RTL_H_DEFAULT 0x0 +#define NV4_PFB_RTL_MC 1 +#define NV4_PFB_RTL_MC_DEFAULT 0x0 +#define NV4_PFB_RTL_V 2 +#define NV4_PFB_RTL_V_DEFAULT 0x0 +#define NV4_PFB_RTL_G 3 +#define NV4_PFB_RTL_G_DEFAULT 0x0 +#define NV4_PFB_RTL_GB 4 +#define NV4_PFB_RTL_GB_DEFAULT 0x0 +#define NV4_PFB_SCRAMBLE(i) (0x100400+((i)*4)) +#define NV4_PFB_SCRAMBLE_SIZE_1 8 +#define NV4_PFB_SCRAMBLE_w0 0 +#define NV4_PFB_SCRAMBLE_w1 8 +#define NV4_PFB_SCRAMBLE_w2 16 +#define NV4_PFB_SCRAMBLE_w3 24 +#define NV4_PFB_SCRAMBLE_EN 0x100420 +#define NV4_PFB_SCRAMBLE_VALUE_0 0x03020100 +#define NV4_PFB_SCRAMBLE_VALUE_1 0x07060504 +#define NV4_PFB_SCRAMBLE_VALUE_2 0x0b0a0908 +#define NV4_PFB_SCRAMBLE_VALUE_3 0x0f0e0d0c +#define NV4_PFB_SCRAMBLE_VALUE_4 0x13121110 +#define NV4_PFB_SCRAMBLE_VALUE_5 0x17161514 +#define NV4_PFB_SCRAMBLE_VALUE_6 0x1b1a1918 +#define NV4_PFB_SCRAMBLE_VALUE_7 0x1f1e1d1c +#define NV4_PFB_CONFIG_0_RESOLUTION 0 +#define NV4_PFB_CONFIG_0_RESOLUTION_320_PIXELS 0xA +#define NV4_PFB_CONFIG_0_RESOLUTION_400_PIXELS 0xD +#define NV4_PFB_CONFIG_0_RESOLUTION_480_PIXELS 0xF +#define NV4_PFB_CONFIG_0_RESOLUTION_512_PIXELS 0x10 +#define NV4_PFB_CONFIG_0_RESOLUTION_640_PIXELS 0x14 +#define NV4_PFB_CONFIG_0_RESOLUTION_800_PIXELS 0x19 +#define NV4_PFB_CONFIG_0_RESOLUTION_960_PIXELS 0x1e +#define NV4_PFB_CONFIG_0_RESOLUTION_1024_PIXELS 0x20 +#define NV4_PFB_CONFIG_0_RESOLUTION_1152_PIXELS 0x24 +#define NV4_PFB_CONFIG_0_RESOLUTION_1280_PIXELS 0x28 +#define NV4_PFB_CONFIG_0_RESOLUTION_1600_PIXELS 0x32 +#define NV4_PFB_CONFIG_0_RESOLUTION_DEFAULT 0x14 +#define NV4_PFB_CONFIG_0_PIXEL_DEPTH 8 +#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_8_BITS 0x1 +#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_16_BITS 0x2 +#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_32_BITS 0x3 +#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_DEFAULT 0x1 +#define NV4_PFB_CONFIG_0_TILING 12 +#define NV4_PFB_CONFIG_0_TILING_ENABLED 0x0 +#define NV4_PFB_CONFIG_0_TILING_DISABLED 0x1 +#define NV4_PFB_CONFIG_0_TILING_DEBUG 13 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_DISABLED 0x0 +#define NV4_PFB_CONFIG_0_TILE 12 +#define NV4_PFB_CONFIG_0_TILE_OLD1024_FIXED 0x0 +#define NV4_PFB_CONFIG_0_TILE_OLD1024_VARIABLE 0x4 +#define NV4_PFB_CONFIG_0_TILE_TETRIS_ALLOW 0x1 +#define NV4_PFB_CONFIG_0_TILE_TETRIS_REDUNDANT 0x2 +#define NV4_PFB_CONFIG_0_TILE_TETRIS_REDUNDANT2 0x3 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON 13 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON_ENABLED 0x0 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON_DISABLED 0x1 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE 14 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE_FIXED 0x0 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE_VARIABLE 0x1 +// tetris mode # = 17:15 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_MODE 15 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_MODE_PASS 0x0 +// tetris shift # = 18 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_SHIFT 18 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP 20 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_OFF 0x0 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_ON 0x1 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB 21 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_1M 0x0 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_2M 0x2 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_4M 0x3 +#define NV4_PFB_CONFIG_0_TILING_DEBUG_UNUSED 23 +#define NV4_PFB_CONFIG_1_SGRAM100 3 +#define NV4_PFB_CONFIG_1_SGRAM100_ENABLED 0x0 +#define NV4_PFB_CONFIG_1_SGRAM100_DISABLED 0x1 +#define NV4_PFB_DEBUG_0_CKE_ALWAYSON 29 +#define NV4_PFB_DEBUG_0_CKE_ALWAYSON_OFF 0x0 +#define NV4_PFB_DEBUG_0_CKE_ALWAYSON_ON 0x1 + +// WARNING! WARNING! WARNING! +// STB V4400 has shown PROM at 0x700000 instead of 0x300000 (errata), clashing with first 0x10000 of instance memory +// Nvidia never ran into this issue because they never used that area +// But this part may not work +#define NV4_PRAMIN_START 0x700000 +#define NV4_PRAMIN_END 0x7FFFFF + +#define NV4_PRAMIN_CONTEXT_0 ( 0*32+31):( 0*32+ 0) +#define NV4_PRAMIN_CONTEXT_1 ( 1*32+31):( 1*32+ 0) +#define NV4_PRAMIN_CONTEXT_2 ( 2*32+31):( 2*32+ 0) +#define NV4_PRAMIN_CONTEXT_3 ( 3*32+31):( 3*32+ 0) +#define NV4_PRAMIN_RAMHT_START 0x710000 +#define NV4_PRAMIN_RAMHT_END 0x710FFF +#define NV4_PRAMIN_RAMFC_START 0x711000 +#define NV4_PRAMIN_RAMFC_END 0x7111FF +#define NV4_PRAMIN_RAMRO_START 0x711200 +#define NV4_PRAMIN_RAMRO_END 0x7113FF +#define NV4_PRAMIN_CTX_0(i) (0x700000 + (i)*16) +// This is the same format as PGRAPH_CTX_SWITCH +#define NV4_PRAMIN_CTX_0_SIZE_1 0x10000 +#define NV4_PRAMIN_CTX_0_NVCLASS 0 +#define NV4_PRAMIN_CTX_0_CHROMA_KEY 12 +#define NV4_PRAMIN_CTX_0_CHROMA_KEY_ENABLE 0x1 +#define NV4_PRAMIN_CTX_0_USER_CLIP 13 +#define NV4_PRAMIN_CTX_0_USER_CLIP_ENABLE 0x1 +#define NV4_PRAMIN_CTX_0_SWIZZLE 14 +#define NV4_PRAMIN_CTX_0_SWIZZLE_ENABLE 0x1 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG 17:15 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY_AND 0x0 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_ROP_AND 0x1 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_BLEND_AND 0x2 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY 0x3 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY_PRE 0x4 +#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_BLEND_PRE 0x5 +#define NV4_PRAMIN_CTX_0_PATCH_STATUS 24 +#define NV4_PRAMIN_CTX_0_PATCH_STATUS_INVALID 0x0 +#define NV4_PRAMIN_CTX_0_PATCH_STATUS_VALID 0x1 +#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE 25 +#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE_INVALID 0x0 +#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE_VALID 0x1 +#define NV4_PRAMIN_CTX_1(i) (0x700004 + (i)*16) +#define NV4_PRAMIN_CTX_1_SIZE_1 0x10000 +#define NV4_PRAMIN_CTX_1_MONO_FORMAT 0 +#define NV4_PRAMIN_CTX_1_MONO_FORMAT_INVALID 0x00 +#define NV4_PRAMIN_CTX_1_MONO_FORMAT_CGA6_M1 0x01 +#define NV4_PRAMIN_CTX_1_MONO_FORMAT_LE_M1 0x02 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT 8 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_INVALID 0x00 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y8 0x01 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16A8Y8 0x02 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X24Y8 0x03 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A1R5G5B5 0x06 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X1R5G5B5 0x07 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16A1R5G5B5 0x08 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X17R5G5B5 0x09 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_R5G6B5 0x0A +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A16R5G6B5 0x0B +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16R5G6B5 0x0C +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A8R8G8B8 0x0D +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X8R8G8B8 0x0E +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y16 0x0F +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A16Y16 0x10 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16Y16 0x11 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_V8YB8U8YA8 0x12 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_YB8V8YA8U8 0x13 +#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y32 0x14 +#define NV4_PRAMIN_CTX_1_NOTIFY_INSTANCE 16 +#define NV4_PRAMIN_CTX_1_NOTIFY_INSTANCE_INVALID 0x00 +#define NV4_PRAMIN_CTX_2(i) (0x700008 + (i)*16) +#define NV4_PRAMIN_CTX_2_SIZE_1 0x10000 +#define NV4_PRAMIN_CTX_2_DMA_0_INSTANCE 0 +#define NV4_PRAMIN_CTX_2_DMA_0_INSTANCE_INVALID 0x00 +#define NV4_PRAMIN_CTX_2_DMA_1_INSTANCE 16 +#define NV4_PRAMIN_CTX_2_DMA_1_INSTANCE_INVALID 0x00 + +#define NV4_FIFO_DMA_OPCODE ( 0*32+31):( 0*32+29) +#define NV4_FIFO_DMA_OPCODE_METHOD 0x0 +#define NV4_FIFO_DMA_OPCODE_JUMP 0x1 +#define NV4_FIFO_DMA_METHOD_COUNT ( 0*32+28):( 0*32+18) +#define NV4_FIFO_DMA_METHOD_SUBCHANNEL ( 0*32+15):( 0*32+13) +#define NV4_FIFO_DMA_METHOD_ADDRESS ( 0*32+12):( 0*32+ 2) +#define NV4_FIFO_DMA_DATA ( 1*32+31):( 1*32+ 0) +#define NV4_FIFO_DMA_JUMP_OFFSET 2 + +// DFB is in BAR1. Access it as VRAM + +#define NV4_PEXTDEV_BOOT_0 0x101000 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED 0 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED_33MHZ 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED_66MHZ 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR 1 +#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR_NO_BIOS 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR_BIOS 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE 2 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_256K 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_512K_2BANK 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_512K_4BANK 0x2 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_1024K_2BANK 0x3 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH 4 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH_64 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH_128 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE 5 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE_PCI 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE_AGP 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL 6 +#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL_13500K 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL_14318180 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE 7 +#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_SECAM 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_NTSC 0x1 +#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_PAL 0x2 +#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_DISABLED 0x3 +#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE 11 +#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE_DISABLED 0x0 +#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE_ENABLED 0x1 + +#define NV4_PDAC_START 0x680000 +#define NV4_PDAC_END 0x680FFF + +#define NV4_PDAC_DATA(i) (0x680000+(i)*4) +#define NV4_PDAC_DATA_SIZE_1 16 +#define NV4_PDAC_DATA_VALUE 0 + +// WARNING! +// This has shown up at 0x700000 on real NV4 +#define NV4_PROM_START 0x300000 +#define NV4_PROM_END 0x30FFFF +#define NV4_PROM_DATA(i) (NV4_PROM_START+(i)) +#define NV4_PROM_SIZE 65536 + +#define NV4_PROM_ERRATA_START 0x700000 +#define NV4_PROM_ERRATA_END 0x70FFFF +#define NV4_PROM_ERRATA_DATA(i) (NV4_PROM_ERRATA_START+(i)) + +#define NV4_PRM 0x5FFF:0x4000 +#define NV4_PRM_INTR_0 0x4100 +#define NV4_PRM_INTR_0_TRACE_MPU401 0 +#define NV4_PRM_INTR_0_TRACE_MPU401_NOT_PENDING 0x0 +#define NV4_PRM_INTR_0_TRACE_MPU401_PENDING 0x1 +#define NV4_PRM_INTR_0_TRACE_MPU401_RESET 0x1 +#define NV4_PRM_INTR_0_TRACE_FM 4 +#define NV4_PRM_INTR_0_TRACE_FM_NOT_PENDING 0x0 +#define NV4_PRM_INTR_0_TRACE_FM_PENDING 0x1 +#define NV4_PRM_INTR_0_TRACE_FM_RESET 0x1 +#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL 8 +#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_NOT_PENDING 0x0 +#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_PENDING 0x1 +#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_RESET 0x1 +#define NV4_PRM_INTR_0_TRACE_SB_MIXER 12 +#define NV4_PRM_INTR_0_TRACE_SB_MIXER_NOT_PENDING 0x0 +#define NV4_PRM_INTR_0_TRACE_SB_MIXER_PENDING 0x1 +#define NV4_PRM_INTR_0_TRACE_SB_MIXER_RESET 0x1 +#define NV4_PRM_INTR_0_TRACE_OVERFLOW 16 +#define NV4_PRM_INTR_0_TRACE_OVERFLOW_NOT_PENDING 0x0 +#define NV4_PRM_INTR_0_TRACE_OVERFLOW_PENDING 0x1 +#define NV4_PRM_INTR_0_TRACE_OVERFLOW_RESET 0x1 +#define NV4_PRM_INTR_EN_0 0x4140 +#define NV4_PRM_INTR_EN_0_TRACE_MPU401 0 +#define NV4_PRM_INTR_EN_0_TRACE_MPU401_ENABLED 0x1 +#define NV4_PRM_INTR_EN_0_TRACE_FM 4 +#define NV4_PRM_INTR_EN_0_TRACE_FM_ENABLED 0x1 +#define NV4_PRM_INTR_EN_0_TRACE_SB_DIGITAL 8 +#define NV4_PRM_INTR_EN_0_TRACE_SB_DIGITAL_ENABLED 0x1 +#define NV4_PRM_INTR_EN_0_TRACE_SB_MIXER 12 +#define NV4_PRM_INTR_EN_0_TRACE_SB_MIXER_ENABLED 0x1 +#define NV4_PRM_INTR_EN_0_TRACE_OVERFLOW 16 +#define NV4_PRM_INTR_EN_0_TRACE_OVERFLOW_ENABLED 0x1 +#define NV4_PRM_RAMRM 0x4200 +#define NV4_PRM_RAMRM_BASE_ADDRESS 12 +#define NV4_PRM_RAMRM_BASE_ADDRESS_2000 0x2000 +#define NV4_PRM_TRACE 0x4300 +#define NV4_PRM_TRACE_IO_CAPTURE 0 +#define NV4_PRM_TRACE_IO_CAPTURE_DISABLED 0x0 +#define NV4_PRM_TRACE_IO_CAPTURE_WRITES 0x1 +#define NV4_PRM_TRACE_IO_CAPTURE_READS 0x2 +#define NV4_PRM_TRACE_IO_CAPTURE_READS_WRITES 0x3 +#define NV4_PRM_TRACE_IO_WRITE 4 +#define NV4_PRM_TRACE_IO_WRITE_NONE 0x0 +#define NV4_PRM_TRACE_IO_WRITE_OCCURED 0x1 +#define NV4_PRM_TRACE_IO_WRITE_RESET 0x1 +#define NV4_PRM_TRACE_IO_READ 5 +#define NV4_PRM_TRACE_IO_READ_NONE 0x0 +#define NV4_PRM_TRACE_IO_READ_OCCURED 0x1 +#define NV4_PRM_TRACE_IO_READ_RESET 0x1 +#define NV4_PRM_TRACE_INDEX 0x4310 +#define NV4_PRM_TRACE_INDEX_ADDRESS 0 +#define NV4_PRM_TRACE_INDEX_ADDRESS_0 0x0 +#define NV4_PRM_IGNORE_0 0x4320 +#define NV4_PRM_IGNORE_0_MPU401 0 +#define NV4_PRM_IGNORE_0_MPU401_DISABLED 0x0 +#define NV4_PRM_IGNORE_0_MPU401_WRITES 0x1 +#define NV4_PRM_IGNORE_0_MPU401_READS 0x2 +#define NV4_PRM_IGNORE_0_MPU401_READS_WRITES 0x3 +#define NV4_PRM_IGNORE_0_FM 4 +#define NV4_PRM_IGNORE_0_FM_DISABLED 0x0 +#define NV4_PRM_IGNORE_0_FM_WRITES 0x1 +#define NV4_PRM_IGNORE_0_FM_READS 0x2 +#define NV4_PRM_IGNORE_0_FM_READS_WRITES 0x3 +#define NV4_PRM_IGNORE_0_SB_DIGITAL 8 +#define NV4_PRM_IGNORE_0_SB_DIGITAL_DISABLED 0x0 +#define NV4_PRM_IGNORE_0_SB_DIGITAL_WRITES 0x1 +#define NV4_PRM_IGNORE_0_SB_DIGITAL_READS 0x2 +#define NV4_PRM_IGNORE_0_SB_DIGITAL_READS_WRITES 0x3 +#define NV4_PRM_IGNORE_0_SB_MIXER 12 +#define NV4_PRM_IGNORE_0_SB_MIXER_DISABLED 0x0 +#define NV4_PRM_IGNORE_0_SB_MIXER_WRITES 0x1 +#define NV4_PRM_IGNORE_0_SB_MIXER_READS 0x2 +#define NV4_PRM_IGNORE_0_SB_MIXER_READS_WRITES 0x3 + +// +// PIO submission +// + +#define NV4_USER_START 0x800000 +#define NV4_USER_END 0xFFFFFF + +#define NV4_USER_OBJECT(i,j) (0x800000+(i)*0x10000+(j)*0x2000) +#define NV4_USER_OBJECT_SIZE_1 16 +#define NV4_USER_OBJECT_SIZE_2 8 +#define NV4_USER_OBJECT_HANDLE 0 +#define NV4_USER_FREE016(i,j) (0x800010+(i)*65536+(j)*8192) +#define NV4_USER_FREE016_SIZE_1 16 +#define NV4_USER_FREE016_SIZE_2 8 +#define NV4_USER_FREE016_COUNT_LO 0 +#define NV4_USER_FREE016_COUNT_LO_0 0x0 +#define NV4_USER_FREE016_COUNT 2 +#define NV4_USER_FREE016_COUNT_HI 10 +#define NV4_USER_FREE016_COUNT_HI_0 0x0 +#define NV4_USER_FREE032(i,j) (0x800010+(i)*65536+(j)*8192) +#define NV4_USER_FREE032_SIZE_1 16 +#define NV4_USER_FREE032_SIZE_2 8 +#define NV4_USER_FREE032_COUNT_LO 0 +#define NV4_USER_FREE032_COUNT_LO_0 0x0 +#define NV4_USER_FREE032_COUNT 2 +#define NV4_USER_FREE032_COUNT_HI 10 +#define NV4_USER_FREE032_COUNT_HI_0 0x0 +#define NV4_USER_ZERO016(i,j,k) (0x0800012+(i)*65536+(j)*8192+(k)*2) +#define NV4_USER_ZERO016_SIZE_1 16 +#define NV4_USER_ZERO016_SIZE_2 8 +#define NV4_USER_ZERO016_SIZE_3 7 +#define NV4_USER_ZERO016_COUNT 0 +#define NV4_USER_ZERO016_COUNT_0 0x0 +#define NV4_USER_ZERO032(i,j,k) (0x0800014+(i)*65536+(j)*8192+(k)*4) +#define NV4_USER_ZERO032_SIZE_1 16 +#define NV4_USER_ZERO032_SIZE_2 8 +#define NV4_USER_ZERO032_SIZE_3 3 +#define NV4_USER_ZERO032_COUNT 0 +#define NV4_USER_ZERO032_COUNT_0 0x0 +#define NV4_USER_DMA_PUT(i,j) (0x800040+(i)*0x10000+(j)*0x2000) +#define NV4_USER_DMA_PUT_OFFSET 2 +#define NV4_USER_DMA_GET(i,j) (0x800044+(i)*0x10000+(j)*0x2000) +#define NV4_USER_DMA_GET_OFFSET 2 + +#define NV4_USER_ADR_CHID 16 +#define NV4_USER_ADR_SUBCHID 13 +#define NV4_USER_ADR_METHOD 0 +#define NV4_USER_DEVICE 16 + +#define NV4_PTIMER_START 0x9000 +#define NV4_PTIMER_END 0x9FFF + +#define NV4_PTIMER_INTR_0 0x9100 +#define NV4_PTIMER_INTR_0_ALARM 0 +#define NV4_PTIMER_INTR_0_ALARM_NOT_PENDING 0x0 +#define NV4_PTIMER_INTR_0_ALARM_PENDING 0x1 +#define NV4_PTIMER_INTR_0_ALARM_RESET 0x1 +#define NV4_PTIMER_INTR_EN_0 0x9140 +#define NV4_PTIMER_INTR_EN_0_ALARM 0 +#define NV4_PTIMER_INTR_EN_0_ALARM_ENABLED 0x1 // 0 = disabled +#define NV4_PTIMER_NUMERATOR 0x9200 +#define NV4_PTIMER_NUMERATOR_VALUE 0 +#define NV4_PTIMER_NUMERATOR_VALUE_0 0x0 +#define NV4_PTIMER_DENOMINATOR 0x9210 +#define NV4_PTIMER_DENOMINATOR_VALUE 0 +#define NV4_PTIMER_DENOMINATOR_VALUE_0 0x0 +#define NV4_PTIMER_TIME_0 0x9400 +#define NV4_PTIMER_TIME_0_NSEC 31:5 +#define NV4_PTIMER_TIME_1 0x9410 +#define NV4_PTIMER_TIME_1_NSEC 0 +#define NV4_PTIMER_ALARM_0 0x9420 +#define NV4_PTIMER_ALARM_0_NSEC 31:5 + +#define NV4_TRACE 0xFFFF: 0x0 +#define NV4_TRACE_DATA ( 0*32+ 7):( 0*32+ 0) +#define NV4_TRACE_ACCESS ( 0*32+14):( 0*32+14) +#define NV4_TRACE_ACCESS_WRITE 0x0 +#define NV4_TRACE_ACCESS_READ 0x1 +#define NV4_TRACE_TYPE ( 0*32+15):( 0*32+15) +#define NV4_TRACE_TYPE_IO 0x0 +#define NV4_TRACE_TYPE_MEMORY 0x1 +#define NV4_TRACE_ADDRESS ( 0*32+31):( 0*32+16) + +#define NV4_RAMHT_SIZE_0 0xFFF +#define NV4_RAMHT_SIZE_1 0x1FFF +#define NV4_RAMHT_SIZE_2 0x3FFF +#define NV4_RAMHT_SIZE_3 0x7FFF +#define NV4_RAMHT_HANDLE ( 0*32+31):( 0*32+ 0) +#define NV4_RAMHT_INSTANCE ( 1*32+15):( 1*32+ 0) +#define NV4_RAMHT_ENGINE ( 1*32+17):( 1*32+16) +#define NV4_RAMHT_ENGINE_SW 0x0 +#define NV4_RAMHT_ENGINE_GRAPHICS 0x1 +#define NV4_RAMHT_ENGINE_DVD 0x2 +#define NV4_RAMHT_CHID ( 1*32+27):( 1*32+24) +#define NV4_RAMHT_STATUS ( 1*32+31):( 1*32+31) +#define NV4_RAMHT_STATUS_INVALID 0x0 +#define NV4_RAMHT_STATUS_VALID 0x1 + +#define NV4_RAMRO_SIZE_0 0x1FF +#define NV4_RAMRO_SIZE_1 0x1FFF +#define NV4_RAMRO_METHOD ( 0*32+12):( 0*32+ 0) +#define NV4_RAMRO_SUBCHANNEL ( 0*32+15):( 0*32+13) +#define NV4_RAMRO_CHID ( 0*32+22):( 0*32+16) +#define NV4_RAMRO_TYPE ( 0*32+23):( 0*32+23) +#define NV4_RAMRO_TYPE_WRITE 0x0 +#define NV4_RAMRO_TYPE_READ 0x1 +#define NV4_RAMRO_BYTE_ENABLES ( 0*32+27):( 0*32+24) +#define NV4_RAMRO_REASON ( 0*32+31):( 0*32+28) +#define NV4_RAMRO_REASON_ILLEGAL_ACCESS 0x0 +#define NV4_RAMRO_REASON_NO_CACHE_AVAILABLE 0x1 +#define NV4_RAMRO_REASON_CACHE_RAN_OUT 0x2 +#define NV4_RAMRO_REASON_FREE_COUNT_OVERRUN 0x3 +#define NV4_RAMRO_REASON_CAUGHT_LYING 0x4 +#define NV4_RAMRO_REASON_RESERVED_ACCESS 0x5 +#define NV4_RAMRO_DATA ( 1*32+31):( 1*32+ 0) + +#define NV4_RAMFC_SIZE_0 0x1FF +#define NV4_RAMFC_DMA_PUT ( 0*32+28):( 0*32+ 2) +#define NV4_RAMFC_DMA_GET ( 1*32+28):( 1*32+ 2) +#define NV4_RAMFC_DMA_INST ( 2*32+15):( 2*32+ 0) +#define NV4_RAMFC_DMA_METHOD ( 3*32+12):( 3*32+ 2) +#define NV4_RAMFC_DMA_SUBCHANNEL ( 3*32+15):( 3*32+13) +#define NV4_RAMFC_DMA_METHOD_COUNT ( 3*32+28):( 3*32+18) +#define NV4_RAMFC_DMA_FETCH_TRIG ( 4*32+ 7):( 4*32+ 3) +#define NV4_RAMFC_DMA_FETCH_SIZE ( 4*32+15):( 4*32+13) +#define NV4_RAMFC_DMA_FETCH_MAX_REQS ( 4*32+19):( 4*32+16) +#define NV4_RAMFC_ENGINE_SUB_0 ( 5*32+ 1):( 5*32+ 0) +#define NV4_RAMFC_ENGINE_SUB_1 ( 5*32+ 5):( 5*32+ 4) +#define NV4_RAMFC_ENGINE_SUB_2 ( 5*32+ 9):( 5*32+ 8) +#define NV4_RAMFC_ENGINE_SUB_3 ( 5*32+13):( 5*32+12) +#define NV4_RAMFC_ENGINE_SUB_4 ( 5*32+17):( 5*32+16) +#define NV4_RAMFC_ENGINE_SUB_5 ( 5*32+21):( 5*32+20) +#define NV4_RAMFC_ENGINE_SUB_6 ( 5*32+25):( 5*32+24) +#define NV4_RAMFC_ENGINE_SUB_7 ( 5*32+29):( 5*32+28) +#define NV4_RAMFC_ENGINE_SW 0x0 +#define NV4_RAMFC_ENGINE_GRAPHICS 0x1 +#define NV4_RAMFC_ENGINE_DVD 0x2 +#define NV4_RAMFC_PULL1_ENGINE ( 6*32+ 1):( 6*32+ 0) +#define NV4_RAMFC_PULL1_ENGINE_SW 0x0 +#define NV4_RAMFC_PULL1_ENGINE_GRAPHICS 0x1 +#define NV4_RAMFC_PULL1_ENGINE_DVD 0x2 + + +#define NV4_RAMDVD_CTX_TABLE (63*32+31):( 0*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT(c,s) (((c)*4+((s)/2))*32+((s)%2)*16+15):(((c)*4+((s)/2))*32+((s)%2)*16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_0 ( 0*32+15):( 0*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_1 ( 0*32+31):( 0*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_2 ( 1*32+15):( 1*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_3 ( 1*32+31):( 1*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_4 ( 2*32+15):( 2*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_5 ( 2*32+31):( 2*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_6 ( 3*32+15):( 3*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_7 ( 3*32+31):( 3*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_0 (60*32+15):(60*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_1 (60*32+31):(60*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_2 (61*32+15):(61*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_3 (61*32+31):(61*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_4 (62*32+15):(62*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_5 (62*32+31):(62*32+16) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_6 (63*32+15):(63*32+ 0) +#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_7 (63*32+31):(63*32+16) + +#define NV4_DMA_CLASS ( 0*32+11):( 0*32+ 0) +#define NV4_DMA_PAGE_TABLE ( 0*32+12):( 0*32+12) +#define NV4_DMA_PAGE_TABLE_NOT_PRESENT 0x0 +#define NV4_DMA_PAGE_TABLE_PRESENT 0x1 +#define NV4_DMA_PAGE_ENTRY ( 0*32+13):( 0*32+13) +#define NV4_DMA_PAGE_ENTRY_NOT_LINEAR 0x0 +#define NV4_DMA_PAGE_ENTRY_LINEAR 0x1 +#define NV4_DMA_TARGET_NODE ( 0*32+17):( 0*32+16) +#define NV4_DMA_TARGET_NODE_NVM 0x0 +#define NV4_DMA_TARGET_NODE_PCI 0x2 +#define NV4_DMA_TARGET_NODE_AGP 0x3 +#define NV4_DMA_ADJUST ( 0*32+31):( 0*32+20) +#define NV4_DMA_LIMIT ( 1*32+31):( 1*32+ 0) +#define NV4_DMA_ACCESS ( 2*32+ 1):( 2*32+ 1) +#define NV4_DMA_ACCESS_READ_ONLY 0x0 +#define NV4_DMA_ACCESS_READ_AND_WRITE 0x1 +#define NV4_DMA_FRAME_ADDRESS ( 2*32+31):( 2*32+12) + +#define NV4_SUBCHAN_CTX_SWITCH ( 0*32+31):( 0*32+ 0) +#define NV4_SUBCHAN_DMA_INSTANCE ( 1*32+15):( 1*32+ 0) +#define NV4_SUBCHAN_NOTIFY_INSTANCE ( 1*32+31):( 1*32+16) +#define NV4_SUBCHAN_MEMFMT_INSTANCE ( 2*32+15):( 2*32+ 0) +#define NV4_SUBCHAN_MEMFMT_LINEAR ( 2*32+16):( 2*32+16) +#define NV4_SUBCHAN_MEMFMT_LINEAR_OUT 0x0 +#define NV4_SUBCHAN_MEMFMT_LINEAR_IN 0x1 \ No newline at end of file From 984660794c9512c5557b8d3d5eea872a4521be44 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 11 Sep 2025 14:46:37 +0100 Subject: [PATCH 219/274] NV4 Timer is the same as NV3 Timer. Some minor stuff and start working on the "new way" of doing things... --- src/include/86box/nv/vid_nv4.h | 22 +- src/include/86box/nv/vid_nv4_defines.h | 26 +-- src/video/nv/nv4/nv4_core.c | 9 +- src/video/nv/nv4/nv4_debug_register_list.c | 0 src/video/nv/nv4/subsystems/nv4_ptimer.c | 226 +++++++++++++++++++++ 5 files changed, 266 insertions(+), 17 deletions(-) create mode 100644 src/video/nv/nv4/nv4_debug_register_list.c create mode 100644 src/video/nv/nv4/subsystems/nv4_ptimer.c diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 7fef843d4..6e94024a6 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -20,9 +20,10 @@ #include #include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv4_defines.h> -extern const device_config_t nv4_config[]; // Config for RIVA 128 (revision A/B) +extern const device_config_t nv4_config[]; +extern nv4_t* nv4; // Allocated at device startup // Structures typedef struct nv4_s @@ -30,6 +31,25 @@ typedef struct nv4_s nv_base_t nvbase; // Base Nvidia structure } nv4_t; +// +// PTIMER +// + +typedef struct nv4_ptimer_s +{ + uint32_t interrupt_status; // PTIMER Interrupt status + uint32_t interrupt_enable; // PTIMER Interrupt enable + uint32_t clock_numerator; // PTIMER (tick?) numerator + uint32_t clock_denominator; // PTIMER (tick?) denominator + uint64_t time; // time + uint32_t alarm; // The value of time when there should be an alarm +} nv4_ptimer_t; + + +// +// Functions +// + // Device Core void nv4_init(); void nv4_close(void* priv); diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h index f7499e3a8..e27ff091e 100644 --- a/src/include/86box/nv/vid_nv4_defines.h +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -2745,7 +2745,7 @@ #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0 1 #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_COLOR 0x0 #define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 2 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_ZERO 0x1 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_FACTOR 0x2 #define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_DIFFUSE 0x3 @@ -2807,7 +2807,7 @@ #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0 0 #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_NORMAL 0x0 #define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 4:2 +#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 2 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_ZERO 0x1 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_FACTOR 0x2 #define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_DIFFUSE 0x3 @@ -4177,14 +4177,14 @@ #define NV4_PTIMER_START 0x9000 #define NV4_PTIMER_END 0x9FFF -#define NV4_PTIMER_INTR_0 0x9100 -#define NV4_PTIMER_INTR_0_ALARM 0 -#define NV4_PTIMER_INTR_0_ALARM_NOT_PENDING 0x0 -#define NV4_PTIMER_INTR_0_ALARM_PENDING 0x1 -#define NV4_PTIMER_INTR_0_ALARM_RESET 0x1 -#define NV4_PTIMER_INTR_EN_0 0x9140 -#define NV4_PTIMER_INTR_EN_0_ALARM 0 -#define NV4_PTIMER_INTR_EN_0_ALARM_ENABLED 0x1 // 0 = disabled +#define NV4_PTIMER_INTR 0x9100 +#define NV4_PTIMER_INTR_ALARM 0 +#define NV4_PTIMER_INTR_ALARM_NOT_PENDING 0x0 +#define NV4_PTIMER_INTR_ALARM_PENDING 0x1 +#define NV4_PTIMER_INTR_ALARM_RESET 0x1 +#define NV4_PTIMER_INTR_EN 0x9140 +#define NV4_PTIMER_INTR_EN_ALARM 0 +#define NV4_PTIMER_INTR_EN_ALARM_ENABLED 0x1 // 0 = disabled #define NV4_PTIMER_NUMERATOR 0x9200 #define NV4_PTIMER_NUMERATOR_VALUE 0 #define NV4_PTIMER_NUMERATOR_VALUE_0 0x0 @@ -4192,11 +4192,11 @@ #define NV4_PTIMER_DENOMINATOR_VALUE 0 #define NV4_PTIMER_DENOMINATOR_VALUE_0 0x0 #define NV4_PTIMER_TIME_0 0x9400 -#define NV4_PTIMER_TIME_0_NSEC 31:5 +#define NV4_PTIMER_TIME_0_NSEC 5 #define NV4_PTIMER_TIME_1 0x9410 #define NV4_PTIMER_TIME_1_NSEC 0 -#define NV4_PTIMER_ALARM_0 0x9420 -#define NV4_PTIMER_ALARM_0_NSEC 31:5 +#define NV4_PTIMER_ALARM 0x9420 +#define NV4_PTIMER_ALARM_NSEC 5 #define NV4_TRACE 0xFFFF: 0x0 #define NV4_TRACE_DATA ( 0*32+ 7):( 0*32+ 0) diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 2af47f22a..d020e44e6 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -27,19 +27,22 @@ #include <86box/nv/vid_nv.h> #include <86box/nv/vid_nv4.h> +nv4_t* nv4; + void nv4_init() { - + nv4 = calloc(1, sizeof(nv4_t)); } void* nv4_init_stb4400(const device_t *info) { - + nv4_init(); + return nv4; } void nv4_close(void* priv) { - + free(nv4); } void nv4_speed_changed(void *priv) diff --git a/src/video/nv/nv4/nv4_debug_register_list.c b/src/video/nv/nv4/nv4_debug_register_list.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/video/nv/nv4/subsystems/nv4_ptimer.c b/src/video/nv/nv4/subsystems/nv4_ptimer.c new file mode 100644 index 000000000..b52f79f17 --- /dev/null +++ b/src/video/nv/nv4/subsystems/nv4_ptimer.c @@ -0,0 +1,226 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Nvidia RIVA TNT (NV4 architecture) - Timer emulation + * + * + * + * Authors: Connor Hyde, + * + * Copyright 2024-2025 starfrost + */ + +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +nv_register_t ptimer_registers[] = { + { NV4_PTIMER_INTR, "NV4 PTIMER - Interrupt Status", NULL, NULL}, + { NV4_PTIMER_INTR_EN, "NV4 PTIMER - Interrupt Enable", NULL, NULL,}, + { NV4_PTIMER_NUMERATOR, "NV4 PTIMER - Numerator", NULL, NULL, }, + { NV4_PTIMER_DENOMINATOR, "NV4 PTIMER - Denominator", NULL, NULL, }, + { NV4_PTIMER_TIME_0_NSEC, "NV4 PTIMER - Time0", NULL, NULL, }, + { NV4_PTIMER_TIME_1_NSEC, "NV4 PTIMER - Time1", NULL, NULL, }, + { NV4_PTIMER_ALARM_NSEC, "NV4 PTIMER - Alarm", NULL, NULL, }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +// ptimer init code +void nv4_ptimer_init(void) +{ + nv_log("Initialising PTIMER..."); + + nv_log("Done!\n"); +} + +// Handles the PTIMER alarm interrupt +void nv4_ptimer_interrupt(uint32_t num) +{ + nv4->ptimer.interrupt_status |= (1 << num); + + nv4_pmc_handle_interrupts(true); +} + +// Ticks the timer. +void nv4_ptimer_tick(double real_time) +{ + // prevent a divide by zero + if (nv4->ptimer.clock_numerator == 0 + || nv4->ptimer.clock_denominator == 0) + return; + + // get the current time + + // See Envytools. We need to use the frequency as a source. + // We need to figure out how many cycles actually occurred because this counts up every cycle... + // However it seems that their formula is wrong. I can't be bothered to figure out what's going on and, based on documentation from NVIDIA, + // timer_0 is meant to roll over every 4 seconds. Multiplying by 10 basically does the job. + + // Convert to microseconds + double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv4->nvbase.memory_clock_frequency) * 10.0f; + double current_time = freq_base * ((double)nv4->ptimer.clock_numerator) / (double)nv4->ptimer.clock_denominator; // *10.0? + + // truncate it + nv4->ptimer.time += (uint64_t)current_time; + + // Check if the alarm has actually triggered.. + // Only log on ptimer alarm. Otherwise, it's too much spam. + if (nv4->ptimer.time >= nv4->ptimer.alarm) + { + nv_log_verbose_only("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv4->ptimer.alarm); + nv4_ptimer_interrupt(NV4_PTIMER_INTR_ALARM); + } +} + +uint32_t nv4_ptimer_read(uint32_t address) +{ + // always enabled + + nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); + + // Only log these when tehy actually tick + if (address != NV4_PTIMER_TIME_0_NSEC + && address != NV4_PTIMER_TIME_1_NSEC) + { + nv_log_verbose_only("PTIMER Read from 0x%08x", address); + } + + uint32_t ret = 0x00; + + // if the register actually exists + if (reg) + { + // on-read function + if (reg->on_read) + ret = reg->on_read(); + else + { + // Interrupt state: + // Bit 0: Alarm + + switch (reg->address) + { + case NV4_PTIMER_INTR: + ret = nv4->ptimer.interrupt_status; + break; + case NV4_PTIMER_INTR_EN: + ret = nv4->ptimer.interrupt_enable; + break; + case NV4_PTIMER_NUMERATOR: + ret = nv4->ptimer.clock_numerator; // 15:0 + break; + case NV4_PTIMER_DENOMINATOR: + ret = nv4->ptimer.clock_denominator ; //15:0 + break; + // 64-bit value + // High part + case NV4_PTIMER_TIME_0_NSEC: + ret = nv4->ptimer.time & 0xFFFFFFFF; //28:0 + break; + // Low part + case NV4_PTIMER_TIME_1_NSEC: + ret = nv4->ptimer.time >> 32; // 31:5 + break; + case NV4_PTIMER_ALARM_NSEC: + ret = nv4->ptimer.alarm; // 31:5 + break; + } + + } + //TIME0 and TIME1 produce too much log spam that slows everything down + if (reg->address != NV4_PTIMER_TIME_0_NSEC + && reg->address != NV4_PTIMER_TIME_1_NSEC) + { + if (reg->friendly_name) + nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); + else + nv_log_verbose_only("\n"); + } + } + else + { + nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); + } + + return ret; +} + +void nv4_ptimer_write(uint32_t address, uint32_t value) +{ + // before doing anything, check the subsystem enablement + nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); + + nv_log_verbose_only("PTIMER Write 0x%08x -> 0x%08x", value, address); + + // if the register actually exists + if (reg) + { + if (reg->friendly_name) + nv_log_verbose_only(": %s\n", reg->friendly_name); + else + nv_log_verbose_only("\n"); + + // on-read function + if (reg->on_write) + reg->on_write(value); + else + { + switch (reg->address) + { + // Interrupt state: + // Bit 0 - Alarm + + case NV4_PTIMER_INTR: + nv4->ptimer.interrupt_status &= ~value; + nv4_pmc_clear_interrupts(); + break; + + // Interrupt enablement state + case NV4_PTIMER_INTR_EN: + nv4->ptimer.interrupt_enable = value & 0x1; + break; + // nUMERATOR + case NV4_PTIMER_NUMERATOR: + nv4->ptimer.clock_numerator = value & 0xFFFF; // 15:0 + break; + case NV4_PTIMER_DENOMINATOR: + // prevent Div0 + if (!value) + value = 1; + + nv4->ptimer.clock_denominator = value & 0xFFFF; //15:0 + break; + // 64-bit value + // High part + case NV4_PTIMER_TIME_0_NSEC: + nv4->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 + break; + // Low part + case NV4_PTIMER_TIME_1_NSEC: + nv4->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 + break; + case NV4_PTIMER_ALARM: + nv4->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 + break; + } + } + } + else /* Completely unknown */ + { + nv_log(": Unknown register write (address=0x%08x)\n", address); + } +} \ No newline at end of file From f73e6af2e5ff7d6f03bed72f36b28fa7650f4637 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Thu, 11 Sep 2025 14:49:28 +0100 Subject: [PATCH 220/274] fix compile [again] --- src/include/86box/nv/vid_nv1.h | 1 - src/include/86box/nv/vid_nv4.h | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/include/86box/nv/vid_nv1.h b/src/include/86box/nv/vid_nv1.h index 9d08bb186..be6895c8b 100644 --- a/src/include/86box/nv/vid_nv1.h +++ b/src/include/86box/nv/vid_nv1.h @@ -17,7 +17,6 @@ #include #include -#include <86Box/nv/vid_nv.h> extern const device_config_t nv1_config[]; // Config for RIVA 128 (revision A/B) diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 6e94024a6..3415f065b 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -18,13 +18,8 @@ #include #include -#include <86Box/nv/vid_nv.h> #include <86Box/nv/vid_nv4_defines.h> -extern const device_config_t nv4_config[]; - -extern nv4_t* nv4; // Allocated at device startup - // Structures typedef struct nv4_s { @@ -46,6 +41,13 @@ typedef struct nv4_ptimer_s } nv4_ptimer_t; +// +// Globals +// +extern const device_config_t nv4_config[]; + +extern nv4_t* nv4; // Allocated at device startup + // // Functions // From d6388a825ce32eeaac0e232f002b1fcb7934308e Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 13 Sep 2025 17:22:04 +0100 Subject: [PATCH 221/274] Initial port of core nv3 stuff to nv4 --- src/include/86box/nv/vid_nv.h | 12 +- src/include/86box/nv/vid_nv3.h | 11 +- src/include/86box/nv/vid_nv4.h | 74 +- src/include/86box/nv/vid_nv4_defines.h | 471 ++++----- src/video/CMakeLists.txt | 1 + src/video/nv/nv3/nv3_core.c | 84 +- src/video/nv/nv4/nv4_core.c | 173 +++- src/video/nv/nv4/nv4_core_io.c | 1310 ++++++++++++++++++++++++ 8 files changed, 1819 insertions(+), 317 deletions(-) create mode 100644 src/video/nv/nv4/nv4_core_io.c diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 655fb3950..3d69f75c7 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -97,10 +97,19 @@ typedef enum nv_bus_generation_e } nv_bus_generation; +// PCI configuration +typedef struct nv_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; +} nv_pci_config_t; + // NV Base typedef struct nv_base_s { rom_t vbios; // NVIDIA/OEm VBIOS + nv_pci_config_t pci_config; // PCI configuration // move to nv3_cio_t? svga_t svga; // SVGA core (separate to nv3) - Weitek licensed uint32_t vram_amount; // The amount of VRAM @@ -113,7 +122,7 @@ typedef struct nv_base_s 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) + mem_mapping_t ramin_mapping_mirror; // RAM INput area mapping (mirrored) - NV3 ONLY 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 @@ -131,6 +140,7 @@ typedef struct nv_base_s void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID bool agp_enabled; // AGP Enabled (for debugging) + bool agp_sba_enabled; // AGP Sideband Addressing enabled // // DEBUG UI STUFF diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h index b954355bb..3726ecc03 100644 --- a/src/include/86box/nv/vid_nv3.h +++ b/src/include/86box/nv/vid_nv3.h @@ -1013,15 +1013,7 @@ typedef struct nv3_pmc_s } 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 +// add enums for eac // Chip configuration typedef struct nv3_straps_s { @@ -1546,7 +1538,6 @@ typedef struct nv3_s // Config nv3_straps_t straps; // OEM Configuration - nv3_pci_config_t pci_config; // PCI Configuration // Subsystems nv3_pmc_t pmc; // Master Control diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 3415f065b..7fda0fca4 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -20,11 +20,28 @@ #include #include <86Box/nv/vid_nv4_defines.h> -// Structures -typedef struct nv4_s + +// +// PBUS/RMA +// + +// Access the GPU from real-mode +typedef struct nv4_pbus_rma_s { - nv_base_t nvbase; // Base Nvidia structure -} nv4_t; + 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[NV4_RMA_NUM_REGS]; // The rma registers (saved) +} nv4_pbus_rma_t; + +// Bus Configuration +typedef struct nv4_pbus_s +{ + uint32_t debug_0; + uint32_t interrupt_status; // Interrupt status + uint32_t interrupt_enable; // Interrupt enable + nv4_pbus_rma_t rma; +} nv4_pbus_t; // // PTIMER @@ -40,6 +57,27 @@ typedef struct nv4_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv4_ptimer_t; +// +// PRAMDAC +// +typedef struct nv4_pramdac_s +{ + uint32_t mclk; + uint32_t vclk; + uint32_t nvclk; + uint32_t cursor_address; +} nv4_pramdac_t; + +// Structures +typedef struct nv4_s +{ + nv_base_t nvbase; // Base Nvidia structure + uint32_t straps; // Straps. See defines + nv4_pbus_t pbus; + nv4_ptimer_t ptimer; + nv4_pramdac_t pramdac; +} nv4_t; + // // Globals @@ -58,4 +96,30 @@ void nv4_close(void* priv); void nv4_speed_changed(void *priv); void nv4_draw_cursor(svga_t* svga, int32_t drawline); void nv4_recalc_timings(svga_t* svga); -void nv4_force_redraw(void* priv); \ No newline at end of file +void nv4_force_redraw(void* priv); + +// I/o +uint8_t nv4_mmio_read8(uint32_t addr, void* priv); +uint16_t nv4_mmio_read16(uint32_t addr, void* priv); +uint32_t nv4_mmio_read32(uint32_t addr, void* priv); +void nv4_mmio_write8(uint32_t addr, uint8_t val, void* priv); +void nv4_mmio_write16(uint32_t addr, uint16_t val, void* priv); +void nv4_mmio_write32(uint32_t addr, uint32_t val, void* priv); +uint8_t nv4_dfb_read8(uint32_t addr, void* priv); +uint16_t nv4_dfb_read16(uint32_t addr, void* priv); +uint32_t nv4_dfb_read32(uint32_t addr, void* priv); +void nv4_dfb_write8(uint32_t addr, uint8_t val, void* priv); +void nv4_dfb_write16(uint32_t addr, uint16_t val, void* priv); +void nv4_dfb_write32(uint32_t addr, uint32_t val, void* priv); +uint8_t nv4_ramin_read8(uint32_t addr, void* priv); +uint16_t nv4_ramin_read16(uint32_t addr, void* priv); +uint32_t nv4_ramin_read32(uint32_t addr, void* priv); +void nv4_ramin_write8(uint32_t addr, uint8_t val, void* priv); +void nv4_ramin_write16(uint32_t addr, uint16_t val, void* priv); +void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); + + +uint8_t nv4_svga_read(uint16_t addr, void* priv); +void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); + +void nv4_update_mappings(); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h index e27ff091e..21cf5739a 100644 --- a/src/include/86box/nv/vid_nv4_defines.h +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -7,12 +7,12 @@ // General // #define NV4_VRAM_SIZE_2MB 0x200000 // 2MB (never used; NV4 only) -#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) -#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB -#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB -#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only +#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) +#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB +#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB +#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only -#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! +#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! // // VBIOS @@ -21,6 +21,12 @@ #define NV4_PRMIO_START 0x7000 #define NV4_PRMIO_END 0x7FFF + +// NV4 Legacy I/O space +#define NV4_RMA_REGISTER_START 0x3D0 +#define NV4_RMA_REGISTER_END 0x3D3 +#define NV4_RMA_NUM_REGS 4 + #define NV4_PRMIO_RMA_ID 0x7100 #define NV4_PRMIO_RMA_ID_CODE 0 #define NV4_PRMIO_RMA_ID_CODE_VALID 0x2B16D065 @@ -32,6 +38,7 @@ #define NV4_PRMIO_RMA_DATA32_BYTE2 16 #define NV4_PRMIO_RMA_DATA32_BYTE1 8 #define NV4_PRMIO_RMA_DATA32_BYTE0 0 +#define NV4_PRMIO_RMA_MODE_MAX 0x0F #define NV4_PRAMDAC_START 0x680300 #define NV4_PRAMDAC_END 0x680FFF @@ -261,6 +268,8 @@ #define NV4_USER_DAC_START 0x681200 #define NV4_USER_DAC_END 0x681FFF +#define NV4_USER_DAC_PALETTE_START 0x6813C6 +#define NV4_USER_DAC_PALETTE_END 0x6813C9 #define NV4_USER_DAC_PIXEL_MASK 0x6813C6 #define NV4_USER_DAC_PIXEL_MASK_VALUE 0 @@ -486,223 +495,181 @@ #define NV4_PBUS_INTR_EN_0_PCI_BUS_ERROR_ENABLED 0x1 #define NV4_PBUS_ROM_CONFIG 0x1200 #define NV4_PBUS_ROM_CONFIG_TW1 0 -#define NV4_PBUS_ROM_CONFIG_TW1_DEFAULT 0xF -#define NV4_PBUS_ROM_CONFIG_TW0 4 -#define NV4_PBUS_ROM_CONFIG_TW0_DEFAULT 0x3 -#define NV4_PBUS_PCI_NV_0 0x1800 -#define NV4_PBUS_PCI_NV_0_VENDOR_ID 0 -#define NV4_PBUS_PCI_NV_0_VENDOR_ID_NVIDIA_SGS 0x12D2 -#define NV4_PBUS_PCI_NV_0_VENDOR_ID_NVIDIA 0x10DE -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_FUNC 16 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_FUNC_VGA 0x0 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP 3 19 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV0 0x0 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV1 0x1 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV2 0x2 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV3 0x3 -#define NV4_PBUS_PCI_NV_0_DEVICE_ID_CHIP_NV4 0x4 -#define NV4_PBUS_PCI_NV_1 0x1804 -#define NV4_PBUS_PCI_NV_1_IO_SPACE 0 -#define NV4_PBUS_PCI_NV_1_IO_SPACE_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_1_MEMORY_SPACE 1 -#define NV4_PBUS_PCI_NV_1_MEMORY_SPACE_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_1_BUS_MASTER 2 -#define NV4_PBUS_PCI_NV_1_BUS_MASTER_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_1_WRITE_AND_INVAL 4 -#define NV4_PBUS_PCI_NV_1_WRITE_AND_INVAL_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_1_PALETTE_SNOOP 5 -#define NV4_PBUS_PCI_NV_1_PALETTE_SNOOP_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_1_CAPLIST 20 -#define NV4_PBUS_PCI_NV_1_CAPLIST_NOT_PRESENT 0x0 -#define NV4_PBUS_PCI_NV_1_CAPLIST_PRESENT 0x1 -#define NV4_PBUS_PCI_NV_1_66MHZ 21 -#define NV4_PBUS_PCI_NV_1_66MHZ_INCAPABLE 0x0 -#define NV4_PBUS_PCI_NV_1_66MHZ_CAPABLE 0x1 -#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK 23 -#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK_INCAPABLE 0x0 -#define NV4_PBUS_PCI_NV_1_FAST_BACK2BACK_CAPABLE 0x1 -#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING 25 -#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_FAST 0x0 -#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_MEDIUM 0x1 -#define NV4_PBUS_PCI_NV_1_DEVSEL_TIMING_SLOW 0x2 -#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET 27 -#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_NO_ABORT 0x0 -#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_ABORT 0x1 -#define NV4_PBUS_PCI_NV_1_SIGNALED_TARGET_CLEAR 0x1 -#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET 28 -#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_NO_ABORT 0x0 -#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_ABORT 0x1 -#define NV4_PBUS_PCI_NV_1_RECEIVED_TARGET_CLEAR 0x1 -#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER 29 -#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_NO_ABORT 0x0 -#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_ABORT 0x1 -#define NV4_PBUS_PCI_NV_1_RECEIVED_MASTER_CLEAR 0x1 -#define NV4_PBUS_PCI_NV_2 0x1808 -#define NV4_PBUS_PCI_NV_2_REVISION_ID 0 -#define NV4_PBUS_PCI_NV_2_REVISION_ID_A01 0x0 -#define NV4_PBUS_PCI_NV_2_REVISION_ID_B01 0x10 -#define NV4_PBUS_PCI_NV_2_CLASS_CODE 8 -#define NV4_PBUS_PCI_NV_2_CLASS_CODE_VGA 0x30000 -#define NV4_PBUS_PCI_NV_2_CLASS_CODE_MULTIMEDIA 0x48000 -#define NV4_PBUS_PCI_NV_3 0x180C -#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER 11 -#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_0_CLOCKS 0x0 -#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_8_CLOCKS 0x1 -#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_240_CLOCKS 0x1E -#define NV4_PBUS_PCI_NV_3_LATENCY_TIMER_248_CLOCKS 0x1F -#define NV4_PBUS_PCI_NV_3_HEADER_TYPE 16 -#define NV4_PBUS_PCI_NV_3_HEADER_TYPE_SINGLEFUNC 0x0 -#define NV4_PBUS_PCI_NV_3_HEADER_TYPE_MULTIFUNC 0x80 -#define NV4_PBUS_PCI_NV_4 0x1810 -#define NV4_PBUS_PCI_NV_4_SPACE_TYPE 0 -#define NV4_PBUS_PCI_NV_4_SPACE_TYPE_MEMORY 0x0 -#define NV4_PBUS_PCI_NV_4_SPACE_TYPE_IO 0x1 -#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE 1 -#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_32_BIT 0x0 -#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_20_BIT 0x1 -#define NV4_PBUS_PCI_NV_4_ADDRESS_TYPE_64_BIT 0x2 -#define NV4_PBUS_PCI_NV_4_PREFETCHABLE 3 -#define NV4_PBUS_PCI_NV_4_PREFETCHABLE_NOT 0x0 -#define NV4_PBUS_PCI_NV_4_PREFETCHABLE_MERGABLE 0x1 -#define NV4_PBUS_PCI_NV_4_BASE_ADDRESS 24 -#define NV4_PBUS_PCI_NV_5 0x1814 -#define NV4_PBUS_PCI_NV_5_SPACE_TYPE 0 -#define NV4_PBUS_PCI_NV_5_SPACE_TYPE_MEMORY 0x0 -#define NV4_PBUS_PCI_NV_5_SPACE_TYPE_IO 0x1 -#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE 2:1 -#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_32_BIT 0x0 -#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_20_BIT 0x1 -#define NV4_PBUS_PCI_NV_5_ADDRESS_TYPE_64_BIT 0x2 -#define NV4_PBUS_PCI_NV_5_PREFETCHABLE 3 -#define NV4_PBUS_PCI_NV_5_PREFETCHABLE_NOT 0x0 -#define NV4_PBUS_PCI_NV_5_PREFETCHABLE_MERGABLE 0x1 -#define NV4_PBUS_PCI_NV_5_BASE_ADDRESS 24 -#define NV4_PBUS_PCI_NV_6(i) (0x1818+(i)*4) -#define NV4_PBUS_PCI_NV_6_SIZE_1 5 -#define NV4_PBUS_PCI_NV_6_RESERVED 0 -#define NV4_PBUS_PCI_NV_6_RESERVED_0 0x0 -#define NV4_PBUS_PCI_NV_11 0x182C -#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_VENDOR_ID 0 -#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_VENDOR_ID_NONE 0x0 -#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_ID 16 -#define NV4_PBUS_PCI_NV_11_SUBSYSTEM_ID_NONE 0x0 -#define NV4_PBUS_PCI_NV_12 0x1830 -#define NV4_PBUS_PCI_NV_12_ROM_DECODE 0 -#define NV4_PBUS_PCI_NV_12_ROM_DECODE_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_12_ROM_BASE 16 -#define NV4_PBUS_PCI_NV_13 0x1834 -#define NV4_PBUS_PCI_NV_13_CAP_PTR 0 -#define NV4_PBUS_PCI_NV_13_CAP_PTR_AGP 0x44 -#define NV4_PBUS_PCI_NV_13_CAP_PTR_POWER_MGMT 0x60 -#define NV4_PBUS_PCI_NV_14 0x1838 -#define NV4_PBUS_PCI_NV_14_RESERVED 0 -#define NV4_PBUS_PCI_NV_14_RESERVED_0 0x0 -#define NV4_PBUS_PCI_NV_15 0x183C -#define NV4_PBUS_PCI_NV_15_INTR_LINE 0 -#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ0 0x0 -#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ1 0x1 -#define NV4_PBUS_PCI_NV_15_INTR_LINE_IRQ15 0xF -#define NV4_PBUS_PCI_NV_15_INTR_LINE_UNKNOWN 0xFF -#define NV4_PBUS_PCI_NV_15_INTR_PIN 8 -#define NV4_PBUS_PCI_NV_15_INTR_PIN_INTA 0x1 -#define NV4_PBUS_PCI_NV_15_MIN_GNT 16 -#define NV4_PBUS_PCI_NV_15_MIN_GNT_NO_REQUIREMENTS 0x0 -#define NV4_PBUS_PCI_NV_15_MIN_GNT_750NS 0x3 -#define NV4_PBUS_PCI_NV_15_MIN_GNT_1250NS 0x5 -#define NV4_PBUS_PCI_NV_15_MAX_LAT 24 -#define NV4_PBUS_PCI_NV_15_MAX_LAT_NO_REQUIREMENTS 0x0 -#define NV4_PBUS_PCI_NV_15_MAX_LAT_250NS 0x1 -#define NV4_PBUS_PCI_NV_16 0x1840 -#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_VENDOR_ID 0 -#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_VENDOR_ID_NONE 0x0 -#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_ID 16 -#define NV4_PBUS_PCI_NV_16_SUBSYSTEM_ID_NONE 0x0 -#define NV4_PBUS_PCI_NV_17 0x1844 -#define NV4_PBUS_PCI_NV_17_AGP_REV_MAJOR 20 -#define NV4_PBUS_PCI_NV_17_AGP_REV_MAJOR_1 0x1 -#define NV4_PBUS_PCI_NV_17_AGP_REV_MINOR 16 -#define NV4_PBUS_PCI_NV_17_AGP_REV_MINOR_0 0x0 -#define NV4_PBUS_PCI_NV_17_NEXT_PTR 8 -#define NV4_PBUS_PCI_NV_17_NEXT_PTR_NULL 0x0 -#define NV4_PBUS_PCI_NV_17_CAP_ID 0 -#define NV4_PBUS_PCI_NV_17_CAP_ID_AGP 0x2 -#define NV4_PBUS_PCI_NV_18 0x1848 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RQ 24 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RQ_16 0xF -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA 9 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA_NONE 0x0 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_SBA_CAPABLE 0x1 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE 0 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_1X 0x1 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_2X 0x2 -#define NV4_PBUS_PCI_NV_18_AGP_STATUS_RATE_1X_AND_2X 0x3 -#define NV4_PBUS_PCI_NV_19 0x184C -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_RQ_DEPTH 24 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_RQ_DEPTH_0 0x0 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE 9 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE_OFF 0x0 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_SBA_ENABLE_ON 0x1 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE 8 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE_OFF 0x0 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_AGP_ENABLE_ON 0x1 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE 0 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_OFF 0x0 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_1X 0x1 -#define NV4_PBUS_PCI_NV_19_AGP_COMMAND_DATA_RATE_2X 0x2 -#define NV4_PBUS_PCI_NV_20 0x1850 -#define NV4_PBUS_PCI_NV_20_ROM_SHADOW 0 -#define NV4_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_21 0x1854 -#define NV4_PBUS_PCI_NV_21_VGA 0 -#define NV4_PBUS_PCI_NV_21_VGA_ENABLED 0x1 -#define NV4_PBUS_PCI_NV_22 0x1858 -#define NV4_PBUS_PCI_NV_22_SCRATCH 0 -#define NV4_PBUS_PCI_NV_22_SCRATCH_DEFAULT 0x23D6CE -#define NV4_PBUS_PCI_NV_23 0x185C -#define NV4_PBUS_PCI_NV_23_DT_TIMEOUT 0 -#define NV4_PBUS_PCI_NV_23_DT_TIMEOUT_16 0xF -#define NV4_PBUS_PCI_NV_24 0x1860 -#define NV4_PBUS_PCI_NV_24_PME_D3_COLD 31 -#define NV4_PBUS_PCI_NV_24_PME_D3_COLD_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_PME_D3_HOT 30 -#define NV4_PBUS_PCI_NV_24_PME_D3_HOT_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_PME_D2 29 -#define NV4_PBUS_PCI_NV_24_PME_D2_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_PME_D1 28 -#define NV4_PBUS_PCI_NV_24_PME_D1_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_PME_D0 27 -#define NV4_PBUS_PCI_NV_24_PME_D0_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_D2 26 -#define NV4_PBUS_PCI_NV_24_D2_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_D2_NOT_SUPPORTED 0x0 -#define NV4_PBUS_PCI_NV_24_D1 25 -#define NV4_PBUS_PCI_NV_24_D1_SUPPORTED 0x1 -#define NV4_PBUS_PCI_NV_24_D1_NOT_SUPPORTED 0x0 -#define NV4_PBUS_PCI_NV_24_DSI 21 -#define NV4_PBUS_PCI_NV_24_DSI_NOT_REQUIRED 0x0 -#define NV4_PBUS_PCI_NV_24_PME_CLOCK 19 -#define NV4_PBUS_PCI_NV_24_PME_CLOCK_NOT_REQUIRED 0x0 -#define NV4_PBUS_PCI_NV_24_VERSION 16 -#define NV4_PBUS_PCI_NV_24_VERSION_1 0x1 -#define NV4_PBUS_PCI_NV_24_NEXT_PTR 8 -#define NV4_PBUS_PCI_NV_24_NEXT_PTR_NULL 0x0 -#define NV4_PBUS_PCI_NV_24_NEXT_PTR_AGP 0x44 -#define NV4_PBUS_PCI_NV_24_CAP_ID 0 -#define NV4_PBUS_PCI_NV_24_CAP_ID_POWER_MGMT 0x1 -#define NV4_PBUS_PCI_NV_25 0x1864 -#define NV4_PBUS_PCI_NV_25_POWER_STATE 0 -#define NV4_PBUS_PCI_NV_25_POWER_STATE_D3_HOT 0x3 -#define NV4_PBUS_PCI_NV_25_POWER_STATE_D2 0x2 -#define NV4_PBUS_PCI_NV_25_POWER_STATE_D1 0x1 -#define NV4_PBUS_PCI_NV_25_POWER_STATE_D0 0x0 -#define NV4_PBUS_PCI_NV_26(i) (0x1868+(i)*4) -#define NV4_PBUS_PCI_NV_26_SIZE_1 38 -#define NV4_PBUS_PCI_NV_26_RESERVED 0 -#define NV4_PBUS_PCI_NV_26_RESERVED_0 0x0 +#define NV4_PBUS_ROM_CONFIG_TW1_DEFAULT 0xF +#define NV4_PBUS_ROM_CONFIG_TW0 4 +#define NV4_PBUS_ROM_CONFIG_TW0_DEFAULT 0x3 +// 86Box uses 8-bit PCI registers so this section was rewritten +#define NV4_PBUS_PCI_VENDOR_ID 0x1800 +#define NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA 0x10DE +#define NV4_PBUS_PCI_DEVICE_ID 0x1802 +#define NV4_PBUS_PCI_DEVICE_ID_NV4 0x0020 // Chip (19:17)= NV4, Func = VGA +#define NV4_PBUS_PCI_COMMAND 0x1804 +#define NV4_PBUS_PCI_COMMAND_IO_SPACE 0 +#define NV4_PBUS_PCI_COMMAND_IO_SPACE_ENABLED 0x1 +#define NV4_PBUS_PCI_COMMAND_MEMORY_SPACE 1 +#define NV4_PBUS_PCI_COMMAND_MEMORY_SPACE_ENABLED 0x1 +#define NV4_PBUS_PCI_COMMAND_BUS_MASTER 2 +#define NV4_PBUS_PCI_COMMAND_BUS_MASTER_ENABLED 0x1 +#define NV4_PBUS_PCI_COMMAND_WRITE_AND_INVAL 4 +#define NV4_PBUS_PCI_COMMAND_WRITE_AND_INVAL_ENABLED 0x1 +#define NV4_PBUS_PCI_COMMAND_PALETTE_SNOOP 5 +#define NV4_PBUS_PCI_COMMAND_PALETTE_SNOOP_ENABLED 0x1 +#define NV4_PBUS_PCI_STATUS 0x1806 +#define NV4_PBUS_PCI_STATUS_CAPLIST 4 +#define NV4_PBUS_PCI_STATUS_CAPLIST_NOT_PRESENT 0x0 +#define NV4_PBUS_PCI_STATUS_CAPLIST_PRESENT 0x1 +#define NV4_PBUS_PCI_STATUS_66MHZ 5 +#define NV4_PBUS_PCI_STATUS_66MHZ_INCAPABLE 0x0 +#define NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE 0x1 +#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK 7 +#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK_INCAPABLE 0x0 +#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK_CAPABLE 0x1 +#define NV4_PBUS_PCI_STATUS_2 0x1807 +#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING 1 +#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST 0x0 +#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_MEDIUM 0x1 +#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_SLOW 0x2 +#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET 3 +#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_NO_ABORT 0x0 +#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_ABORT 0x1 +#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_CLEAR 0x1 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET 4 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_NO_ABORT 0x0 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_ABORT 0x1 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_CLEAR 0x1 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER 5 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_NO_ABORT 0x0 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_ABORT 0x1 +#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_CLEAR 0x1 +#define NV4_PBUS_PCI_REVISION_ID 0x1808 +#define NV4_PBUS_PCI_REVISION_ID_A01 0x0 +#define NV4_PBUS_PCI_REVISION_ID_B01 0x1 +#define NV4_PBUS_PCI_CLASS_CODE 0x180B +#define NV4_PBUS_PCI_CLASS_CODE_VGA 0x30000 +#define NV4_PBUS_PCI_LATENCY_TIMER 0x180D +// Shift left by 3 to get the real value in clocks. 0x0 = 0, 0x1 = 8, 0x1E = 240, 0x1F = 248 are values used. +#define NV4_PBUS_PCI_LATENCY_TIMER_VALUE 3 +// 0x0 = single function (only value that matters) +#define NV4_PBUS_PCI_HEADER_TYPE 0x180E +#define NV4_PBUS_PCI_BAR_SPACE_TYPE 0 +#define NV4_PBUS_PCI_BAR_SPACE_TYPE_MEMORY 0x0 +#define NV4_PBUS_PCI_BAR_SPACE_TYPE_IO 0x1 +#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE 1 +#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_32_BIT 0x0 +#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_20_BIT 0x1 +#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_64_BIT 0x2 +#define NV4_PBUS_PCI_BAR_PREFETCHABLE 3 +#define NV4_PBUS_PCI_BAR_PREFETCHABLE_NOT 0x0 +#define NV4_PBUS_PCI_BAR_PREFETCHABLE_MERGABLE 0x1 +// Bits 23:4 are resedrved +#define NV4_PBUS_PCI_BAR0_INFO 0x1810 +#define NV4_PBUS_PCI_BAR0_UNUSED1 0x1811 +#define NV4_PBUS_PCI_BAR0_UNUSED2 0x1812 +#define NV4_PBUS_PCI_BAR0_BASE_31_TO_24 0x1813 // Must align to 16MByte +#define NV4_PBUS_PCI_BAR1_INFO 0x1814 +#define NV4_PBUS_PCI_BAR1_UNUSED1 0x1814 +#define NV4_PBUS_PCI_BAR1_UNUSED2 0x1815 +#define NV4_PBUS_PCI_BAR1_BASE_31_TO_24 0x1816 // Must align to 16MByte +//BAR2-5 reserved +#define NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID 0x182C +#define NV4_PBUS_PCI_SUBSYSTEM_ID 0x182E +#define NV4_PBUS_PCI_ROM 0x1830 +#define NV4_PBUS_PCI_ROM_DECODE 0 +#define NV4_PBUS_PCI_ROM_DECODE_ENABLED 0x1 +#define NV4_PBUS_PCI_ROM_BASE 0x1832 +#define NV4_PBUS_PCI_NEXT_PTR 0x1834 +#define NV4_PBUS_PCI_CAP_PTR_AGP 0x44 +#define NV4_PBUS_PCI_CAP_PTR_POWER_MGMT 0x60 +// 0xFF = unknown, otherwise 0x0-0xF = IRQ0-15 +#define NV4_PBUS_PCI_INTR_LINE 0x183C +#define NV4_PBUS_PCI_INTR_LINE_IRQ_NUM 0 +#define NV4_PBUS_PCI_INTR_LINE_IRQ_NUM_UNKNOWN 0xFF +#define NV4_PBUS_PCI_INTR_PIN 0x183D +#define NV4_PBUS_PCI_INTR_PIN_INTA 0x1 +#define NV4_PBUS_PCI_MIN_GNT 0x183E +#define NV4_PBUS_PCI_MIN_GNT_NO_REQUIREMENTS 0x0 +#define NV4_PBUS_PCI_MIN_GNT_750NS 0x3 +#define NV4_PBUS_PCI_MIN_GNT_1250NS 0x5 +#define NV4_PBUS_PCI_MAX_LAT 0x183F +#define NV4_PBUS_PCI_MAX_LAT_NO_REQUIREMENTS 0x0 +#define NV4_PBUS_PCI_MAX_LAT_250NS 0x1 +#define NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE 0x1840 +#define NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE 0x1842 +#define NV4_PBUS_AGP 0x44 +#define NV4_PBUS_AGP_CAPABILITIES 0x1844 +#define NV4_PBUS_AGP_CAPABILITY_AGP 0x2 +// Should be null! + +#define NV4_PBUS_AGP_NEXT_PTR 0x1845 +#define NV4_PBUS_AGP_REV 0x1846 +#define NV4_PBUS_AGP_REV_MINOR 0 +#define NV4_PBUS_AGP_REV_MINOR_0 0x0 +#define NV4_PBUS_AGP_REV_MAJOR 4 +#define NV4_PBUS_AGP_REV_MAJOR_1 0x1 +#define NV4_PBUS_AGP_STATUS_RATE 0x1848 +#define NV4_PBUS_AGP_STATUS_RATE_1X 0x1 +#define NV4_PBUS_AGP_STATUS_RATE_2X 0x2 +#define NV4_PBUS_AGP_STATUS_RATE_1X_AND_2X 0x3 +#define NV4_PBUS_AGP_STATUS_RQ 0x184B +#define NV4_PBUS_AGP_STATUS_RQ_16 0xF +#define NV4_PBUS_AGP_STATUS_SBA 0x1849 +#define NV4_PBUS_AGP_STATUS_SBA_STATUS 1 +#define NV4_PBUS_AGP_STATUS_SBA_STATUS_NONE 0x0 +#define NV4_PBUS_AGP_STATUS_SBA_STATUS_CAPABLE 0x1 +#define NV4_PBUS_AGP_COMMAND 0x184C +#define NV4_PBUS_AGP_COMMAND_DATA_RATE 0 +#define NV4_PBUS_AGP_COMMAND_DATA_RATE_OFF 0x0 +#define NV4_PBUS_AGP_COMMAND_DATA_RATE_1X 0x1 +#define NV4_PBUS_AGP_COMMAND_DATA_RATE_2X 0x2 +#define NV4_PBUS_AGP_COMMAND_2 0x184D +#define NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED 0 // 1 = enabled, 0 = disabled +#define NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED 1 // 1 = enabled, 0 = disabled +#define NV4_PBUS_AGP_COMMAND_RQ_DEPTH 0x184F //DEFAUlt 0 +#define NV4_PBUS_PCI_ROM_SHADOW 0x1850 +#define NV4_PBUS_PCI_ROM_SHADOW_IS_ENABLED 0 // 1 = enabled, 0 = disabled +#define NV4_PBUS_PCI_VGA 0x1854 +#define NV4_PBUS_PCI_VGA_IS_ENABLED 0 // 1 = enabled, 0 = disabled +#define NV4_PBUS_PCI_SCRATCH 0x1858 +#define NV4_PBUS_PCI_SCRATCH_DEFAULT 0x23D6CE +#define NV4_PBUS_PCI_DT 0x185C +#define NV4_PBUS_PCI_DT_TIMEOUT 0 +#define NV4_PBUS_PCI_DT_TIMEOUT_16 0xF +//TODO: Implement +#define NV4_PBUS_PCIPOWER 0x1860 +#define NV4_PBUS_PCIPOWER_CAP_ID 0 +#define NV4_PBUS_PCIPOWER_CAP_ID_POWER_MGMT 0x1 +#define NV4_PBUS_PCIPOWER_NEXT_PTR 0x1861 // should be 0x44=AGP +#define NV4_PBUS_PCIPOWER_2 0x1862 +#define NV4_PBUS_PCIPOWER_2_VERSION 0 +#define NV4_PBUS_PCIPOWER_2_VERSION_1 0x1 +#define NV4_PBUS_PCIPOWER_2_CLOCK 3 +#define NV4_PBUS_PCIPOWER_2_CLOCK_NOT_REQUIRED 0x0 +#define NV4_PBUS_PCIPOWER_2_DSI 5 +#define NV4_PBUS_PCIPOWER_2_DSI_NOT_REQUIRED 0x0 +#define NV4_PBUS_PCIPOWER_SUPPORTED_STATES 0x1863 +#define NV4_PBUS_PCIPOWER_D1 1 +#define NV4_PBUS_PCIPOWER_D1_SUPPORTED 0x1 // 1 = supported +#define NV4_PBUS_PCIPOWER_D2 2 +#define NV4_PBUS_PCIPOWER_D2_SUPPORTED 0x1 // 0 = not supported +#define NV4_PBUS_PCIPOWER_PME_D0 3 +#define NV4_PBUS_PCIPOWER_PME_D0_SUPPORTED 0x1 +#define NV4_PBUS_PCIPOWER_PME_D1 4 +#define NV4_PBUS_PCIPOWER_PME_D1_SUPPORTED 0x1 +#define NV4_PBUS_PCIPOWER_PME_D2 5 +#define NV4_PBUS_PCIPOWER_PME_D2_SUPPORTED 0x1 +#define NV4_PBUS_PCIPOWER_PME_D3_HOT 6 +#define NV4_PBUS_PCIPOWER_PME_D3_HOT_SUPPORTED 0x1 +#define NV4_PBUS_PCIPOWER_PME_D3_COLD 7 +#define NV4_PBUS_PCIPOWER_PME_D3_COLD_SUPPORTED 0x1 +#define NV4_PBUS_PCIPOWER_STATE_CURRENT 0x1864 +#define NV4_PBUS_PCIPOWER_STATE 0 +#define NV4_PBUS_PCIPOWER_STATE_D3_HOT 0x3 +#define NV4_PBUS_PCIPOWER_STATE_D2 0x2 +#define NV4_PBUS_PCIPOWER_STATE_D1 0x1 +#define NV4_PBUS_PCIPOWER_STATE_D0 0x0 #define NV4_PFIFO_START 0x2000 #define NV4_PFIFO_END 0x3FFF - #define NV4_PFIFO_DELAY_0 0x2040 #define NV4_PFIFO_DELAY_0_WAIT_RETRY 0 #define NV4_PFIFO_DELAY_0_WAIT_RETRY_0 0x0 @@ -3249,6 +3216,7 @@ #define NV4_CIO_START 0x3B0 #define NV4_CIO_END 0x3DF +#define NV4_CIO_SIZE NV4_CIO_END - NV4_CIO_START #define NV4_CIO_INP0 0x3c2 @@ -3403,7 +3371,7 @@ #define NV4_CIO_CRE_TREG_VCNTA_INDEX 0x6 #define NV4_CIO_CRE_TREG_VCNTB_INDEX 0x7 #define NV4_CIO_CRE_DDC_STATUS_INDEX 0x3E -#define NV4_CIO_CRE_DDC_WR_INDEX 0x3F +#define NV4_CIO_CRE_DDC_WR_INDEX 0x3F // Write to i2c for EDID/DDC #define NV4_CIO_CRE_PCI_TO_INDEX 0x40 #define NV4_CIO_CRE_PCI_TO_DELAY 0 @@ -3456,6 +3424,10 @@ #define NV4_PRMVIO_GX_READ_MAP_INDEX 0x4 #define NV4_PRMVIO_GX_MODE_INDEX 0x5 #define NV4_PRMVIO_GX_MISC_INDEX 0x6 +#define NV4_PRMVIO_GX_MISC_BANKED_128K_A0000 0x00 +#define NV4_PRMVIO_GX_MISC_BANKED_64K_A0000 0x04 +#define NV4_PRMVIO_GX_MISC_BANKED_32K_B0000 0x08 +#define NV4_PRMVIO_GX_MISC_BANKED_32K_B8000 0x0C #define NV4_PRMVIO_GX_DONT_CARE_INDEX 0x7 #define NV4_PRMVIO_GX_BIT_MASK_INDEX 0x8 @@ -3922,6 +3894,7 @@ // But this part may not work #define NV4_PRAMIN_START 0x700000 #define NV4_PRAMIN_END 0x7FFFFF +#define NV4_PRAMIN_SIZE 0xFFFFF #define NV4_PRAMIN_CONTEXT_0 ( 0*32+31):( 0*32+ 0) #define NV4_PRAMIN_CONTEXT_1 ( 1*32+31):( 1*32+ 0) @@ -4002,35 +3975,35 @@ // DFB is in BAR1. Access it as VRAM -#define NV4_PEXTDEV_BOOT_0 0x101000 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED 0 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED_33MHZ 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_SPEED_66MHZ 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR 1 -#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR_NO_BIOS 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_SUB_VENDOR_BIOS 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE 2 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_256K 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_512K_2BANK 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_SGRAM_512K_4BANK 0x2 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_TYPE_1024K_2BANK 0x3 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH 4 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH_64 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_RAM_WIDTH_128 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE 5 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE_PCI 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_BUS_TYPE_AGP 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL 6 -#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL_13500K 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_CRYSTAL_14318180 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE 7 -#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_SECAM 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_NTSC 0x1 -#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_PAL 0x2 -#define NV4_PEXTDEV_BOOT_0_STRAP_TVMODE_DISABLED 0x3 -#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE 11 -#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE_DISABLED 0x0 -#define NV4_PEXTDEV_BOOT_0_STRAP_OVERWRITE_ENABLED 0x1 +#define NV4_PEXTDEV_BOOT_0 0x101000 +#define NV4_STRAP_BUS_SPEED 0 +#define NV4_STRAP_BUS_SPEED_33MHZ 0x0 +#define NV4_STRAP_BUS_SPEED_66MHZ 0x1 +#define NV4_STRAP_SUB_VENDOR 1 +#define NV4_STRAP_SUB_VENDOR_NO_BIOS 0x0 +#define NV4_STRAP_SUB_VENDOR_BIOS 0x1 +#define NV4_STRAP_RAM_TYPE 2 +#define NV4_STRAP_RAM_TYPE_SGRAM_256K 0x0 +#define NV4_STRAP_RAM_TYPE_SGRAM_512K_2BANK 0x1 +#define NV4_STRAP_RAM_TYPE_SGRAM_512K_4BANK 0x2 +#define NV4_STRAP_RAM_TYPE_1024K_2BANK 0x3 +#define NV4_STRAP_RAM_WIDTH 4 +#define NV4_STRAP_RAM_WIDTH_64 0x0 +#define NV4_STRAP_RAM_WIDTH_128 0x1 +#define NV4_STRAP_BUS_TYPE 5 +#define NV4_STRAP_BUS_TYPE_PCI 0x0 +#define NV4_STRAP_BUS_TYPE_AGP 0x1 +#define NV4_STRAP_CRYSTAL 6 +#define NV4_STRAP_CRYSTAL_13500K 0x0 +#define NV4_STRAP_CRYSTAL_14318180 0x1 +#define NV4_STRAP_TVMODE 7 +#define NV4_STRAP_TVMODE_SECAM 0x0 +#define NV4_STRAP_TVMODE_NTSC 0x1 +#define NV4_STRAP_TVMODE_PAL 0x2 +#define NV4_STRAP_TVMODE_DISABLED 0x3 +#define NV4_STRAP_OVERWRITE 11 +#define NV4_STRAP_OVERWRITE_DISABLED 0x0 +#define NV4_STRAP_OVERWRITE_ENABLED 0x1 #define NV4_PDAC_START 0x680000 #define NV4_PDAC_END 0x680FFF diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index 41ff22447..c03ac15c9 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -199,6 +199,7 @@ add_library(vid OBJECT # NVidia RIVA TNT/TNT2 - Core nv/nv4/nv4_core.c + nv/nv4/nv4_core_io.c nv/nv4/nv4_core_config.c # Generic diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index 46b6cf9c3..d051a8f15 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -197,9 +197,7 @@ void nv3_mmio_write16(uint32_t addr, uint16_t val, void* priv) nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_write(real_address, val & 0xFF, nv3); - - if (val > 0xFF) - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); return; } @@ -227,15 +225,9 @@ void nv3_mmio_write32(uint32_t addr, uint32_t val, void* priv) nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); nv3_svga_write(real_address, val & 0xFF, nv3); - - if (val > 0xFF) - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); - - if (val > 0xFFFF) - nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); - - if (val > 0xFFFFFF) - nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); + nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); + nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); + nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); return; } @@ -276,7 +268,7 @@ uint8_t nv3_agp_read(int32_t func, int32_t addr) ret = nv3->nvbase.agp_enabled; break; default: - ret = nv3->pci_config.pci_regs[addr]; + ret = nv3->nvbase.pci_config.pci_regs[addr]; break; } @@ -329,25 +321,25 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) // 66Mhz FSB capable case PCI_REG_COMMAND_L: - ret = nv3->pci_config.pci_regs[PCI_REG_COMMAND_L]; + ret = nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L]; 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 + ret = nv3->nvbase.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); + ret = (nv3->nvbase.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]; + ret = nv3->nvbase.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); + ret = (nv3->nvbase.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: @@ -400,7 +392,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) break; case NV3_PCI_CFG_ENABLE_VBIOS: - ret = nv3->pci_config.vbios_enabled; + ret = nv3->nvbase.pci_config.vbios_enabled; break; case NV3_AGP_CAPABILITIES_POINTER: @@ -411,7 +403,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) break; case NV3_PCI_CFG_INT_LINE: - ret = nv3->pci_config.int_line; + ret = nv3->nvbase.pci_config.int_line; break; case NV3_PCI_CFG_INT_PIN: @@ -433,7 +425,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) 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)]; + ret = nv3->nvbase.pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)]; break; case NV3_AGP_START ... NV3_AGP_END: @@ -446,7 +438,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) default: // by default just return pci_config.pci_regs - ret = nv3->pci_config.pci_regs[addr]; + ret = nv3->nvbase.pci_config.pci_regs[addr]; break; } @@ -457,7 +449,7 @@ uint8_t nv3_pci_read(int32_t func, int32_t addr, void* priv) void nv3_agp_write(int32_t func, int32_t addr, uint8_t val) { - nv3->pci_config.pci_regs[addr] = val; + nv3->nvbase.pci_config.pci_regs[addr] = val; switch (addr) { @@ -471,7 +463,6 @@ void nv3_agp_write(int32_t func, int32_t addr, uint8_t val) void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) { - // sanity check if (!nv3) return; @@ -484,29 +475,28 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) nv_log("nv3_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); - nv3->pci_config.pci_regs[addr] = val; + nv3->nvbase.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; + nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L] = val; // actually update the mappings nv3_update_mappings(); break; case PCI_REG_COMMAND_H: - nv3->pci_config.pci_regs[PCI_REG_COMMAND_H] = val; + nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_H] = 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); + nv3->nvbase.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); + nv3->nvbase.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(); @@ -520,9 +510,9 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) // 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); + nv3->nvbase.pci_config.vbios_enabled = (val & 0x01); - if (nv3->pci_config.vbios_enabled) + if (nv3->nvbase.pci_config.vbios_enabled) { // First see if we simply wanted to change the VBIOS location @@ -533,8 +523,8 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) { 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; + uint32_t new_addr = nv3->nvbase.pci_config.pci_regs[NV3_PCI_CFG_VBIOS_BASE_H] << 24 | + nv3->nvbase.pci_config.pci_regs[NV3_PCI_CFG_VBIOS_BASE_L] << 16; // move it mem_mapping_set_addr(&nv3->nvbase.vbios.mapping, new_addr, 0x8000); @@ -555,7 +545,7 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) } break; case NV3_PCI_CFG_INT_LINE: - nv3->pci_config.int_line = val; + nv3->nvbase.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: @@ -564,7 +554,7 @@ void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) // 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; + nv3->nvbase.pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)] = val; break; case NV3_AGP_START ... NV3_AGP_END: @@ -855,16 +845,14 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv) i2c_gpio_set(nv3->nvbase.i2c, scl, sda); break; } - /* [6:0] contains cursorAddr [23:17] */ + /* [6:0] contains cursorAddr [22:16] */ case NV3_CRTC_REGISTER_CURSOR_ADDR0: - nv3->pramdac.cursor_address |= val << 17; //bit7 technically ignored, but nv don't care, so neither do we + nv3->pramdac.cursor_address |= ((val & 0x7F) << 12); //bit7 technically ignored, but nv don't care, so neither do we break; /* [7:2] contains cursorAddr [16:11] */ case NV3_CRTC_REGISTER_CURSOR_ADDR1: - nv3->pramdac.cursor_address |= (val >> 2) << 13; // bit0 and 1 aren't part of the address + nv3->pramdac.cursor_address |= ((val & 0xF8) << 4); // bit0 and 1 aren't part of the address break; - - } /* Recalculate the timings if we actually changed them @@ -1130,7 +1118,6 @@ void nv3_init_mappings_mmio(void) nv3_ramin_write32, NULL, MEM_MAPPING_EXTERNAL, nv3); - mem_mapping_add(&nv3->nvbase.ramin_mapping_mirror, 0, 0, nv3_ramin_read8, nv3_ramin_read16, @@ -1140,14 +1127,11 @@ void nv3_init_mappings_mmio(void) nv3_ramin_write32, NULL, MEM_MAPPING_EXTERNAL, nv3); - } void nv3_init_mappings_svga(void) { nv_log("Initialising SVGA core memory mapping\n"); - - // setup the svga mappings mem_mapping_add(&nv3->nvbase.framebuffer_mapping, 0, 0, nv3_dfb_read8, @@ -1191,20 +1175,20 @@ void nv3_update_mappings(void) 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"); + (nv3->nvbase.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_read, NULL, NULL, nv3_svga_write, NULL, NULL, nv3); - if (nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) + if (nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) io_sethandler(0x03c0, 0x0020, nv3_svga_read, NULL, NULL, nv3_svga_write, NULL, NULL, nv3); - if (!(nv3->pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) + if (!(nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) { nv_log("The memory was turned off, not much is going to happen.\n"); return; @@ -1412,8 +1396,8 @@ void* nv3_init(const device_t *info) 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; + nv3->nvbase.pci_config.int_line = 0xFF; // per datasheet + nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEM; // svga is done, so now initialise the real gpu diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index d020e44e6..58aba93b1 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -6,7 +6,7 @@ * * This file is part of the 86Box distribution. * - * NV3 bringup and device emulation. + * NV4 bringup and device emulation. * * * Authors: Connor Hyde, I need a better email address ;^) @@ -29,9 +29,178 @@ nv4_t* nv4; +// Initialise the MMIO mappings +void nv4_init_mappings_mmio(void) +{ + nv_log("Initialising MMIO mapping\n"); + + // 0x0 - 1000000: regs + // 0x1000000-2000000 + + // initialize the mmio mapping + mem_mapping_add(&nv4->nvbase.mmio_mapping, 0, 0, + nv4_mmio_read8, + nv4_mmio_read16, + nv4_mmio_read32, + nv4_mmio_write8, + nv4_mmio_write16, + nv4_mmio_write32, + NULL, MEM_MAPPING_EXTERNAL, nv4); + + // initialize the mmio mapping + mem_mapping_add(&nv4->nvbase.ramin_mapping, 0, 0, + nv4_ramin_read8, + nv4_ramin_read16, + nv4_ramin_read32, + nv4_ramin_write8, + nv4_ramin_write16, + nv4_ramin_write32, + NULL, MEM_MAPPING_EXTERNAL, nv4); + +} + +void nv4_init_mappings_svga(void) +{ + nv_log("Initialising SVGA core memory mapping\n"); + // setup the svga mappings + mem_mapping_add(&nv4->nvbase.framebuffer_mapping, 0, 0, + nv4_dfb_read8, + nv4_dfb_read16, + nv4_dfb_read32, + nv4_dfb_write8, + nv4_dfb_write16, + nv4_dfb_write32, + nv4->nvbase.svga.vram, 0, &nv4->nvbase.svga); + + // the SVGA/LFB mapping is also mirrored + mem_mapping_add(&nv4->nvbase.framebuffer_mapping_mirror, 0, 0, + nv4_dfb_read8, + nv4_dfb_read16, + nv4_dfb_read32, + nv4_dfb_write8, + nv4_dfb_write16, + nv4_dfb_write32, + nv4->nvbase.svga.vram, 0, &nv4->nvbase.svga); + + io_sethandler(NV4_CIO_START, NV4_CIO_SIZE, + nv4_svga_read, NULL, NULL, + nv4_svga_write, NULL, NULL, + nv4); +} + +void nv4_init_mappings(void) +{ + nv4_init_mappings_mmio(); + nv4_init_mappings_svga(); +} + +// Updates the mappings after initialisation. +void nv4_update_mappings(void) +{ + // sanity check + if (!nv4) + return; + + // setting this to 0 doesn't seem to disable it, based on the datasheet + + nv_log("\nMemory Mapping Config Change:\n"); + + (nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) ? nv_log("Enable I/O\n") : nv_log("Disable I/O\n"); + + io_removehandler(NV4_CIO_START, NV4_CIO_SIZE, + nv4_svga_read, NULL, NULL, + nv4_svga_write, NULL, NULL, + nv4); + + if (nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) + io_sethandler(NV4_CIO_START, NV4_CIO_SIZE, + nv4_svga_read, NULL, NULL, + nv4_svga_write, NULL, NULL, + nv4); + + if (!(nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) + { + nv_log("The memory was turned off, not much is going to happen.\n"); + return; + } + + // turn off bar0 and bar1 by defualt + mem_mapping_disable(&nv4->nvbase.mmio_mapping); + mem_mapping_disable(&nv4->nvbase.framebuffer_mapping); + mem_mapping_disable(&nv4->nvbase.framebuffer_mapping_mirror); + mem_mapping_disable(&nv4->nvbase.ramin_mapping); + + // Setup BAR0 (MMIO) + + nv_log("BAR0 (MMIO Base) = 0x%08x\n", nv4->nvbase.bar0_mmio_base); + + if (nv4->nvbase.bar0_mmio_base) + { + mem_mapping_set_addr(&nv4->nvbase.mmio_mapping, nv4->nvbase.bar0_mmio_base, NV4_MMIO_SIZE); + mem_mapping_set_addr(&nv4->nvbase.ramin_mapping, nv4->nvbase.bar0_mmio_base + NV4_PRAMIN_START, NV4_PRAMIN_SIZE); + } + + // if this breaks anything, remove it + nv_log("BAR1 (Linear Framebuffer & VRAM) = 0x%08x\n", nv4->nvbase.bar1_lfb_base); + + if (nv4->nvbase.bar1_lfb_base) + { + if (nv4->nvbase.vram_amount == NV4_VRAM_SIZE_16MB) + { + // we don't need this one in the case of 16mb, + mem_mapping_disable(&nv4->nvbase.framebuffer_mapping_mirror); + mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping, nv4->nvbase.bar1_lfb_base, NV4_VRAM_SIZE_16MB); + } + else if (nv4->nvbase.vram_amount == NV4_VRAM_SIZE_8MB) + { + mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping, nv4->nvbase.bar1_lfb_base, NV4_VRAM_SIZE_8MB); + mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping_mirror, nv4->nvbase.bar1_lfb_base + NV4_VRAM_SIZE_8MB, NV4_VRAM_SIZE_8MB); + } + } + + // Did we change the banked SVGA mode? + switch (nv4->nvbase.svga.gdcreg[NV4_PRMVIO_GX_MISC_INDEX] & 0x0c) + { + case NV4_PRMVIO_GX_MISC_BANKED_128K_A0000: + nv_log("SVGA Banked Mode = 128K @ A0000h\n"); + mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xA0000, 0x20000); // 128kb @ 0xA0000 + nv4->nvbase.svga.banked_mask = 0x1FFFF; + break; + case NV4_PRMVIO_GX_MISC_BANKED_64K_A0000: + nv_log("SVGA Banked Mode = 64K @ A0000h\n"); + mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xA0000, 0x10000); // 64kb @ 0xA0000 + nv4->nvbase.svga.banked_mask = 0xFFFF; + break; + case NV4_PRMVIO_GX_MISC_BANKED_32K_B0000: + nv_log("SVGA Banked Mode = 32K @ B0000h\n"); + mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xB0000, 0x8000); // 32kb @ 0xB0000 + nv4->nvbase.svga.banked_mask = 0x7FFF; + break; + case NV4_PRMVIO_GX_MISC_BANKED_32K_B8000: + nv_log("SVGA Banked Mode = 32K @ B8000h\n"); + mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xB8000, 0x8000); // 32kb @ 0xB8000 + nv4->nvbase.svga.banked_mask = 0x7FFF; + break; + } +} + + void nv4_init() { nv4 = calloc(1, sizeof(nv4_t)); + + if (!nv4->nvbase.vram_amount) + nv4->nvbase.vram_amount = device_get_config_int("vram_size"); + + /* Set log device name based on card model */ + const char* log_device_name = "NV4"; + + if (device_get_config_int("nv_debug_fulllog")) + nv4->nvbase.log = log_open("NV4"); + else + nv4->nvbase.log = log_open_cyclic("NV4"); + + nv_log_set_device(nv4->nvbase.log); } void* nv4_init_stb4400(const device_t *info) @@ -71,7 +240,7 @@ int32_t nv4_available(void) return (rom_present(NV4_VBIOS_STB_REVA)); } -// NV3 (RIVA 128) +// NV4 (RIVA 128) // AGP // 8MB or 16MB VRAM const device_t nv4_device_agp = diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c new file mode 100644 index 000000000..a7fe0d0c8 --- /dev/null +++ b/src/video/nv/nv4/nv4_core_io.c @@ -0,0 +1,1310 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * NV4 I/O, including Real-Mode Access (RMA), memory mapping, PCI, AGP, SVGA and MMIO via PCI BARs + * + * MMIO dumps are available at: https://nvwiki.org/misc/NVDumps/ + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ + + +// Prototypes for functions only used in this translation unit + +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + + +void nv4_init_mappings_mmio(void); +void nv4_init_mappings_svga(void); +bool nv4_is_svga_redirect_address(uint32_t addr); + +uint8_t nv4_svga_read(uint16_t addr, void* priv); +void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); + + +uint32_t nv4_mmio_arbitrate_read(uint32_t addr) +{ + +} + +void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) +{ + +} + +// Determine if this address needs to be redirected to the SVGA subsystem. + +bool nv4_is_svga_redirect_address(uint32_t addr) +{ + return (addr >= NV4_PRMVIO_START && addr <= NV4_PRMVIO_END) // VGA + || (addr >= NV4_PRMCIO_START && addr <= NV4_PRMCIO_END) // CRTC + || (addr >= NV4_USER_DAC_START && addr <= NV4_USER_DAC_END); // Note: 6813c6-6813c9 are ignored somewhere else +} + +uint8_t nv4_rma_read(uint16_t addr) +{ + addr &= 0xFF; + uint32_t real_final_address = 0x0; + uint8_t ret = 0x0; + + switch (addr) + { + // signature so you know reads work + case 0x00: + ret = NV4_PRMIO_RMA_ID_CODE_VALID & 0xFF; + break; + case 0x01: + ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 8) & 0xFF; + break; + case 0x02: + ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 16) & 0xFF; + break; + case 0x03: + ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 24) & 0xFF; + break; + case 0x08 ... 0x0B: + // reads must be dword aligned + real_final_address = (nv4->pbus.rma.addr + (addr & 0x03)); + + if (nv4->pbus.rma.addr < NV4_MMIO_SIZE) + ret = nv4_mmio_read8(real_final_address, NULL); + else + { + /* Do we need to read RAMIN here? */ + ret = nv4->nvbase.svga.vram[real_final_address - NV4_MMIO_SIZE] & (nv4->nvbase.svga.vram_max - 1); + } + + // log current location for vbios RE + nv_log_verbose_only("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 nv4_rma_write(uint16_t addr, uint8_t val) +{ + // 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 + nv4->pbus.rma.addr &= ~0xff; + nv4->pbus.rma.addr |= val; + break; + case 0x01: // 2nd highest byte + nv4->pbus.rma.addr &= ~0xff00; + nv4->pbus.rma.addr |= (val << 8); + break; + case 0x02: // 3rd highest byte + nv4->pbus.rma.addr &= ~0xff0000; + nv4->pbus.rma.addr |= (val << 16); + break; + case 0x03: // 4th highest byte + nv4->pbus.rma.addr &= ~0xff000000; + nv4->pbus.rma.addr |= (val << 24); + break; + } + } + // Data to send to MMIO + else + { + switch (addr % 0x04) + { + case 0x00: // lowest byte + nv4->pbus.rma.data &= ~0xff; + nv4->pbus.rma.data |= val; + break; + case 0x01: // 2nd highest byte + nv4->pbus.rma.data &= ~0xff00; + nv4->pbus.rma.data |= (val << 8); + break; + case 0x02: // 3rd highest byte + nv4->pbus.rma.data &= ~0xff0000; + nv4->pbus.rma.data |= (val << 16); + break; + case 0x03: // 4th highest byte + nv4->pbus.rma.data &= ~0xff000000; + nv4->pbus.rma.data |= (val << 24); + + nv_log_verbose_only("MMIO Real Mode Access write transaction complete, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + addr, nv4->pbus.rma.addr, nv4->pbus.rma.data); + + if (nv4->pbus.rma.addr < NV4_MMIO_SIZE) + nv4_mmio_write32(nv4->pbus.rma.addr, nv4->pbus.rma.data, NULL); + else // failsafe code, i don't think you will ever write outside of VRAM? + { + uint32_t* vram_32 = (uint32_t*)nv4->nvbase.svga.vram; + vram_32[(nv4->pbus.rma.addr - NV4_MMIO_SIZE) >> 2] = nv4->pbus.rma.data; + } + + + break; + } + } + + if (addr & 0x10) + nv4->pbus.rma.addr += 0x04; // Alignment +} + + + +// 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 nv4_mmio_read8(uint32_t addr, void* priv) +{ + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + // We need to specifically exclude this particular set of registers + // so we can write the 4/8bpp CLUT + if (addr >= NV4_USER_DAC_PALETTE_START && addr <= NV4_USER_DAC_PALETTE_END) + { + // Throw directly into PRAMDAC + return nv4_mmio_arbitrate_read(addr); + } + + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv4_svga_read(real_address, nv4); + + nv_log_verbose_only("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + + return ret; + } + + // see if unaligned reads are a problem + ret = nv4_mmio_read32(addr, priv); + return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFF); +} + +// Read 16-bit MMIO +uint16_t nv4_mmio_read16(uint32_t addr, void* priv) +{ + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv4_svga_read(real_address, nv4) + | (nv4_svga_read(real_address + 1, nv4) << 8); + + nv_log_verbose_only("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + + return ret; + } + + ret = nv4_mmio_read32(addr, priv); + return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFFFF); +} + +// Read 32-bit MMIO +uint32_t nv4_mmio_read32(uint32_t addr, void* priv) +{ + uint32_t ret = 0x00; + + // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. + addr &= 0xFFFFFF; + + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + ret = nv4_svga_read(real_address, nv4) + | (nv4_svga_read(real_address + 1, nv4) << 8) + | (nv4_svga_read(real_address + 2, nv4) << 16) + | (nv4_svga_read(real_address + 3, nv4) << 24); + + nv_log_verbose_only("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); + + return ret; + } + + ret = nv4_mmio_arbitrate_read(addr); + + return ret; +} + +// Write 8-bit MMIO +void nv4_mmio_write8(uint32_t addr, uint8_t val, void* priv) +{ + addr &= 0xFFFFFF; + + // We need to specifically exclude this particular set of registers + // so we can write the 4/8bpp CLUT + if (addr >= NV4_USER_DAC_PALETTE_START && addr <= NV4_USER_DAC_PALETTE_END) + { + // Throw directly into PRAMDAC + nv4_mmio_arbitrate_write(addr, val); + return; + } + + // This is weitek vga stuff + // If we need to add more of these we can convert these to a switch statement + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv_log_verbose_only("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + + nv4_svga_write(real_address, val & 0xFF, nv4); + + return; + } + + // overwrite first 8bits of a 32 bit value + uint32_t new_val = nv4_mmio_read32(addr, NULL); + + new_val &= (~0xFF << (addr & 3) << 3); + new_val |= (val << ((addr & 3) << 3)); + + nv4_mmio_write32(addr, new_val, priv); +} + +// Write 16-bit MMIO +void nv4_mmio_write16(uint32_t addr, uint16_t val, void* priv) +{ + addr &= 0xFFFFFF; + + // This is weitek vga stuff + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + + nv4_svga_write(real_address, val & 0xFF, nv4); + nv4_svga_write(real_address + 1, (val >> 8) & 0xFF, nv4); + + return; + } + + // overwrite first 16bits of a 32 bit value + uint32_t new_val = nv4_mmio_read32(addr, NULL); + + new_val &= (~0xFFFF << (addr & 3) << 3); + new_val |= (val << ((addr & 3) << 3)); + + nv4_mmio_write32(addr, new_val, priv); +} + +// Write 32-bit MMIO +void nv4_mmio_write32(uint32_t addr, uint32_t val, void* priv) +{ + addr &= 0xFFFFFF; + + // This is weitek vga stuff + if (nv4_is_svga_redirect_address(addr)) + { + // svga writes are not logged anyway rn + uint32_t real_address = addr & 0x3FF; + + nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); + + nv4_svga_write(real_address, val & 0xFF, nv4); + nv4_svga_write(real_address + 1, (val >> 8) & 0xFF, nv4); + nv4_svga_write(real_address + 2, (val >> 16) & 0xFF, nv4); + nv4_svga_write(real_address + 3, (val >> 24) & 0xFF, nv4); + + return; + } + + nv4_mmio_arbitrate_write(addr, val); +} + +// PCI stuff +// BAR0 Pointer to MMIO space & RAMIN +// BAR1 Pointer to Linear Framebuffer +uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) +{ + uint8_t ret = 0x00; + + // sanity check + if (!nv4) + return ret; + + // Convert to the MMIO addresses + if (addr <= 0xFF) + addr += 0x1800; + + // Anything not listed is 0x00 + // PCI values extracted from https://nvwiki.org/misc/NVDumps/RivaMobileNV4/Nv4win_HL/nv4bar0.bin + // STB V4400 (running Half-Life) + switch (addr) + { + // Get the pci vendor id.. + + case NV4_PBUS_PCI_VENDOR_ID: + ret = (NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA & 0xFF); + break; + + case NV4_PBUS_PCI_VENDOR_ID + 1: // all access 8bit + ret = (NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA >> 8); + break; + + // device id + + case NV4_PBUS_PCI_DEVICE_ID: + ret = (NV_PCI_DEVICE_NV4 & 0xFF); + break; + + case NV4_PBUS_PCI_DEVICE_ID + 1: + ret = (NV_PCI_DEVICE_NV4 >> 8); + break; + + // various capabilities enabled by default + // IO space enabled + // Memory space enabled + // Bus master enabled + // Write/inval enabled + // Pal snoop enabled + // Capabiliies list enabled + // 66Mhz FSB capable + + case NV4_PBUS_PCI_COMMAND: + ret = nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L]; + break; + // COMMAND_L not used + + // pci status register + case NV4_PBUS_PCI_STATUS: + if (nv4->straps + & NV4_STRAP_BUS_SPEED_66MHZ) + ret = (nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] | NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE); + else + ret = nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L]; + + break; + + case NV4_PBUS_PCI_STATUS_2: + ret = (nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H]) & (NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST << NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING); + break; + + case NV4_PBUS_PCI_REVISION_ID: + ret = nv4->nvbase.gpu_revision; // Commercial release + break; + + case PCI_REG_PROG_IF: + ret = 0x00; + break; + + // We only need to return 0x30 since the VGA class code is 0x30000 + case NV4_PBUS_PCI_CLASS_CODE: + ret = (NV4_PBUS_PCI_CLASS_CODE_VGA) >> 12; // CLASS_CODE_VGA + break; + + + case NV4_PBUS_PCI_LATENCY_TIMER: + case NV4_PBUS_PCI_HEADER_TYPE: + ret = 0x00; + break; + + // BARs are marked as prefetchable per the datasheet + case NV4_PBUS_PCI_BAR0_INFO: + case NV4_PBUS_PCI_BAR1_INFO: + // only bit that matters is bit 3 (prefetch bit) + ret = (NV4_PBUS_PCI_BAR_PREFETCHABLE_MERGABLE << NV4_PBUS_PCI_BAR_PREFETCHABLE); + break; + + // MMIO base address + case NV4_PBUS_PCI_BAR0_BASE_31_TO_24: + ret = nv4->nvbase.bar0_mmio_base >> 24;//8bit value + break; + + case NV4_PBUS_PCI_BAR1_BASE_31_TO_24: + ret = nv4->nvbase.bar1_lfb_base >> 24; //8bit value + break; + + case NV4_PBUS_PCI_ROM: + ret = nv4->nvbase.pci_config.vbios_enabled; + break; + + case NV4_PBUS_PCI_INTR_LINE: + ret = nv4->nvbase.pci_config.int_line; + break; + + case NV4_PBUS_PCI_INTR_PIN: + ret = PCI_INTA; + break; + + // + // Capabilities pointers + // + + case NV4_PBUS_PCI_NEXT_PTR: + ret = NV4_PBUS_PCI_CAP_PTR_POWER_MGMT; + break; + + case NV4_PBUS_PCIPOWER_NEXT_PTR: + ret = NV4_PBUS_PCI_CAP_PTR_AGP; + break; + + // AGP is the end of the chain + case NV4_PBUS_AGP_NEXT_PTR: + ret = 0x00; + break; + + case NV4_PBUS_PCI_MAX_LAT: + ret = NV4_PBUS_PCI_MAX_LAT_250NS; + break; + + // these map to the subsystem + // todo: port this bugfix to NV4 + case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE: + case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE + 1: + case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE: + case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE + 1: + ret = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_SUBSYSTEM_ID + (addr & 0x03)]; + break; + case NV4_PBUS_AGP_CAPABILITIES: + ret = NV4_PBUS_AGP_CAPABILITY_AGP; // AGP capable device + break; + case NV4_PBUS_AGP_REV: + ret = (NV4_PBUS_AGP_REV_MAJOR_1 << NV4_PBUS_AGP_REV_MAJOR) | NV4_PBUS_AGP_REV_MINOR; + break; + case NV4_PBUS_AGP_STATUS_RATE: + ret = NV4_PBUS_AGP_STATUS_RATE_1X_AND_2X; + break; + case NV4_PBUS_AGP_STATUS_SBA: + ret = (NV4_PBUS_AGP_STATUS_SBA_STATUS_CAPABLE) << NV4_PBUS_AGP_STATUS_SBA_STATUS; // Sideband is supported on NV4 + break; + case NV4_PBUS_AGP_STATUS_RQ: + ret = NV4_PBUS_AGP_STATUS_RQ_16; + break; + case NV4_PBUS_AGP_COMMAND_2: + ret = (nv4->nvbase.agp_enabled) << NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED + | (nv4->nvbase.agp_sba_enabled) << NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED; + break; + default: // by default just return pci_config.pci_regs (default value for nonwritten registers is 0x00) + ret = nv4->nvbase.pci_config.pci_regs[addr]; + break; + + } + + nv_log("nv4_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); + return ret; +} + + +// nv4 pci/agp write +void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) +{ + // sanity check + if (!nv4) + return; + + // Convert to the MMIO addresses + if (addr <= 0xFF) + addr += 0x1800; + // some addresses are not writable so can't have any effect and can't be allowed to be modified using this code + // as an example, only the most significant byte of the PCI BARs can be modified + if (addr == NV4_PBUS_PCI_BAR0_UNUSED1 || addr == NV4_PBUS_PCI_BAR0_UNUSED2 + && addr == NV4_PBUS_PCI_BAR1_UNUSED1 || addr == NV4_PBUS_PCI_BAR1_UNUSED2) + return; + + nv_log("nv4_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); + + nv4->nvbase.pci_config.pci_regs[addr] = val; + + switch (addr) + { + // standard pci command stuff + case PCI_REG_COMMAND_L: + nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L] = val; + // actually update the mappings + nv4_update_mappings(); + break; + case PCI_REG_COMMAND_H: + nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_H] = val; + // actually update the mappings + nv4_update_mappings(); + break; + // pci status register + case PCI_REG_STATUS_L: + nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] = val | (NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE << NV4_PBUS_PCI_STATUS_66MHZ); + break; + case PCI_REG_STATUS_H: + nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H] = val | (NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST << NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING); + break; + //TODO: ACTUALLY REMAP THE MMIO AND NV_USER + case NV4_PBUS_PCI_BAR0_BASE_31_TO_24: + nv4->nvbase.bar0_mmio_base = val << 24; + nv4_update_mappings(); + break; + case NV4_PBUS_PCI_BAR1_BASE_31_TO_24: + nv4->nvbase.bar1_lfb_base = val << 24; + nv4_update_mappings(); + break; + case NV4_PBUS_PCI_ROM: + case NV4_PBUS_PCI_ROM_BASE: + + // make sure we are actually toggling the vbios, not the rom base + if (addr == NV4_PBUS_PCI_ROM) + nv4->nvbase.pci_config.vbios_enabled = (val & 0x01); + + if (nv4->nvbase.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(&nv4->nvbase.vbios.mapping); + + if (addr != NV4_PBUS_PCI_ROM) + { + uint32_t old_addr = nv4->nvbase.vbios.mapping.base; + // 9bit register + uint32_t new_addr = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 1] << 24 | + nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM] << 16; + + // move it + mem_mapping_set_addr(&nv4->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(&nv4->nvbase.vbios.mapping); + + } + break; + case NV4_PBUS_PCI_INTR_LINE: + nv4->nvbase.pci_config.int_line = val; + break; + // these are mirrored to the subsystem id and also stored in the ROMBIOS + //todo: port to pci + case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE: + case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE + 1: + case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE: + case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE + 1: + nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_SUBSYSTEM_ID + (addr & 0x03)] = val; + break; + case NV4_PBUS_AGP_COMMAND_2: + nv4->nvbase.agp_enabled = (val >> NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED) & 0x01; + nv4->nvbase.agp_sba_enabled = (val >> NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED) & 0x01; + break; + default: + break; + } +} + + +// +// SVGA functions +// +void nv4_recalc_timings(svga_t* svga) +{ + // sanity check + if (!nv4) + return; + + + nv4_t* nv4 = (nv4_t*)svga->priv; + + // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the OFFSET register + uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; + + svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; + + /* Turn off override if we are in VGA mode */ + svga->override = !(pixel_mode == NV4_CIO_CRE_PIXEL_FORMAT_VGA); + + /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. + + Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. + + This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. + In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. + + Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick + to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from + the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. + */ + + // Set the pixel mode + switch (pixel_mode) + { + case NV4_CIO_CRE_PIXEL_FORMAT_8BPP: + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 1; // ????? + svga->bpp = 8; + svga->lowres = 0; + svga->map8 = svga->pallook; + break; + case NV4_CIO_CRE_PIXEL_FORMAT_16BPP: + /* This is some sketchy shit that is an attempt at an educated guess + at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, + this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ + if ((svga->crtc[NV4_CIO_CR_VRS_INDEX] >> 1) & 0x01) + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 2; + else + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; + + // 15bpp mode is removed on NV4 + // TODO: Not svga + svga->bpp = 16; + svga->lowres = 0; + + break; + case NV4_CIO_CRE_PIXEL_FORMAT_32BPP: + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; + + svga->bpp = 32; + svga->lowres = 0; + //svga->render = nv4_render_32bpp; + break; + } + + + if (((svga->miscout >> 2) & 2) == 2) + { + // set clocks + //nv4_pramdac_set_pixel_clock(); + //nv4_pramdac_set_vram_clock(); + } +} + +void nv4_speed_changed(void* priv) +{ + // sanity check + if (!nv4) + return; + + nv4_recalc_timings(&nv4->nvbase.svga); +} + +// Force Redraw +// Reset etc. +void nv4_force_redraw(void* priv) +{ + // sanity check + if (!nv4) + return; + + nv4->nvbase.svga.fullchange = changeframecount; +} + +// CHECK that ramin is the smae as nv4 + +// Read 8-bit ramin +uint8_t nv4_ramin_read8(uint32_t addr, void* priv) +{ + if (!nv4) return 0x00; + + addr &= (nv4->nvbase.svga.vram_max - 1); + uint32_t raw_addr = addr; // saved after and + + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + + uint32_t val = 0x00; + + //if (!nv4_ramin_arbitrate_read(addr, &val)) // Oh well + //{ + val = (uint8_t)nv4->nvbase.svga.vram[addr]; + nv_log_verbose_only("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + //} + + return (uint8_t)val; +} + +// Read 16-bit ramin +uint16_t nv4_ramin_read16(uint32_t addr, void* priv) +{ + if (!nv4) return 0x00; + + addr &= (nv4->nvbase.svga.vram_max - 1); + + // why does this not work in one line + svga_t* svga = &nv4->nvbase.svga; + uint16_t* vram_16bit = (uint16_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + addr >>= 1; // what + + uint32_t val = 0x00; + + //if (!nv4_ramin_arbitrate_read(addr, &val)) + //{ + val = (uint16_t)vram_16bit[addr]; + nv_log_verbose_only("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); + //} + + return val; +} + +// Read 32-bit ramin +uint32_t nv4_ramin_read32(uint32_t addr, void* priv) +{ + if (!nv4) + return 0x00; + + addr &= (nv4->nvbase.svga.vram_max - 1); + + // why does this not work in one line + uint32_t* vram_32bit = (uint32_t*)nv4->nvbase.svga.vram; + uint32_t raw_addr = addr; // saved after and logged + + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + addr >>= 2; // what + + uint32_t val = 0x00; + + //if (!nv4_ramin_arbitrate_read(addr, &val)) + //{ + val = vram_32bit[addr]; + nv_log_verbose_only("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); + //} + + return val; +} + +// Write 8-bit ramin +void nv4_ramin_write8(uint32_t addr, uint8_t val, void* priv) +{ + if (!nv4) return; + + addr &= (nv4->nvbase.svga.vram_max - 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/nv4t only and 2mb...i haven't found a 22mb card) + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + + uint32_t val32 = (uint32_t)val; + + //if (!nv4_ramin_arbitrate_write(addr, val32)) + //{ + nv4->nvbase.svga.vram[addr] = val; + nv_log_verbose_only("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); + //} +} + +// Write 16-bit ramin +void nv4_ramin_write16(uint32_t addr, uint16_t val, void* priv) +{ + if (!nv4) return; + + addr &= (nv4->nvbase.svga.vram_max - 1); + + // why does this not work in one line + svga_t* svga = &nv4->nvbase.svga; + uint16_t* vram_16bit = (uint16_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + addr >>= 1; // what + + uint32_t val32 = (uint32_t)val; + + //if (!nv4_ramin_arbitrate_write(addr, val32)) + //{ + vram_16bit[addr] = val; + nv_log_verbose_only("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); + //} + + +} + +// Write 32-bit ramin +void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv) +{ + if (!nv4) return; + + addr &= (nv4->nvbase.svga.vram_max - 1); + + // why does this not work in one line + svga_t* svga = &nv4->nvbase.svga; + uint32_t* vram_32bit = (uint32_t*)svga->vram; + uint32_t raw_addr = addr; // saved after and + + addr ^= (nv4->nvbase.svga.vram_max - 0x10); + addr >>= 2; // what + + //if (!nv4_ramin_arbitrate_write(addr, val)) + //{ + vram_32bit[addr] = val; + nv_log_verbose_only("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); + //} +} + +// Read from SVGA core memory +uint8_t nv4_svga_read(uint16_t addr, void* priv) +{ + // CR = CRTC Controller + // CRE = CRTC Controller Extended (weitek) + + nv4_t* nv4 = (nv4_t*)priv; + + uint8_t ret = 0x00; + + // sanity check + if (!nv4) + return ret; + + // If we need to RMA from GPU MMIO, go do that + if (addr >= NV4_RMA_REGISTER_START + && addr <= NV4_RMA_REGISTER_END) + { + if (!(nv4->pbus.rma.mode & 0x01)) + return ret; + + // must be dword aligned + uint32_t real_rma_read_addr = ((nv4->pbus.rma.mode & 0x0E) << 1) + (addr & 0x03); + ret = nv4_rma_read(real_rma_read_addr); + return ret; + } + + // mask off b0/d0 registers + if ((((addr & 0xFFF0) == 0x3D0 + || (addr & 0xFFF0) == 0x3B0) && addr < 0x3de) + && !(nv4->nvbase.svga.miscout & 1)) + addr ^= 0x60; + + switch (addr) + { + // Alias for "get current SVGA CRTC register ID" + case NV4_CIO_CRX_COLOR: + ret = nv4->nvbase.svga.crtcreg; + break; + case NV4_CIO_CR_COLOR: + // Support the extended NVIDIA CRTC register range + switch (nv4->nvbase.svga.crtcreg) + { + case NV4_CIO_CRE_RL0_INDEX: + ret = nv4->nvbase.svga.displine & 0xFF; + break; + /* Is rl1?*/ + case NV4_CIO_CRE_RL1_INDEX: + ret = (nv4->nvbase.svga.displine >> 8) & 7; + break; + case NV4_CIO_CRE_DDC_STATUS_INDEX: + ret = i2c_gpio_get_sda(nv4->nvbase.i2c) << 3 + | i2c_gpio_get_scl(nv4->nvbase.i2c) << 2; + + break; + default: + ret = nv4->nvbase.svga.crtc[nv4->nvbase.svga.crtcreg]; + } + break; + default: + ret = svga_in(addr, &nv4->nvbase.svga); + break; + } + + return ret; //TEMP +} + +// Write to SVGA core memory +void nv4_svga_write(uint16_t addr, uint8_t val, void* priv) +{ + // sanity check + if (!nv4) + return; + + // If we need to RMA to GPU MMIO, go do that + if (addr >= NV4_RMA_REGISTER_START + && addr <= NV4_RMA_REGISTER_END) + { + // we don't need to store these registers... + nv4->pbus.rma.rma_regs[addr & 3] = val; + + if (!(nv4->pbus.rma.mode & 0x01)) // we are halfway through sending something + return; + + uint32_t real_rma_write_addr = ((nv4->pbus.rma.mode & (0x0E)) << 1) + (addr & 0x03); + + nv4_rma_write(real_rma_write_addr, nv4->pbus.rma.rma_regs[addr & 0x03]); + return; + } + + // mask off b0/d0 registers + if ((((addr & 0xFFF0) == 0x3D0 || (addr & 0xFFF0) == 0x3B0) + && addr < 0x3de) + && !(nv4->nvbase.svga.miscout & 1))//miscout bit 7 controls mappping + addr ^= 0x60; + + uint8_t crtcreg = nv4->nvbase.svga.crtcreg; + uint8_t old_val = 0x00; + + // todo: + // Pixel formats (8bit vs 555 vs 565) + // VBE 3.0? + + switch (addr) + { + case NV4_CIO_CRX_COLOR: + // real mode access to GPU MMIO space... + nv4->nvbase.svga.crtcreg = val; + break; + // support the extended crtc regs and debug this out + case NV4_CIO_CR_COLOR: + + // Implements the VGA Protect register + if ((nv4->nvbase.svga.crtcreg < NV4_CIO_CR_OVL_INDEX) && (nv4->nvbase.svga.crtc[NV4_CIO_CR_VRE_INDEX] & 0x80)) + return; + + // Ignore certain bits when VGA Protect register set and we are writing to CRTC register=07h + if ((nv4->nvbase.svga.crtcreg == NV4_CIO_CR_OVL_INDEX) && (nv4->nvbase.svga.crtc[NV4_CIO_CR_VRE_INDEX] & 0x80)) + val = (nv4->nvbase.svga.crtc[NV4_CIO_CR_OVL_INDEX] & ~0x10) | (val & 0x10); + + // set the register value... + old_val = nv4->nvbase.svga.crtc[crtcreg]; + + nv4->nvbase.svga.crtc[crtcreg] = val; + // ...now act on it + + // Handle nvidia extended Bank0/Bank1 IDs + switch (crtcreg) + { + case NV4_CIO_CRE_PAGE0_INDEX: + nv4->nvbase.cio_read_bank = val; + if (nv4->nvbase.svga.chain4) // chain4 addressing (planar?) + nv4->nvbase.svga.read_bank = nv4->nvbase.cio_read_bank << 15; + else + nv4->nvbase.svga.read_bank = nv4->nvbase.cio_read_bank << 13; // extended bank numbers + break; + case NV4_CIO_CRE_PAGE1_INDEX: + nv4->nvbase.cio_write_bank = val; + if (nv4->nvbase.svga.chain4) + nv4->nvbase.svga.write_bank = nv4->nvbase.cio_write_bank << 15; + else + nv4->nvbase.svga.write_bank = nv4->nvbase.cio_write_bank << 13; + break; + case NV4_CIO_CRE_RMA_INDEX: + nv4->pbus.rma.mode = val & NV4_PRMIO_RMA_MODE_MAX; + break; + /* Handle some large screen stuff */ + case NV4_CIO_CRE_PIXEL_INDEX: + if (val & 1 << (NV4_CIO_CRE_LSR_VDT_10)) + nv4->nvbase.svga.vtotal += 0x400; + if (val & 1 << (NV4_CIO_CRE_LSR_VRS_10)) + nv4->nvbase.svga.vblankstart += 0x400; + if (val & 1 << (NV4_CIO_CRE_LSR_VBS_10)) + nv4->nvbase.svga.vsyncstart += 0x400; + if (val & 1 << (NV4_CIO_CRE_LSR_HBE_6)) + nv4->nvbase.svga.hdisp += 0x400; + + /* Make sure dispend and vblankstart are right if we are displaying above 1024 vert */ + if (nv4->nvbase.svga.crtc[NV4_CIO_CRE_PIXEL_INDEX] & 1 << (NV4_CIO_CRE_LSR_VDE_10)) + nv4->nvbase.svga.dispend += 0x400; + + break; + case NV4_CIO_CRE_HEB_INDEX: // large screen bit + if (val & 0x01) + nv4->nvbase.svga.hdisp += 0x100; + break; + case NV4_CIO_CRE_DDC_WR_INDEX: + { + uint8_t scl = !!(val & 0x20); + uint8_t sda = !!(val & 0x10); + // Set an I2C GPIO register + i2c_gpio_set(nv4->nvbase.i2c, scl, sda); + break; + } + /* [6:0] contains cursorAddr [23:17] */ + case NV4_CIO_CRE_HCUR_ADDR0_INDEX: + nv4->pramdac.cursor_address |= (val & 0x7F) << 13; //bit7 technically ignored, but nv don't care, so neither do we + break; + /* [7:2] contains cursorAddr [16:11] */ + case NV4_CIO_CRE_HCUR_ADDR1_INDEX: + nv4->pramdac.cursor_address |= (val & 0xFC) << 5; // bit0 and 1 aren't part of the address + break; + + + } + + /* Recalculate the timings if we actually changed them + Additionally only do it if the value actually changed*/ + if (old_val != val) + { + // Thx to Fuel who basically wrote most of the SVGA compatibility code already (although I fixed some issues), because VGA is boring + // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. + // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" + if (nv4->nvbase.svga.crtcreg < 0xE + || nv4->nvbase.svga.crtcreg > 0x10) + { + nv4->nvbase.svga.fullchange = changeframecount; + nv4_recalc_timings(&nv4->nvbase.svga); + } + } + + break; + default: + svga_out(addr, val, &nv4->nvbase.svga); + break; + } + +} + +/* DFB, sets up a dumb framebuffer */ +uint8_t nv4_dfb_read8(uint32_t addr, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + return nv4->nvbase.svga.vram[addr]; +} + +uint16_t nv4_dfb_read16(uint32_t addr, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + return (nv4->nvbase.svga.vram[addr + 1] << 8) | nv4->nvbase.svga.vram[addr]; +} + +uint32_t nv4_dfb_read32(uint32_t addr, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + return (nv4->nvbase.svga.vram[addr + 3] << 24) | (nv4->nvbase.svga.vram[addr + 2] << 16) + + (nv4->nvbase.svga.vram[addr + 1] << 8) | nv4->nvbase.svga.vram[addr]; +} + +void nv4_dfb_write8(uint32_t addr, uint8_t val, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + nv4->nvbase.svga.vram[addr] = val; + nv4->nvbase.svga.changedvram[addr >> 12] = val; + //nv4_render_current_bpp_dfb_8(addr); +} + +void nv4_dfb_write16(uint32_t addr, uint16_t val, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + nv4->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; + nv4->nvbase.svga.vram[addr] = (val) & 0xFF; + nv4->nvbase.svga.changedvram[addr >> 12] = val; + + //nv4_render_current_bpp_dfb_16(addr); + +} + +void nv4_dfb_write32(uint32_t addr, uint32_t val, void* priv) +{ + addr &= (nv4->nvbase.svga.vram_mask); + nv4->nvbase.svga.vram[addr + 3] = (val >> 24) & 0xFF; + nv4->nvbase.svga.vram[addr + 2] = (val >> 16) & 0xFF; + nv4->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; + nv4->nvbase.svga.vram[addr] = (val) & 0xFF; + nv4->nvbase.svga.changedvram[addr >> 12] = val; + + //removed until there is a render pipeline + //nv4_render_current_bpp_dfb_32(addr); + +} + +/* Cursor shit */ +void nv4_draw_cursor(svga_t* svga, int32_t drawline) +{ + // sanity check + if (!nv4) + return; + + /* + Commented out until we have a real graphics pipeline going here + + // if cursor disabled is set, return + if ((nv4->nvbase.svga.crtc[NV4_CRTC_REGISTER_CURSOR_START] >> NV4_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01) + return; + + // NT GDI drivers: Load cursor using NV_IMAGE_FROM_MEMORY ("NV4LCD") + // 9x GDI drivers: Use H/W cursor in RAMIN + + // Do we need to emulate it? + + // THIS IS CORRECT. BUT HOW DO WE FIND IT? + uint32_t ramin_cursor_position = NV4_RAMIN_OFFSET_CURSOR; + + /* let's just assume buffer 0 here...that code needs to be totally rewritten + nv4_coord_16_t start_position = nv4->pramdac.cursor_start; + + /* refuse to draw if thge cursor is offscreen + if (start_position.x >= nv4->nvbase.svga.hdisp + || start_position.y >= nv4->nvbase.svga.dispend) + { + return; + } + + nv_log("nv4_draw_cursor start=0x%04x,0x%04x", start_position.x, start_position.y); + + uint32_t final_position = nv4_render_get_vram_address_for_buffer(start_position, 0); + + uint16_t* vram_16 = (uint16_t*)nv4->nvbase.svga.vram; + uint32_t* vram_32 = (uint32_t*)nv4->nvbase.svga.vram; + + /* + We have to get a 32x32, "A"1R5G5B5-format cursor + out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1 + + These are expanded to RGB10 only if they are XORed. We don't do this (we don't really need to + there is no grobj specified here so special casing + would be needed) so we just xor it with the current pixel format + + for (int32_t y = 0; y < NV4_PRAMDAC_CURSOR_SIZE_Y; y++) + { + for (int32_t x = 0; x < NV4_PRAMDAC_CURSOR_SIZE_X; x++) + { + uint16_t current_pixel = nv4_ramin_read16(ramin_cursor_position, nv4); + + // 0000 = transparent, so skip drawing + if (current_pixel) + { + bool replace_bit = (current_pixel & 0x8000); + + // use buffer 0 BPIXEL + uint32_t bpixel_format = (nv4->pgraph.bpixel[0]) & 0x03; + + switch (bpixel_format) + { + case bpixel_fmt_8bit: + if (replace_bit) + nv4->nvbase.svga.vram[final_position] = current_pixel; + else //xor + { + // not sure what to do here. we'd have to search through the palette to find the closest possible colour. + uint8_t final = current_pixel ^ nv4->nvbase.svga.vram[final_position]; + nv4->nvbase.svga.vram[final_position] = final; + } + case bpixel_fmt_16bit: // easy case (our cursor is 15bpp format) + uint32_t index_16 = final_position >> 1; + + if (replace_bit) // just replace + vram_16[index_16] = current_pixel; + else // xor + { + current_pixel &= ~0x8000; // mask off the xor bit + uint16_t final = current_pixel ^ vram_16[index_16]; + vram_16[index_16] = final; + } + case bpixel_fmt_32bit: + uint32_t index_32 = final_position >> 2; + + if (replace_bit) // just replace + vram_32[index_32] = nv4->nvbase.svga.conv_16to32(&nv4->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here + else //xor + { + current_pixel &= ~0x8000; // mask off the xor bit + uint32_t current_pixel_32 = nv4->nvbase.svga.conv_16to32(&nv4->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here + + uint32_t final = current_pixel_32 ^ vram_32[index_32]; + vram_32[index_32] = final; + } + break; + } + } + + // increment vram position + ramin_cursor_position += 2; + + // go + switch (nv4->nvbase.svga.bpp) + { + case 8: + final_position++; + case 15 ... 16: + final_position += 2; + break; + case 32: + final_position += 4; + break; + } + + start_position.x++; + } + + + start_position.y++; + start_position.x = nv4->pramdac.cursor_start.x; + + // reset at the end of each line so we "jump" to the start x + final_position = nv4_render_get_vram_address_for_buffer(start_position, 0); + }*/ +} + +// MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. +// Note this area is 64kb and the vbios is only 32kb. See below.. + +uint8_t nv4_prom_read(uint32_t address) +{ + // prom area is 64k, so... + // first see if we even have a rom of 64kb in size + uint32_t max_rom_size = NV4_PROM_END - NV4_PROM_START; + uint32_t real_rom_size = max_rom_size; + + // set it + if (nv4->nvbase.vbios.sz < max_rom_size) + real_rom_size = nv4->nvbase.vbios.sz; + + //get our real address + uint8_t rom_address = address & max_rom_size; + + // Does this mirror on real hardware? + if (rom_address >= real_rom_size) + { + nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); + return 0xFF; + } + else + { + uint8_t val = nv4->nvbase.vbios.rom[rom_address]; + nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); + return val; + } +} + +void nv4_prom_write(uint32_t address, uint32_t value) +{ + uint32_t real_addr = address & 0x1FFFF; + nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", real_addr, value); +} From 51f009b1e033dbd5ffdd129d8a72a4a2bba7be26 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 13 Sep 2025 17:23:59 +0100 Subject: [PATCH 222/274] log read/write --- src/video/nv/nv4/nv4_core_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index a7fe0d0c8..87614b8c2 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -44,12 +44,12 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); uint32_t nv4_mmio_arbitrate_read(uint32_t addr) { - + nv_log_verbose_only("MMIO read from address=0x%08x", addr); } void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) { - + nv_log_verbose_only("MMIO write to address=0x%08x value %08x", addr, val); } // Determine if this address needs to be redirected to the SVGA subsystem. From 3f27fab2db6e247df640ad6f911c2b9079e03d58 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 13 Sep 2025 19:24:16 +0100 Subject: [PATCH 223/274] actually initialise mappings --- src/include/86box/nv/vid_nv4.h | 7 +++- src/video/CMakeLists.txt | 1 + src/video/nv/nv3/nv3_core.c | 3 -- src/video/nv/nv4/nv4_core.c | 42 ++++++++++++++++++++++-- src/video/nv/nv4/nv4_core_config.c | 4 --- src/video/nv/nv4/subsystems/nv4_ptimer.c | 15 +++++---- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 7fda0fca4..47c62853a 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -91,7 +91,10 @@ extern nv4_t* nv4; // Alloc // // Device Core -void nv4_init(); +bool nv4_init(); + +void* nv4_init_stb4400(const device_t* info); + void nv4_close(void* priv); void nv4_speed_changed(void *priv); void nv4_draw_cursor(svga_t* svga, int32_t drawline); @@ -117,6 +120,8 @@ uint32_t nv4_ramin_read32(uint32_t addr, void* priv); void nv4_ramin_write8(uint32_t addr, uint8_t val, void* priv); void nv4_ramin_write16(uint32_t addr, uint16_t val, void* priv); void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); +uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv); +void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); uint8_t nv4_svga_read(uint16_t addr, void* priv); diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index c03ac15c9..b9c735be8 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -201,6 +201,7 @@ add_library(vid OBJECT nv/nv4/nv4_core.c nv/nv4/nv4_core_io.c nv/nv4/nv4_core_config.c + nv/nv4/subsystems/nv4_ptimer.c # Generic vid_bochs_vbe.c diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index d051a8f15..ec5fe5f92 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -138,10 +138,7 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) return ret; } - ret = nv3_mmio_arbitrate_read(addr); - - return ret; } diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 58aba93b1..c210b0424 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -29,6 +29,9 @@ nv4_t* nv4; +// Stolen from Voodoo 3 +static video_timings_t timing_nv4_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; + // Initialise the MMIO mappings void nv4_init_mappings_mmio(void) { @@ -185,7 +188,7 @@ void nv4_update_mappings(void) } -void nv4_init() +bool nv4_init() { nv4 = calloc(1, sizeof(nv4_t)); @@ -200,13 +203,46 @@ void nv4_init() else nv4->nvbase.log = log_open_cyclic("NV4"); + nv4->nvbase.bus_generation = nv_bus_agp_2x; + nv_log_set_device(nv4->nvbase.log); + // Figure out which vbios the user selected + // This depends on the bus we are using and if the gpu is rev a/b or rev c + + const char* vbios_file = NV4_VBIOS_STB_REVA; + + int32_t err = rom_init(&nv4->nvbase.vbios, vbios_file, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); + + if (err) + { + nv_log("NV4 FATAL: failed to load VBIOS err=%d\n", err); + fatal("Nvidia NV4 init failed: Somehow selected a nonexistent VBIOS? err=%d\n", err); + return false; + } + else + nv_log("Successfully loaded VBIOS located at %s\n", vbios_file); + + pci_add_card(PCI_ADD_AGP, nv4_pci_read, nv4_pci_write, NULL, &nv4->nvbase.pci_slot); + + svga_init(&nv4_device_agp, &nv4->nvbase.svga, nv4, nv4->nvbase.vram_amount, + nv4_recalc_timings, nv4_svga_read, nv4_svga_write, nv4_draw_cursor, NULL); + + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv4_agp); + + nv4_init_mappings(); + //nv4_update_mappings(); + + return true; } void* nv4_init_stb4400(const device_t *info) { - nv4_init(); - return nv4; + bool successful = nv4_init(); + + if (successful) + return nv4; + else + return NULL; } void nv4_close(void* priv) diff --git a/src/video/nv/nv4/nv4_core_config.c b/src/video/nv/nv4/nv4_core_config.c index ea9ef9332..2dd88eed6 100644 --- a/src/video/nv/nv4/nv4_core_config.c +++ b/src/video/nv/nv4/nv4_core_config.c @@ -55,11 +55,7 @@ const device_config_t nv4_config[] = { .name = "pgraph_threads", -#ifndef RELEASE_BUILD - .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", -#else .description = "Render threads", -#endif .type = CONFIG_SELECTION, .default_int = 1, // todo: change later .selection = diff --git a/src/video/nv/nv4/subsystems/nv4_ptimer.c b/src/video/nv/nv4/subsystems/nv4_ptimer.c index b52f79f17..29df0168b 100644 --- a/src/video/nv/nv4/subsystems/nv4_ptimer.c +++ b/src/video/nv/nv4/subsystems/nv4_ptimer.c @@ -52,7 +52,8 @@ void nv4_ptimer_interrupt(uint32_t num) { nv4->ptimer.interrupt_status |= (1 << num); - nv4_pmc_handle_interrupts(true); + //todo + //nv4_pmc_handle_interrupts(true); } // Ticks the timer. @@ -128,14 +129,14 @@ uint32_t nv4_ptimer_read(uint32_t address) break; // 64-bit value // High part - case NV4_PTIMER_TIME_0_NSEC: + case NV4_PTIMER_TIME_0: ret = nv4->ptimer.time & 0xFFFFFFFF; //28:0 break; // Low part - case NV4_PTIMER_TIME_1_NSEC: + case NV4_PTIMER_TIME_1: ret = nv4->ptimer.time >> 32; // 31:5 break; - case NV4_PTIMER_ALARM_NSEC: + case NV4_PTIMER_ALARM: ret = nv4->ptimer.alarm; // 31:5 break; } @@ -186,7 +187,7 @@ void nv4_ptimer_write(uint32_t address, uint32_t value) case NV4_PTIMER_INTR: nv4->ptimer.interrupt_status &= ~value; - nv4_pmc_clear_interrupts(); + //TODO nv4_pmc_clear_interrupts(); break; // Interrupt enablement state @@ -206,11 +207,11 @@ void nv4_ptimer_write(uint32_t address, uint32_t value) break; // 64-bit value // High part - case NV4_PTIMER_TIME_0_NSEC: + case NV4_PTIMER_TIME_0: nv4->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 break; // Low part - case NV4_PTIMER_TIME_1_NSEC: + case NV4_PTIMER_TIME_1: nv4->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 break; case NV4_PTIMER_ALARM: From accf4997905b3763f7b7d3a353911022894470a5 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sat, 13 Sep 2025 22:55:01 +0100 Subject: [PATCH 224/274] RIVA TNT Video BIOS now runs. --- src/include/86box/nv/vid_nv4_defines.h | 10 ++- src/video/nv/nv3/nv3_core.c | 1 - src/video/nv/nv4/nv4_core.c | 88 +++++++++++++++++-- src/video/nv/nv4/nv4_core_io.c | 114 ++++++------------------- 4 files changed, 116 insertions(+), 97 deletions(-) diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h index 21cf5739a..3bda4dc1c 100644 --- a/src/include/86box/nv/vid_nv4_defines.h +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -505,6 +505,7 @@ #define NV4_PBUS_PCI_DEVICE_ID 0x1802 #define NV4_PBUS_PCI_DEVICE_ID_NV4 0x0020 // Chip (19:17)= NV4, Func = VGA #define NV4_PBUS_PCI_COMMAND 0x1804 +#define NV4_PBUS_PCI_COMMAND_H 0x1805 #define NV4_PBUS_PCI_COMMAND_IO_SPACE 0 #define NV4_PBUS_PCI_COMMAND_IO_SPACE_ENABLED 0x1 #define NV4_PBUS_PCI_COMMAND_MEMORY_SPACE 1 @@ -568,9 +569,12 @@ #define NV4_PBUS_PCI_BAR0_UNUSED2 0x1812 #define NV4_PBUS_PCI_BAR0_BASE_31_TO_24 0x1813 // Must align to 16MByte #define NV4_PBUS_PCI_BAR1_INFO 0x1814 -#define NV4_PBUS_PCI_BAR1_UNUSED1 0x1814 -#define NV4_PBUS_PCI_BAR1_UNUSED2 0x1815 -#define NV4_PBUS_PCI_BAR1_BASE_31_TO_24 0x1816 // Must align to 16MByte +#define NV4_PBUS_PCI_BAR1_UNUSED1 0x1815 +#define NV4_PBUS_PCI_BAR1_UNUSED2 0x1816 +#define NV4_PBUS_PCI_BAR1_BASE_31_TO_24 0x1817 // Must align to 16MByte +#define NV4_PBUS_PCI_BAR_RESERVED_START 0x1818 +#define NV4_PBUS_PCI_BAR_RESERVED_END 0x182B + //BAR2-5 reserved #define NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID 0x182C #define NV4_PBUS_PCI_SUBSYSTEM_ID 0x182E diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c index ec5fe5f92..01af9100d 100644 --- a/src/video/nv/nv3/nv3_core.c +++ b/src/video/nv/nv3/nv3_core.c @@ -137,7 +137,6 @@ uint32_t nv3_mmio_read32(uint32_t addr, void* priv) return ret; } - ret = nv3_mmio_arbitrate_read(addr); return ret; diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index c210b0424..285988800 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -66,6 +66,7 @@ void nv4_init_mappings_svga(void) { nv_log("Initialising SVGA core memory mapping\n"); // setup the svga mappings + mem_mapping_add(&nv4->nvbase.framebuffer_mapping, 0, 0, nv4_dfb_read8, nv4_dfb_read16, @@ -198,14 +199,18 @@ bool nv4_init() /* Set log device name based on card model */ const char* log_device_name = "NV4"; + + /* Just hardcode full logging */ + if (device_get_config_int("nv_debug_fulllog")) - nv4->nvbase.log = log_open("NV4"); + nv4->nvbase.log = log_open(log_device_name); else - nv4->nvbase.log = log_open_cyclic("NV4"); + nv4->nvbase.log = log_open_cyclic(log_device_name); + + nv_log_set_device(nv4->nvbase.log); nv4->nvbase.bus_generation = nv_bus_agp_2x; - nv_log_set_device(nv4->nvbase.log); // Figure out which vbios the user selected // This depends on the bus we are using and if the gpu is rev a/b or rev c @@ -260,11 +265,82 @@ void nv4_draw_cursor(svga_t* svga, int32_t drawline) } -void nv4_recalc_timings(svga_t* svga) -{ +// +// SVGA functions +// +void nv4_recalc_timings(svga_t* svga) +{ + // sanity check + if (!nv4) + return; + + + nv4_t* nv4 = (nv4_t*)svga->priv; + + // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the OFFSET register + uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; + + svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; + + /* Turn off override if we are in VGA mode */ + svga->override = !(pixel_mode == NV4_CIO_CRE_PIXEL_FORMAT_VGA); + + /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. + + Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. + + This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. + In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. + + Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick + to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from + the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. + */ + + // Set the pixel mode + switch (pixel_mode) + { + case NV4_CIO_CRE_PIXEL_FORMAT_8BPP: + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 1; // ????? + svga->bpp = 8; + svga->lowres = 0; + svga->map8 = svga->pallook; + break; + case NV4_CIO_CRE_PIXEL_FORMAT_16BPP: + /* This is some sketchy shit that is an attempt at an educated guess + at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, + this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ + if ((svga->crtc[NV4_CIO_CR_VRS_INDEX] >> 1) & 0x01) + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 2; + else + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; + + // 15bpp mode is removed on NV4 + // TODO: Not svga + svga->bpp = 16; + svga->lowres = 0; + + break; + case NV4_CIO_CRE_PIXEL_FORMAT_32BPP: + svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; + + svga->bpp = 32; + svga->lowres = 0; + //svga->render = nv4_render_32bpp; + break; + } + + + if (((svga->miscout >> 2) & 2) == 2) + { + // set clocks + //nv4_pramdac_set_pixel_clock(); + //nv4_pramdac_set_vram_clock(); + } } + void nv4_force_redraw(void* priv) { @@ -281,7 +357,7 @@ int32_t nv4_available(void) // 8MB or 16MB VRAM const device_t nv4_device_agp = { - .name = "nVIDIA RIVA TNT [STB Velocity 4400]", + .name = "nVIDIA RIVA TNT (STB Velocity 4400)", .internal_name = "nv4_stb4400", .flags = DEVICE_AGP, .local = 0, diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index 87614b8c2..2849efac1 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -438,7 +438,7 @@ uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) // We only need to return 0x30 since the VGA class code is 0x30000 case NV4_PBUS_PCI_CLASS_CODE: - ret = (NV4_PBUS_PCI_CLASS_CODE_VGA) >> 12; // CLASS_CODE_VGA + ret = (NV4_PBUS_PCI_CLASS_CODE_VGA) >> 16; // CLASS_CODE_VGA break; @@ -462,6 +462,15 @@ uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) case NV4_PBUS_PCI_BAR1_BASE_31_TO_24: ret = nv4->nvbase.bar1_lfb_base >> 24; //8bit value break; + + case NV4_PBUS_PCI_BAR_RESERVED_START ... NV4_PBUS_PCI_BAR_RESERVED_END: + case NV4_PBUS_PCI_BAR0_UNUSED1: + case NV4_PBUS_PCI_BAR0_UNUSED2: + case NV4_PBUS_PCI_BAR1_UNUSED1: + case NV4_PBUS_PCI_BAR1_UNUSED2: + + ret = 0x00; // hard lock + break; case NV4_PBUS_PCI_ROM: ret = nv4->nvbase.pci_config.vbios_enabled; @@ -504,6 +513,7 @@ uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE + 1: ret = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_SUBSYSTEM_ID + (addr & 0x03)]; break; + case NV4_PBUS_AGP_CAPABILITIES: ret = NV4_PBUS_AGP_CAPABILITY_AGP; // AGP capable device break; @@ -529,7 +539,7 @@ uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) } - nv_log("nv4_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); + nv_log("nv4_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr & 0xFF, ret); return ret; } @@ -550,28 +560,28 @@ void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) && addr == NV4_PBUS_PCI_BAR1_UNUSED1 || addr == NV4_PBUS_PCI_BAR1_UNUSED2) return; - nv_log("nv4_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); + nv_log("nv4_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr & 0xFF, val); nv4->nvbase.pci_config.pci_regs[addr] = val; switch (addr) { // standard pci command stuff - case PCI_REG_COMMAND_L: + case NV4_PBUS_PCI_COMMAND: nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L] = val; // actually update the mappings nv4_update_mappings(); break; - case PCI_REG_COMMAND_H: + case NV4_PBUS_PCI_COMMAND_H: nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_H] = val; // actually update the mappings nv4_update_mappings(); break; // pci status register - case PCI_REG_STATUS_L: + case NV4_PBUS_PCI_STATUS: nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] = val | (NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE << NV4_PBUS_PCI_STATUS_66MHZ); break; - case PCI_REG_STATUS_H: + case NV4_PBUS_PCI_STATUS_2: nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H] = val | (NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST << NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING); break; //TODO: ACTUALLY REMAP THE MMIO AND NV_USER @@ -583,6 +593,7 @@ void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) nv4->nvbase.bar1_lfb_base = val << 24; nv4_update_mappings(); break; + case NV4_PBUS_PCI_ROM: case NV4_PBUS_PCI_ROM_BASE: @@ -601,13 +612,16 @@ void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) { uint32_t old_addr = nv4->nvbase.vbios.mapping.base; // 9bit register - uint32_t new_addr = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 1] << 24 | - nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM] << 16; + uint32_t new_addr = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 3] << 24 | + nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 2] << 16; + + // only bits 31;22 matter + //new_addr &= 0xFFC00000; // move it mem_mapping_set_addr(&nv4->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); + nv_log("...i like to move it move it (VBIOS Relocation) 0x%x -> 0x%x\n", old_addr, new_addr); } else @@ -643,80 +657,6 @@ void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) } -// -// SVGA functions -// -void nv4_recalc_timings(svga_t* svga) -{ - // sanity check - if (!nv4) - return; - - - nv4_t* nv4 = (nv4_t*)svga->priv; - - // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the OFFSET register - uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; - - svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; - - /* Turn off override if we are in VGA mode */ - svga->override = !(pixel_mode == NV4_CIO_CRE_PIXEL_FORMAT_VGA); - - /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. - - Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. - - This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. - In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. - - Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick - to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from - the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. - */ - - // Set the pixel mode - switch (pixel_mode) - { - case NV4_CIO_CRE_PIXEL_FORMAT_8BPP: - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 1; // ????? - svga->bpp = 8; - svga->lowres = 0; - svga->map8 = svga->pallook; - break; - case NV4_CIO_CRE_PIXEL_FORMAT_16BPP: - /* This is some sketchy shit that is an attempt at an educated guess - at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, - this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ - if ((svga->crtc[NV4_CIO_CR_VRS_INDEX] >> 1) & 0x01) - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 2; - else - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; - - // 15bpp mode is removed on NV4 - // TODO: Not svga - svga->bpp = 16; - svga->lowres = 0; - - break; - case NV4_CIO_CRE_PIXEL_FORMAT_32BPP: - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; - - svga->bpp = 32; - svga->lowres = 0; - //svga->render = nv4_render_32bpp; - break; - } - - - if (((svga->miscout >> 2) & 2) == 2) - { - // set clocks - //nv4_pramdac_set_pixel_clock(); - //nv4_pramdac_set_vram_clock(); - } -} - void nv4_speed_changed(void* priv) { // sanity check @@ -888,9 +828,6 @@ uint8_t nv4_svga_read(uint16_t addr, void* priv) { // CR = CRTC Controller // CRE = CRTC Controller Extended (weitek) - - nv4_t* nv4 = (nv4_t*)priv; - uint8_t ret = 0x00; // sanity check @@ -947,6 +884,8 @@ uint8_t nv4_svga_read(uint16_t addr, void* priv) break; } + nv_log("SVGA read 0x%04x value 0x%02x\n", addr, ret); + return ret; //TEMP } @@ -1090,6 +1029,7 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv) break; } + nv_log("SVGA write 0x%04x value 0x%02x\n", addr, val); } /* DFB, sets up a dumb framebuffer */ From b073c1d66a99633e5db3fe00f319066eabd8c12c Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 14 Sep 2025 00:18:54 +0100 Subject: [PATCH 225/274] Fix force redraw so hard reset not needed --- CMakePresets.json | 4 +++- src/include/86box/nv/vid_nv4.h | 20 +++++++++----------- src/video/nv/nv4/nv4_core.c | 5 ----- src/video/nv/nv4/nv4_core_io.c | 2 -- src/video/nv/nv4/subsystems/nv4_pramdac.c | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 src/video/nv/nv4/subsystems/nv4_pramdac.c diff --git a/CMakePresets.json b/CMakePresets.json index 30feab8ab..53ddffc74 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -36,7 +36,9 @@ "name": "debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON" + "NV_LOG": "ON", + "NV_LOG_ULTRA": "ON" + }, "inherits": "base" }, diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index 47c62853a..bcdfb4dd0 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -20,12 +20,13 @@ #include #include <86Box/nv/vid_nv4_defines.h> - // -// PBUS/RMA +// Structures // -// Access the GPU from real-mode + +// PBUS +// RMA: Access the GPU from real-mode typedef struct nv4_pbus_rma_s { uint32_t addr; // Address to RMA to @@ -43,10 +44,8 @@ typedef struct nv4_pbus_s nv4_pbus_rma_t rma; } nv4_pbus_t; -// -// PTIMER -// +// PTIMER typedef struct nv4_ptimer_s { uint32_t interrupt_status; // PTIMER Interrupt status @@ -57,9 +56,7 @@ typedef struct nv4_ptimer_s uint32_t alarm; // The value of time when there should be an alarm } nv4_ptimer_t; -// // PRAMDAC -// typedef struct nv4_pramdac_s { uint32_t mclk; @@ -68,7 +65,7 @@ typedef struct nv4_pramdac_s uint32_t cursor_address; } nv4_pramdac_t; -// Structures +// Device Core typedef struct nv4_s { nv_base_t nvbase; // Base Nvidia structure @@ -78,10 +75,10 @@ typedef struct nv4_s nv4_pramdac_t pramdac; } nv4_t; - // // Globals // + extern const device_config_t nv4_config[]; extern nv4_t* nv4; // Allocated at device startup @@ -101,7 +98,7 @@ void nv4_draw_cursor(svga_t* svga, int32_t drawline); void nv4_recalc_timings(svga_t* svga); void nv4_force_redraw(void* priv); -// I/o +// I/O uint8_t nv4_mmio_read8(uint32_t addr, void* priv); uint16_t nv4_mmio_read16(uint32_t addr, void* priv); uint32_t nv4_mmio_read32(uint32_t addr, void* priv); @@ -123,6 +120,7 @@ void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv); void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); +// PRAMDAC uint8_t nv4_svga_read(uint16_t addr, void* priv); void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 285988800..13f9df321 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -341,11 +341,6 @@ void nv4_recalc_timings(svga_t* svga) } -void nv4_force_redraw(void* priv) -{ - -} - // See if the bios rom is available. int32_t nv4_available(void) { diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index 2849efac1..51a702bd3 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -884,7 +884,6 @@ uint8_t nv4_svga_read(uint16_t addr, void* priv) break; } - nv_log("SVGA read 0x%04x value 0x%02x\n", addr, ret); return ret; //TEMP } @@ -1029,7 +1028,6 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv) break; } - nv_log("SVGA write 0x%04x value 0x%02x\n", addr, val); } /* DFB, sets up a dumb framebuffer */ diff --git a/src/video/nv/nv4/subsystems/nv4_pramdac.c b/src/video/nv/nv4/subsystems/nv4_pramdac.c new file mode 100644 index 000000000..d8d4883ec --- /dev/null +++ b/src/video/nv/nv4/subsystems/nv4_pramdac.c @@ -0,0 +1,16 @@ +/* +* 86Box A hypervisor and IBM PC system emulator that specializes in +* running old operating systems and software designed for IBM +* PC systems and compatibles from 1981 through fairly recent +* system designs based on the PCI bus. +* +* This file is part of the 86Box distribution. +* +* NV4/Riva TNT - RAMDAC +* +* +* Authors: Connor Hyde, I need a better email address ;^) +* +* Copyright 2024-2025 starfrost +*/ + From ec40252a71bead4468ca5b00eb7c0bb0eb430947 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 14 Sep 2025 01:08:13 +0100 Subject: [PATCH 226/274] slightly clarify some divs --- src/include/86box/nv/vid_nv4_defines.h | 6 +++--- src/video/nv/nv3/subsystems/nv3_pramdac.c | 2 -- src/video/nv/nv4/nv4_core_io.c | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h index 3bda4dc1c..2a959670c 100644 --- a/src/include/86box/nv/vid_nv4_defines.h +++ b/src/include/86box/nv/vid_nv4_defines.h @@ -49,15 +49,15 @@ #define NV4_PRAMDAC_NVPLL_COEFF 0x680500 #define NV4_PRAMDAC_NVPLL_COEFF_MDIV 0 #define NV4_PRAMDAC_NVPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_NVPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_NVPLL_COEFF_PDIV 16 // 18:16 #define NV4_PRAMDAC_MPLL_COEFF 0x680504 #define NV4_PRAMDAC_MPLL_COEFF_MDIV 0 #define NV4_PRAMDAC_MPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_MPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_MPLL_COEFF_PDIV 16 // 18:16 #define NV4_PRAMDAC_VPLL_COEFF 0x680508 #define NV4_PRAMDAC_VPLL_COEFF_MDIV 0 #define NV4_PRAMDAC_VPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_VPLL_COEFF_PDIV 16 +#define NV4_PRAMDAC_VPLL_COEFF_PDIV 16 // 18:16 #define NV4_PRAMDAC_PLL_COEFF_SELECT 0x68050C #define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE 0 #define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE_XTAL 0x0 diff --git a/src/video/nv/nv3/subsystems/nv3_pramdac.c b/src/video/nv/nv3/subsystems/nv3_pramdac.c index f179c8136..9ce5a189a 100644 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ b/src/video/nv/nv3/subsystems/nv3_pramdac.c @@ -156,8 +156,6 @@ void nv3_pramdac_set_pixel_clock(void) // 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) diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index 51a702bd3..d9fa6f58a 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -44,12 +44,12 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); uint32_t nv4_mmio_arbitrate_read(uint32_t addr) { - nv_log_verbose_only("MMIO read from address=0x%08x", addr); + nv_log_verbose_only("MMIO read from address=0x%08x\n", addr); } void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) { - nv_log_verbose_only("MMIO write to address=0x%08x value %08x", addr, val); + nv_log_verbose_only("MMIO write to address=0x%08x value %08x\n", addr, val); } // Determine if this address needs to be redirected to the SVGA subsystem. @@ -95,7 +95,7 @@ uint8_t nv4_rma_read(uint16_t addr) } // log current location for vbios RE - nv_log_verbose_only("MMIO Real Mode Access read, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", + nv_log_verbose_only("MMIO Real Mode Access read, initial address=0x%08x final RMA MMIO address=0x%08x data=0x%08x\n", addr, real_final_address, ret); break; From 599b3910951c85220ef26a319fc8fdbf1acb16f8 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 21 Sep 2025 18:45:32 +0100 Subject: [PATCH 227/274] Add register framework --- src/include/86box/nv/vid_nv.h | 4 ++ src/include/86box/nv/vid_nv4.h | 27 ++++++++- src/video/CMakeLists.txt | 2 + src/video/nv/nv4/nv4_core.c | 27 ++++++++- src/video/nv/nv4/nv4_core_io.c | 39 ++++++++++++- src/video/nv/nv4/nv4_debug_register_list.c | 46 +++++++++++++++ src/video/nv/nv4/subsystems/nv4_pramdac.c | 68 ++++++++++++++++++++++ 7 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h index 3d69f75c7..83f94c167 100644 --- a/src/include/86box/nv/vid_nv.h +++ b/src/include/86box/nv/vid_nv.h @@ -136,6 +136,10 @@ typedef struct nv_base_s bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times double memory_clock_frequency; // Source Frequency for PTIMER rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock + + // VCLK / NVCLK do not have timers set here. + pc_timer_t* nv4_vclk_timer; // NV4+ MCLK (Video RAM) timer + bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times void* i2c; // I2C for monitor EDID void* ddc; // Display Data Channel for EDID diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h index bcdfb4dd0..0f2aad285 100644 --- a/src/include/86box/nv/vid_nv4.h +++ b/src/include/86box/nv/vid_nv4.h @@ -62,6 +62,7 @@ typedef struct nv4_pramdac_s uint32_t mclk; uint32_t vclk; uint32_t nvclk; + uint32_t clk_coeff_select; // Clock coefficient selection uint32_t cursor_address; } nv4_pramdac_t; @@ -83,6 +84,13 @@ extern const device_config_t nv4_config[]; extern nv4_t* nv4; // Allocated at device startup +#ifdef NV_LOG + +// Debug register list +extern nv_register_t nv4_registers[]; + +#endif + // // Functions // @@ -120,9 +128,22 @@ void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv); void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); -// PRAMDAC - +// SVGA uint8_t nv4_svga_read(uint16_t addr, void* priv); void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); -void nv4_update_mappings(); \ No newline at end of file +// Memory +void nv4_update_mappings(); + +// PRAMDAC +uint32_t nv4_pramdac_read(uint32_t address); +void nv4_pramdac_write(uint32_t address, uint32_t data); + +// We don't implement NVCLK/VCLK because they are too fast +void nv4_pramdac_set_vclk(); + +void nv4_vclk_tick(); + +// PTIMER +uint32_t nv4_ptimer_read(uint32_t address); +void nv4_ptimer_write(uint32_t address, uint32_t data); \ No newline at end of file diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index b9c735be8..f1d755dba 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -201,6 +201,8 @@ add_library(vid OBJECT nv/nv4/nv4_core.c nv/nv4/nv4_core_io.c nv/nv4/nv4_core_config.c + nv/nv4/nv4_debug_register_list.c + nv/nv4/subsystems/nv4_pramdac.c nv/nv4/subsystems/nv4_ptimer.c # Generic diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c index 13f9df321..5712cf7d1 100644 --- a/src/video/nv/nv4/nv4_core.c +++ b/src/video/nv/nv4/nv4_core.c @@ -199,7 +199,6 @@ bool nv4_init() /* Set log device name based on card model */ const char* log_device_name = "NV4"; - /* Just hardcode full logging */ if (device_get_config_int("nv_debug_fulllog")) @@ -234,6 +233,15 @@ bool nv4_init() video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv4_agp); + // Setup clock information. These values don't matter, I pulled them out of an STB BIOS, we don't need to macro them + nv4->pramdac.nvclk = 0x17809; + nv4->pramdac.mclk = 0x1a30a; + nv4->pramdac.vclk = 0x1400c; + + //timer_add(nv4->pramdac.nvclk, ) + + + nv4_init_mappings(); //nv4_update_mappings(); @@ -250,6 +258,21 @@ void* nv4_init_stb4400(const device_t *info) return NULL; } +void nv4_nvclk_tick() +{ + +} + +void nv4_mclk_tick() +{ + +} + +void nv4_vclk_tick() +{ + +} + void nv4_close(void* priv) { free(nv4); @@ -278,7 +301,7 @@ void nv4_recalc_timings(svga_t* svga) nv4_t* nv4 = (nv4_t*)svga->priv; - // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the OFFSET register + // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the offset register uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c index d9fa6f58a..4c5aa5a40 100644 --- a/src/video/nv/nv4/nv4_core_io.c +++ b/src/video/nv/nv4/nv4_core_io.c @@ -44,12 +44,47 @@ void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); uint32_t nv4_mmio_arbitrate_read(uint32_t addr) { - nv_log_verbose_only("MMIO read from address=0x%08x\n", addr); + uint32_t ret = 0x00; + + if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) + ret = nv4_ptimer_read(addr); + else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) + ret = nv4_pramdac_read(addr); + + #ifdef NV_LOG + nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); + + if (reg) + { + if (reg->on_read) + ret = reg->on_read(); + + nv_log_verbose_only("Register read from 0x%08x value=%08x", addr, ret); + + } + #endif + } void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) { - nv_log_verbose_only("MMIO write to address=0x%08x value %08x\n", addr, val); + if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) + nv4_ptimer_write(addr, val); + else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) + nv4_pramdac_write(addr, val); + + #ifdef NV_LOG + nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); + + if (reg) + { + if (reg->on_write) + reg->on_write(val); + + nv_log_verbose_only("Register write from 0x%08x value=%08x", addr, val); + + } + #endif } // Determine if this address needs to be redirected to the SVGA subsystem. diff --git a/src/video/nv/nv4/nv4_debug_register_list.c b/src/video/nv/nv4/nv4_debug_register_list.c index e69de29bb..32516be0c 100644 --- a/src/video/nv/nv4/nv4_debug_register_list.c +++ b/src/video/nv/nv4/nv4_debug_register_list.c @@ -0,0 +1,46 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * NV4 debug register list + * + * + * Authors: Connor Hyde, I need a better email address ;^) + * + * Copyright 2024-2025 starfrost + */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/io.h> +#include <86box/pci.h> +#include <86box/rom.h> +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + +#ifdef NV_LOG + +nv_register_t nv4_registers[] = { + { NV4_PTIMER_INTR, "NV4 PTIMER - Interrupt Status", NULL, NULL}, + { NV4_PTIMER_INTR_EN, "NV4 PTIMER - Interrupt Enable", NULL, NULL,}, + { NV4_PTIMER_NUMERATOR, "NV4 PTIMER - Numerator", NULL, NULL, }, + { NV4_PTIMER_DENOMINATOR, "NV4 PTIMER - Denominator", NULL, NULL, }, + { NV4_PTIMER_TIME_0_NSEC, "NV4 PTIMER - Time0", NULL, NULL, }, + { NV4_PTIMER_TIME_1_NSEC, "NV4 PTIMER - Time1", NULL, NULL, }, + { NV4_PTIMER_ALARM_NSEC, "NV4 PTIMER - Alarm", NULL, NULL, }, + { NV4_PRAMDAC_VPLL_COEFF, "NV4 PRAMDAC - Pixel Clock Coefficient", NULL, NULL, }, + { NV4_PRAMDAC_NVPLL_COEFF, "NV4 PRAMDAC - GPU Core Clock Coefficient", NULL, NULL, }, + { NV4_PRAMDAC_MPLL_COEFF, "NV4 PRAMDAC - VRAM Clock Coefficient", NULL, NULL, }, + { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value +}; + +#endif \ No newline at end of file diff --git a/src/video/nv/nv4/subsystems/nv4_pramdac.c b/src/video/nv/nv4/subsystems/nv4_pramdac.c index d8d4883ec..8460f6df4 100644 --- a/src/video/nv/nv4/subsystems/nv4_pramdac.c +++ b/src/video/nv/nv4/subsystems/nv4_pramdac.c @@ -14,3 +14,71 @@ * Copyright 2024-2025 starfrost */ +#include +#include +#include +#include +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/mem.h> +#include <86box/pci.h> +#include <86box/rom.h> // DEPENDENT!!! +#include <86box/video.h> +#include <86box/nv/vid_nv.h> +#include <86box/nv/vid_nv4.h> + + +uint64_t nv4_pramdac_get_hz(uint32_t coeff, bool apply_divider) +{ + uint32_t m = coeff & 0xFF; + uint32_t n = (coeff >> 8) & 0xFF; + uint32_t p = (coeff >> 16) & 0x07; + + // Check clock base + uint32_t hz_base = (nv4->straps & (1 << NV4_STRAP_CRYSTAL)) ? 14318180 : 13500000; + uint32_t final_hz = (hz_base * n) / (m << p); + + // Check VCLK divider + + if (apply_divider) + { + if (nv4->pramdac.clk_coeff_select & (1 << NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO)) + final_hz >>= 1; + } + + return final_hz; +} + +void nv4_pramdac_set_vclk() +{ + uint64_t final_hz = nv4_pramdac_get_hz(nv4->pramdac.nvclk, false); + + //TODO: Everything + if (!nv4->nvbase.nv4_vclk_timer) + timer_set_delay_u64(nv4->nvbase.nv4_vclk_timer, final_hz / TIMER_USEC); + +} + +uint32_t nv4_pramdac_read(uint32_t address) +{ + uint32_t ret = 0x00; + + switch (address) + { + case NV4_PRAMDAC_VPLL_COEFF: // Pixel clock + ret = nv4->pramdac.vclk; + break; + case NV4_PRAMDAC_NVPLL_COEFF: // System clock + ret = nv4->pramdac.nvclk; + break; + case NV4_PRAMDAC_MPLL_COEFF: // Memory clock + ret = nv4->pramdac.mclk; + break; + + } +} + +void nv4_pramdac_write(uint32_t address, uint32_t data) +{ + +} From 8254a7f3aa3f1ce5327fe10960b67def005584a1 Mon Sep 17 00:00:00 2001 From: starfrost013 Date: Sun, 21 Sep 2025 20:08:19 +0100 Subject: [PATCH 228/274] set the timer if it exists... --- src/video/nv/nv4/subsystems/nv4_pramdac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/nv/nv4/subsystems/nv4_pramdac.c b/src/video/nv/nv4/subsystems/nv4_pramdac.c index 8460f6df4..0b8292796 100644 --- a/src/video/nv/nv4/subsystems/nv4_pramdac.c +++ b/src/video/nv/nv4/subsystems/nv4_pramdac.c @@ -54,7 +54,7 @@ void nv4_pramdac_set_vclk() uint64_t final_hz = nv4_pramdac_get_hz(nv4->pramdac.nvclk, false); //TODO: Everything - if (!nv4->nvbase.nv4_vclk_timer) + if (nv4->nvbase.nv4_vclk_timer) timer_set_delay_u64(nv4->nvbase.nv4_vclk_timer, final_hz / TIMER_USEC); } From b1e1596e2b4e1f59fd18ca895c6a3ba98019e7e6 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 23 Sep 2025 18:26:32 +0600 Subject: [PATCH 229/274] Remove other remnants of OpenGL ES support (#6213) * Remove other remnants of OpenGL ES support * Force OpenGL surface format if possible --- src/qt/qt_openglrenderer.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/qt/qt_openglrenderer.cpp b/src/qt/qt_openglrenderer.cpp index b74b74202..71f4e724c 100644 --- a/src/qt/qt_openglrenderer.cpp +++ b/src/qt/qt_openglrenderer.cpp @@ -814,10 +814,7 @@ OpenGLRenderer::OpenGLRenderer(QWidget *parent) format.setVersion(3, 2); #endif format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); - - if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) - format.setRenderableType(QSurfaceFormat::OpenGLES); - + format.setRenderableType(QSurfaceFormat::OpenGL); format.setSwapInterval(video_vsync ? 1 : 0); setFormat(format); From 1d9f84b02598fd207e4c6ee77b39789425dbea24 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 23 Sep 2025 12:34:46 -0300 Subject: [PATCH 230/274] Revert "Bump GLSL version of default shaders to 1.50" This reverts commit 999812e9b986229fc5be853419dfdd178b84058b. --- src/qt/qt_openglrenderer.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/qt/qt_openglrenderer.cpp b/src/qt/qt_openglrenderer.cpp index 71f4e724c..7f8a5e2fa 100644 --- a/src/qt/qt_openglrenderer.cpp +++ b/src/qt/qt_openglrenderer.cpp @@ -75,7 +75,11 @@ extern int video_focus_dim; extern int video_refresh_rate; const char* vertex_shader_default_tex_src = +#ifdef __APPLE__ "#version 150\n" +#else + "#version 130\n" +#endif "\n" "in vec4 VertexCoord;\n" "in vec2 TexCoord;\n" @@ -89,7 +93,11 @@ const char* vertex_shader_default_tex_src = "}\n"; const char* fragment_shader_default_tex_src = +#ifdef __APPLE__ "#version 150\n" +#else + "#version 130\n" +#endif "\n" "in vec2 texCoord;\n" "uniform sampler2D Texture;\n" @@ -103,7 +111,11 @@ const char* fragment_shader_default_tex_src = "}\n"; const char* vertex_shader_default_color_src = +#ifdef __APPLE__ "#version 150\n" +#else + "#version 130\n" +#endif "\n" "in vec4 VertexCoord;\n" "in vec4 Color;\n" @@ -117,7 +129,11 @@ const char* vertex_shader_default_color_src = "}\n"; const char* fragment_shader_default_color_src = +#ifdef __APPLE__ "#version 150\n" +#else + "#version 130\n" +#endif "\n" "in vec4 color;\n" "\n" From c2607f8671052b77a5423d1a589d3c44a27327e5 Mon Sep 17 00:00:00 2001 From: RichardG867 Date: Tue, 23 Sep 2025 12:35:04 -0300 Subject: [PATCH 231/274] Remoe FDD samples as they're now part of the romset --- ...-5_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav | Bin 138308 -> 0 bytes ...5_5.25_1.2MB_motor_start_48000_16_1_PCM.wav | Bin 94708 -> 0 bytes ...-5_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav | Bin 122478 -> 0 bytes ...acks_285ms_5ms_per_track_48000_16_1_PCM.wav | Bin 27378 -> 0 bytes ...acks_285ms_5ms_per_track_48000_16_1_PCM.wav | Bin 27378 -> 0 bytes ...-5_5.25_1.2MB_track_step_48000_16_1_PCM.wav | Bin 4232 -> 0 bytes ...FR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav | Bin 62924 -> 0 bytes ...R_5.25_1.2MB_motor_start_48000_16_1_PCM.wav | Bin 31146 -> 0 bytes ...FR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav | Bin 64278 -> 0 bytes ...FR_5.25_1.2MB_track_step_48000_16_1_PCM.wav | Bin 13188 -> 0 bytes ...ekupdown_80_tracks1100ms_48000_16_1_PCM.wav | Bin 106992 -> 0 bytes ...offseek_80_tracks_1000ms_48000_16_1_PCM.wav | Bin 94412 -> 0 bytes ...umi_seek_80_tracks_380ms_48000_16_1_PCM.wav | Bin 37754 -> 0 bytes ...umi_seek_80_tracks_495ms_48000_16_1_PCM.wav | Bin 47530 -> 0 bytes ...tsumi_spindle_motor_loop_48000_16_1_PCM.wav | Bin 172700 -> 0 bytes ...sumi_spindle_motor_start_48000_16_1_PCM.wav | Bin 44172 -> 0 bytes ...tsumi_spindle_motor_stop_48000_16_1_PCM.wav | Bin 50200 -> 0 bytes samples/mitsumi_track_step_48000_16_1_PCM.wav | Bin 8952 -> 0 bytes samples/readme.txt | 13 ------------- 19 files changed, 13 deletions(-) delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_motor_start_48000_16_1_PCM.wav delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_seekdown_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_seekup_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav delete mode 100644 samples/Panasonic_JU-475-5_5.25_1.2MB_track_step_48000_16_1_PCM.wav delete mode 100644 samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav delete mode 100644 samples/TeacFD-55GFR_5.25_1.2MB_motor_start_48000_16_1_PCM.wav delete mode 100644 samples/TeacFD-55GFR_5.25_1.2MB_motor_stop_48000_16_1_PCM.wav delete mode 100644 samples/TeacFD-55GFR_5.25_1.2MB_track_step_48000_16_1_PCM.wav delete mode 100644 samples/TeacFD_55GFR_5.25_1.2MB_seekupdown_80_tracks1100ms_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_offseek_80_tracks_1000ms_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_seek_80_tracks_380ms_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_seek_80_tracks_495ms_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_spindle_motor_stop_48000_16_1_PCM.wav delete mode 100644 samples/mitsumi_track_step_48000_16_1_PCM.wav delete mode 100644 samples/readme.txt diff --git a/samples/Panasonic_JU-475-5_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav b/samples/Panasonic_JU-475-5_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav deleted file mode 100644 index 90897531e24400e59e937f111f95e4755b6ee7ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138308 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
    rjjS3lel|zj+ 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 z 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�ZbW^1~w47%{oacO9|9!{WU5nBZPjfDwN69N!(4z`wE6AsKU7 z?Z1$AW6!w8xTzO>y`TpVkn~;R)qynfm~8z8HWo8C-w;KNGv6R)Gxvts0VHrXEY^U- z%A^wUc4Z@YF$d^xp654xI69l*7&{r2!0xeDw}_eDC5;8yk6`h+aBg(r_9)|cLnskMLsd-eyHr?N%9;IA+i6+eEue84$SmT zNb*S@s2lur!m}-LZtSuC#2#|Jhsf$-v05{kRci>V z;!}ONsV$DXmP8}Y9`5J?xGYvuzw7ly-#n06L`Od3YBnFacfllPo-SlXPvW3ap0R7y zO&IkS`EF}$6f-DdmT`ZdI`EQD$YZ+u9HVQopY7Z5vod>p0P|aMLd@YjlD$R`4(IoN zq=$VZliGxpDb#(G$y0n{|)(_>y;aA0}p+$~B7+seV!~*dV+-vp|{8R zn!D+@CvLhLJ2lU^vk=)Go-y=(ZC#|TQ8$}_r>5!cJ))uq_3M9Y^zY*LdivjeG&o+R zV;DR>t>J`np{#N6=xa_~Iqov2@Dv*6m=d7dktt=BbSs{u>6jCZ+^&T6vI zI#70|Z%>2xog$RHXzDX^{(`2X-W>bd&g2bZcG@z3iM>l=N5HtK^56;e>%?@Dz3!&Sy>Z%|y75RXt;U`xO86Ft6PQ^F7L^M?`$N zmY++~vDUQ+&lM|g*Lr-aU5ojpPqgtpb%L0+FnLd1G^SG8%^8r>kEvX(1>$i@vWCAI1Kp*FkXXL)cAQ!&+mUE7pOH zRCQjheSfIa6qE~>F@v}~&b-|2R(0^^iV`=_Uj@^^y6E4hNfKQ z9?U;vhCuIpzkK)nefA8$KmWLj%1dTF%v2Tq#cZde^)M_yb_|@$mjA$9MO^1Rm>wNA1!@1k4<^8xB*`?;NG)n<9~$B)VFmF2|V1rxB=v+|6ddfZ2QJHgr= zdL0>JRaReGbXL#`h+lZF#dNSx+!;0h0eTWyO^p3qOa7OOz}je6Q|}LnN?T#6*imQz zYnhG7=8G6Ug_k|_UQk4RBIL(z5toQCPk`+3U3GDJSvZJ&aEr6H*u(!wpJLD7W5g~o za`3zU9xpOD=sYot{$!fHkY+1;#-0?hcWaDc9TJhn?z=HNU=!KL3YMtMe2X*w@F>dv z$6j%fMH~<9bzm{F(BS^qSMF5I7**id6Ye*eSpn^c5!D#+`I;oY5z7^X(PMmnp5H{h zBj$`>$v=cgyoJAr*u5nlifVuC8+5z)W+3kn`}f4E)N!($`+VC=c0R-_d|1|?Qk{4B@$@yujhU-*occ|=F=dWUAPweOO5l)YtBdS5+6{5vhdwCCQn#iZd?)A&eyvpVN z!qIsM->15Kq;)A#+dL%Z-Dke(U*?~FrhgOkI4XaKi6OreO}vkPUle2ADqjeOh`Fq7 zUAco+Hq^?tyiOH)`uS}0ceU1oX6AgabtjnBQ$wvbc8I<&tHsQ6wph2m4jns`ueV%OXkvCa&9@-+T=fVWwTJ4WM{ zC0Hc(&>W42V%#=nF~uy=T`X`kCV7#Tr;DAs=}C<5Jb@P{()&~_`=Y#Oj=w$be^JrA zlaGj9ylz&3h?!N*^z~wqLF~!?GvAgMRzWo<+{+CR1=JG{t z`*X}P)$=Xw9-?iri)-weUI}t5v6pXT7c0nlC?EMmcEx`DLN`~uQ@j;B)eqverqRXc zV!J|qb2y8yBEq_qtRnJ%Onxwo&mE@?&*0z~H;XazlN=SZexrK2#jj^;`Lm*+o1m>2 z&v!x^^RCv>*mf-Wd%lpBr0a0zX~qs><={1VX{g_PAeM|h?snj!GR}E{_&)ZKt_dks zSn@GiR6^eM4X@GIRii>sg;%UalMU$l4Dn#B4vU?9+o>?d9Ozgf5<5kW&SK z?>V{T7~fCOo7j^e_G*j$aY|yDk|NWnz!b2y@O2jdyh#69k7r0L?)DIpx}VINv8af7 zVqWV-(0KuGcTrY5sxR`3QIU=$&|2IS`{c|bmzcZoUFP?`m2>XF+-rEyllb%_T=g^_ zyo$ay5xd45&S!Xwh>m8$+$#|GqKq+i?tKuF`$KAPKB03KkJN&~v;975j>qGbxGzbG zEKaDvXIFR5ivC&!AI9zwQQ6)h;`l>bR%%o1jXs+{xLZ6DInGpg4O@EBr*V4x3VVv` zOk~M_($IFYiP5SeI3`xb#JwJ(0@My?#_IAnWw)Qe=r8>4I!N7&#p3QMPwUk~JXq{p z)`Ha>r%mBc%4+RF(#Z3cfzjnK8ly2$M|nvL29e-R;>+W4K&(SPD*ahidLtYi@cdC- z`baUx8RUIGyZVS79@MrgS$tFm2J^hndXIfGV--Nmb*KR$G4gva{Rihh1rP7a(#z&{ zt3fAxaNHH;9~S?PENcqXKLr1Ew6dsqPM@&i;aYUQ@srrQsCT?R#QI2q*%}>QKEI{o6o+7?H`%jZl>gB<9AD0{_FZPNq_rgBcVM=`evV-v6I;EtCncI zsES71h2bENJ5fuX$3fl5?QS+Wfc|5DkO5gPI0Q?K#|PoF7RbY5CDk{Outr7}I|uHT z@y4!LpToz8;>_3d>I)j&msWs_*ioNXugO!*thM>2vH3vQH28$YQ@G?bu!CC%TP2a@W9l%%_jtrM7xq z2~RtH`c=GB2+x;bUk7p2$t>$2mOMcW8S|0L$V;P&Uy+#8GP3blXSTrg7C}=~!`_DU$RlHqv6p=x^Tsxj*d{jjn;yo!RE}`=+Rju@HMxyv z>|hr)frnu@cIJG>>kKGa2DM-CwO?V6Mc8Au@s*d!@)7^<%?kUGWPM!O5MRXnjTrx& zrWH|5ZjX^`rKhm75+dQM!fb zD(L=rOFJpCOWLG<3JjTqUMjvY$j+Tw?#7Z$oU=wbP@uRq3 z$Z8UZ*lx2k?x(ANeELlVB}VRIht${|IO^hC{q`^46*AZ0mrTEfjEBaEU3s<}W3Ekb zeOrF4j^0O98+Yi5nr*BC`>)o6ZuGE6(d&NpOMcJKIU0AeLsz~ zP-lw~+=_f_HJXfhDgPLM|5rq}K}@;_Q|%K){g*$9cP@vqI^d>Y{HOJIzQ=Ywj*+}7 z+8I%E%vtSEax*_BYOXV&f^YEbMzkOJ z{~S#P_IkqM5U+jxeKZWl8lu>%;49}?0u_O?12WfAWEL64DR`qWxgTQpM{4aij@hEm zdtfR?6QjCvF`s(5N9@numc;Ax7qvtfaZjC+BC5TaT}D5*X4qUwVsTH8&AthJtb_Yl zCluLGc*+CS!M!`^z zXU+VdlmAL3r~<@YFZ+-6*+1*uQE=S~RtICG#k}ZTBXxgj!RKOupTw@8W2sND)C8@3 z8gIqET(PT1)Q9foxBK%0*ZO<~D-A|XVI)S3LMC66M#L>s+3Jft)Npax{W6r6BI#=& z{v6TQ`Q9VDsl^YUWmNABwh$}e&zJplhWFdWIkCU&2y%>ZrytpA>{n1YtD&?|=ZT7V z+;`|K)_NYcxP)Cs#Cfq#wX=BZ6469`_HeCZukxu8PAKMYvDf;4q!0*+8F8_PZ}`2y z@+5x$t*q^FAEH=UJ(pz+)r&XvWgz^<%%5I(IQIDOlWC=wtSDBj$C|0}Vzs&K<6~`( ze1DvC#Ln|CK}^h>_&meikF2U3sl{yW(%N1NhI_Ktm>Com@^_))D|x~vDlCiqZ@SFp zOP=F(UgBxK_d$|xNAgjtK9)uQtsik;o$%GKv%FY2&`lj7Y8`FK^%Pm~zh;B2Hgn)t z`++~FZZgL#lYw>vUnCEnua@$UEVD3OT+d&0($d(^E@m%AMZ7~6=ih=?B3^k=3!Z|A z@c%EvNucBj5pcA3quR+{8F881-$`kC?+I34{w2~}3+u6qW2~yKjT005Jk8P8k(i;K zD_$VKYDo9>%ucEd<(I0#RfLUG(*tsnBkjWVo>eRJ;d!aOh<=j${$%Ax8QfVTi}s%- z?XP&1*eR(BCU{CjI1*xC(%K=cE=GALh}2(YYq3r??(_DrJR@dr#4LyFwX6%CuZ7nO znqM7v@p&iXu2FLGtK{d`s16?Pd5ST@s$Q$4Tlw45S|SJoiqmkL;q6 z|2OBUUKFuS;=O+6>*Br`M?z^omUFe%ydhus){Ln4+1}x1S48IXj&G;YeC(ec^1Vl7 z95HT;vOlP$jc`dF5nQY_kKGNc;F#DOGB z38_|KX%}f{i_8{plg&32y`RbskMn+-_jp{8Mai}FqoD{ne*G9U$B6DRu6kpJgAY`O z7ts8kamG97qN-k%Qg_a!OZeD@);~?QV&uvEEHlL(^l5SaExQCR<%RS4ve>D0 z##*mw%>;J*HfH;cgsZt$d)aF2WF72zHy;~2ZQQ~hZ{`1E^e1+$iFx;Z@l)*LbSWQo z1Ga6(Q@6`v=TXiuNCX`<^4Qz|GgkiueaEL)==@du8utOZj6XY!Zz{^hWAtG^ImPUO zS@>izCRyx#0``iC_&K&8cQT9p0pp%{U17E<9RBYw;(%YH-aaOJIY+f3StIn+^sEkCa8gHe#SfN zdDvliHfE&9?r~Sjb@vlbi;7vCG{FEI;m#&-RkJ(VFCo>~?iPYxAOs ze^nN~#K2>4)@^z&M&_x_Q2#JY&_5ZaWN>ciK^-B)5?tL`2H`Tu?*(&uRKMaRC+ z_8x}s4H*7S$bS_FJ;y4?=tn!hi?g)TpN{a}5*lvGzP+1HVuj>1zbhg-iSeTA^rtBd z^v4;M+2wio^cKBsNh7zzL>JySRvWxZ^08z2uOuB~b6c{$#9ha>+oxqEu8tiC$N5#9 z=^X5pB;Se}V&w4y?VHOkqfW3Vtp($8kDGQ-6npASBfsxx^$p0HgEi;EVyqi`mQ7p< zb#Wh`nAs31>mbp`3a@G`AG;VjK#|gO|ki4FLR%^*zeTjRScEn~k(o`dS6}tr1qqoS7 zTSG%+qEWxQft?oPb@E#Go$EzkzZdPiO((C>&K#_?SKJV@Fwb$Gd+9&!>+>W&_zu6V zRC7Mys&mv!em3KGwkyZ_===2lD)xU2{O!`?@aD_lxi4#el1<0Gc$?~VRGS;JuG)Aj zX6avwspBrMw_x~o{6y4kr@+m-BoVXLzt3uDzu>hPr$2{O;ywzohiPP-b!85*Q(YU_ z=mhHzKzZzX*#qWd6+#b@!z|4AVD|lJ+&)5teme;b@QhjHM~LoYC&jfqZQNh>KM`s< zvWole)E09#q5t-zSc|VdfzK>uy;VdPaUZ5{U_0g&&DPrZ-(Ftzbj%bh!=Hn#9@-N3 za)@~X-L$oizn#FV71e{t+7G(kAs%j-tSrXg4^t`FX{0}9o0p}rm;)DgxVRUl>uFQm zY441zW*qy#=Q7;JZtxYc@Us zU+hA3Ha*7O1rv3{)%gU+FXWS-mbc8emh)qyG)G|5OCf&>DIW!gCz9$I7>aqRU$D}$ z?$hy)*Hg9TyxckVPr5#LoA~2aBQl-bUF0z%F?WhaVjqGdb3gIBQEM6R^=4Ka+47B; z;aXPOhegM{#~9PU3esZ^Qc;!W(j@yg<O-#hzq8lKKmB+Tg4Q*kjC(kKDc!whwR6 z38%$==p9t$jef+;OkI^54#hq8@I^Qa>PptL0kiFbN#}O&SeLbQ^9CMgs zSJcxW;|zK4Wl+^X>=8B5DPqIr*=Waa)?ybo#&n!`>)340`3brI_~z*>Zz_$}qsN%n z7i(!3iWip3LuSg-*75PNk7ZP>n!`$r{WllaT+i}jBrj&e#xC<^yl!!PR1cT3%$WW1 zgN)__^7smhKY{63C2=UNk@4zHEM=@{VLjg;5kqlntaIiI)G|+?mDsSIHQ^0&cX`CV zW7X)boIRyx;m3)3ANhaG`q`|FUx>XT#(7m17WbO`((6c)n;^q^ga>Pn`K#lD!+E&9 zkP_n;|MGhUc)F@MB36jp!ehit!dORn6fTQZ#&P$ANjPwX_+$+^zK@N6fb3`BVzG>C zl)n#V1H)KRj7HwdhFgnS+Wy~bTc5jUJ~|{E(cJ&`4eHL8Vvp|AWD)yi|Nrm=|Enkb z>H0CE8h0y*yN5o_)5U72*a7ZU+&M0*h%E5`SgR8EcHT>(aaX~(19aYq%Be7L2`^e2 z=S0;u>U1d1__>yPa{)kMx!@se~hX%z(J?dbd0&hPU7eJ9P=gG zaBTskbxG&$o85_pB>)4|u*7W@j`|Ev* zI^-sN{XTCx4L{9b$1g+FV=xtW^>WuX^~ zMq=fCegBX94W0@`98k0Tgs;gH^*$8 z3cURV<}sXz^~xEO_}Zv%q1e67jc{{T8n)%uJzZLIe4db4D^c|X!jwpSZ6Ub>jw!w)`Pl36 zc$zvHgI$1SZz7?nOGa+goTM)HdO3O2(5l$`x)nKH2Mw{#FLo&JMK*)rtr2DloV6m& zzW#QfD#Ts>@|fJ>iOhz7z0*NSEa zdd5tzkz&g=9;sEIr$ATiJ~@u(`w4gb%>VsqjA^y#^nmw`7oEHD)OtGx*R5sC~PXN#~RO~II0nC*QDzX^x2ywjQPLk+>B*c zXRArR=O;c>cbv`AUu5&KOGoUGSf71G^}U4AsaSOu_jQ_2PO--JXO{dYEB=mdA`V)? zH^gf5nLO$|xSJ$@jB0cbeR)tnZdJYL>{E03!Sy^sGd+yE9mV+m|DWkKPRnPI^q0;P zE5ml;@84ZBDmUM;j5#zDyG+L(k)!?UCg+d)YcI%Z)BDr9EbwZre+!d-sDHmErQ}HK zWy{BiPgc;^zn&keggvQ;u_u4*7aR9>xPYV%!9hfIu_h_@srXn+UKc&Z>f)#z#+

    q)rgf9o$Eq5Zn7yO!5jX;*+9#TZQ$R!~i>_@+Aa0vYC~a8N)D zS6i-fhK%Kwbol^azF zH`_1un`|t4z5Uq=s{RyZ0d>?x?}CZWWHyd{tklCDMjGP2>X*1i%-*k$kD9B$SJA$v z@EiNYc3@Mj^)0Hev0CR5-f5(E#a&usCjYl0<9|fJA9}2j@4T-+;lHMfG9JcL9b_OU zu;AEb`dy3~gqHjJ)Cp>0RObKDbRTeAj_?1#yEC(* z%#^IiZu&|JQKXd079~ZBY$78i2)$ulpJI zxUTa&_HmrY$f;R6Y^JBukM8-GdnT_j{kE&%w+aQG%EMD;-lspye&BJ99V9pOPWv8d zPb1)WCJf&I9yfX~J(AnOY6JU>j!XC0p{%1tIN%TQzj)6(!sZX7jnS=S@2;*UQoJ2Cf|#tH%l*< zzAOD)T3uRK`c{Xl^tpQ7?8?VW59ut~TkhXJr43F#=7P)+baf8dwq?2WVjg70C!yRu z(di^gDdy zt#mqSh>K74yqQ!^_0llBn>x8I>5lqV9=_}Xx;LY=XZSBKBaIq z1=V%t-?mX9)fT;;gI+HoS(@Ru#$IXc{hZ;Q$Z}{(|6WGYJVTmYN%D-N$6m%mnMn8^ z4f`kkoq0%`I`+=+-e`M#8mFa$$@IeRN;k{x3O~5K_3cG>WIo=mI5vH36Gv^pLik%)5AUb?|&ucKiM$@pi78tju@OMs;#v zTm6N%CmWiwk~imRZHBgQMk$SPZ!df>4vbUh`x5v)>aVwGuV+zpd;FfB;9W(E29S}- z_)I_QOq4srHO}y8R%n&_^znM{3j5uaa4&b{sNEK2Baa?#^x;vQ6f zx3jy+B;KmB>g@1q*#hYlGzUDBhqy|%TGPVYZ$=LvZg;Ua_d(zLvO#NEeWFu4i?Su^ zUBk{QlDSoUDiMJ6;>ygu)F3>Hj-K!sO}h=o<q+xhe#lHV_R{RL9(8gi^7JnSjU z<7IZqIIzDAJ)|RHIw73x=T$hMIc?OF47!vg9*DCikTz4uoiTWRCT;f-ide#im}d{^ zs+|cJ>EiqaOx7-Jzys-%CjM>=cJ*lF{rqf3cE{J;1@_mFU4O9=e`XsdzyCe7o67Xq zP?-!l6^w>}@qAWvI`mW`m+G)Za{iDE+1Ye;I#n*E!{_ilrxzCW7~C|FOi%7drqE|n z@hZ9?^B~re|IdMadPmOj*YiC3u_*ap@tn7c2+1<5PdC;-S+B49#Orj(C$z^Ry8U*a zPF2z%d@%_xKV!9H3QH|YcQ$cUCbBS+Bz_-~3_q$eY@utK% z-+_bF8K&Z(6A6;(z1M@+m)`ktV@>=O8?`&`Onf$0e`PCJ!m551#wMeJ*`Pd$=l&;- z-p=|@_L=r5Zvf0a4_{BPARc6E{e};3;yd)VS%=nevP@k44bI@4)Gd~-IMUoYyv3(0T^YP^a@ z9YWtuW>YStebaSq7(CrU=Z^ILK(MW6cj>g6iN9y#)!-nDnHb1sG~Ek@ zzUT{UnSOH@s!J{S6X;~Q_t%oLJLBJ2A}!!Qv&CcGCBNWice>6MuW{e2#E8!J_q8mX z#_oLrtFRod%Y=qk(8EwyOsDW<-KWcHQ&IVSXo^i!lNx&^6Hhyl3}@SU57HozP1axL z*Bt;yIRkix&idB*z@O!6iU=OU2dL|JJ-TbB(z`6hSv)=**zmtyY3CcN>yNbrS-e05O?pf=c>+!hzW`gY~dTkymo{CrA^Lo0B|3T~h>E{;c zWnZxS6+S=0r62kIf=|DWPA0k6>o{~uVNJXOdvBwF6?Tzq;pA2=Cxz36_&2Ym^VC1M zFO$=1cr=B%YYOZ65fnQMucXh*ccQqn?EWPd)gV|%pQ|?Z*-%Wr3LCttpZlVae_<;V zY|H4S&BgK4w>`6=_hSR>OKN5!NqV~{$Dri7KOLFusa?tVZE4OQ$b#>!H?!8}q0_JY z_om;kq3-nSo9JhH{)BelfZMq!^LtVyy0siTbu)0;#T7HTvKAaPz`51E7X1=hTgI9y z?|kD8R&w$L<3%qlGO1pN=L&eR627gE(vG56Pq)u#?xrlIR7TY$FZOcsmD=%4SFP$( zC)n2xKHC{>r*^OnE3-4(>O7QrD*bjYN>5gJ?9KhHV+Z$7hT*<6(jnk*3`vfn$>Ofy_|2)Q`u4vvx)CToAH!VbJm@0bD>A-aPGl5$rXE#RQ`|NNX1_Z zD^0vL-Rh6Bt1a=vPH<8QUB;VF@04ET>S*$4A-mv5=OSN<%zeV+d8&v@O~qYpSO%>` zyOM8soRuYxnfaJ!7FKrpx2Lvutcb|{yy&a=S?Sx-$Uc+l^dtKG1ts5Y<+t$q&LOWm z0=bKI+JV74M_xk~K{f3xG zV!8L)O>eTSr=MrSe|nRj1WV~~yN^9p0?le@X*s{@efHZL^!N(dl1TDnq*tobuC}Ao z?eSP~i~20i-Nh{qlNEOe?Q$XBk7b(!UDQBY3%4Nvxxp+N^?cZ#@&CzP6M=ZthiAP1_rmN(ZyqJdG?dFrw zMDuku+PU<1xdWV!AL4`~CuGMu=h#m)_E$3QLsT&lysj-YZhC3gbA@!I`;W{>7nbnp zc)U3QZZEL2vv_?C=-~bJa!zgMVdAps3DlLn)lZ~yx^;X>a&2Qrhw{TSZMY2#o=B%$ zi*nLEtrOXj^X@?5cz;_6Cb&F=wvi`<@TE@(osSC-18#ib}8S!Jbc( zbg2nS_q;vW0P*Rfe}=Ld(-Gz)-u{1l{8YdE>yh8;y;Nx^uypYb#dGY^HOKJiRrk(zWYL20i)5Wd}PQTH9)1zp1p#e(h@f>)$ z%C3gf!LOi+SDjwJ=jX?En4Zs>Y5t2d?2klxe&jK3?rd=*yf<FHig+-sL27wu3{yYwl!i_MY#gmYLQ?|{oZd-?*z*3&{;l8$@gg2X?r!i&A| z>3mVEHRVp=7q)RuI=D2k^keDQ(&EyGrESU|P`+0ALp=5?e_;7W3j8P={EX#Px&67@)G)^yRDrgJVfuFCqBCG#-(Czi&WapmS6R*liX?Y z3_mN&)bpKTpcr*9#$TsxzLbBr` zy5ItQdk&jr0Lhc?2^W%7nHHHI8`t5n37+r3Pr8gRfw?dJ{RJ$(#(SB}gBw}!_6?}F z6Mop6RIf-oWHMypuZz%IdeO}Fa~>VGh_oC*x+Ir#rpJ4Qr=0G5?~=x;j?7tIB@!lH zNqnkWxGnt|lWDXqnr;v4hoGxurRVG={gL*?k8QoWGuc;#e2=#l?Y=XezY~7k-QM@6 zUn;v=1N!AOJHLg*y&4AZ_v}Er)q&Nvw9%2SQJV}1PiIckK81I15I)VThx@NNd7G-M z8~nVzknmIRZ#vW_AM#$(qMP3jq59!yHQmggCcEDRu{S|+4NhJmvu774y5*h!S17Ia z{Mjq(iuab{>$SLfB`$v-uU8gj_}uT0VBtMJ@&oR5C5dsoou~6;q)cDlVy1K-hbDGF z&pYa5__r*zO7epD^?x1l-BfWNNdo44BHgh1kwS@x&m{GJ@Yhnd#|D1;_TaW98VST} zfMP0ew`J#6Cg&>qL?)D+TtwVNT03rT%CB;QJ)l z7oyUmQB`KbZOVao2<6;`<|3&Q&pVneat`>WXY`+Jn2J2IrTqK&W9vz-L=S&r8O^ZT zPuLu(F5RZURr(qn4sYqRoSu0dXrV5obSeXHAi<*}I>32)3>@hCyNN|aCMIJmlg_^8 zWtQ>p(ocO4+VliekV@Ka^h-D2zuI+k&Tt^!ujczT{JpzRR;MA3MS17AdUDN@pF5d^ zOTJJv-&DAtNE>F}-_58kXK`nfS{L}<1=g6#`&9i__e>_t;RURdo03il=|z=}K!?G` z9v<=5V||@uhdE8}j^pk_XX#?}l>5KnuhC@fP(AT}<*{9pY1FV`r z=|u0PlfVHW9jmS$emTI;M2T91QBH9mV+l+l@l!Xu1h3#{CYgWG8qXe6c-7|=bl;LCdpbCzPjP*CSVHDa;*lkX=^}pN!D##he{~Y^?Z>)H zzr1+Dsf5|de$Vyo$hgbkBiTvG1Q`H#FS>U+CuYjqpXIidi?c>8+8XxeXZZCsT5Bl! zjUC;Zj7eAbS$w9Rtc&E@w#QY;LCD!-@)efJIZ0=+nm*maO4A+WTo6gmt4w406W_)& zdXA?so3tM3+b2m8jVw%TGBrNwxtD66Smo)4nk<$z@|;40740W8&0E7~=6mF1 zB{_AYNbOe(>v17@y}Qhj4bFu2R)1fOuKB|$_BVW{Y4$vV9J~Ymx)eUZ!LGQ0EDm3+ z77a`mz%q7zJc?8`r89b_xUXlo&BlWtSn~_?Z6>rN1~bDtH|VMKw~EY@t>%32y~-W> zSy4Ln^`%+5!DTv(g&vQ^wTI!fn!K0LcRdu^6@8zM%FgsiY@{LRWg6~gKC?5gBc5Wq zg8%AX={>m^WKxm8(nO%tmUe>IJM3$G5yO2BM}A1RzQPX8#OA56+zZ|>fRzp?IvoiWQ_zDfPUy z$d~fqwL87D1BsCu{d5l74Arb-hi&F=o3Z=K;lC>QZX?ZnP@$JHQLRo9mrA}&7nIo> zZ9ZxB$x%vH@Cv#iyz-O3zo8GFN3Szzpqs$>@0_1oy=REk+|AO6E*R$Tq4daY{QKm-KZzET)sedHS?r2Tf4$j%v(f25vLU_c z-$ug=*%v$THWG(Um;c}WOlPl};JTj`bwTxsiDlkvGrW`R?(SkU_t;tTHdCECj2yd% zyz1xkm%vM+YUiNpU3{ay-K5rXGg#Ud1?+)t(&?bfdOnh)#_}RtZTamKM(bh`)*d6W;M!(I;h@8P@ zCfLhl`nT-u<>XW4 ziX;~)wWx=)e-aaDjk<3kX)}|25{r4B-^sgvo$O8D{1G@Txjwbg$PXk-Do9^t5!_*o znPZ)(^SSKG#M&AY0b8b?%$Og-4MDiJ$pqiUm{8tyTj&x&5kB&^!+Je;hLH24q zp>%PJ?@^6j*woQG)g{$H>`*ej78p0Mr_((yv+nfwy&S}ENBQZ6_=sIR0Mb+N@EH7@ znDSV3ayOf;8(K+UqlT_fo~}$kt#q4OKpNcxvY8ceEBL2dV@KXgrc|HnWH0l7)6YG% z;QiqxC#1=S8;_ztAWPEI`D^l|suk`-J~Tp)=Ym&D>+6gzo03o~R z&#Rm4GIJ7>_wXc+c!Atr;2P<}_&W)de41@>Lwd_a8tvzqe$w~f73s= zIm~CWdS(~*CYK*0?J~P~63sLd?LN!G80FQ>#~vykGQy{lUH1rx-BRd^#&FZlb&{L3 zC0a{g`yGligV}V%e3I`an4MYZsZ0cUrZ^{>2v5=0_u#y1SPqG&o{2)vqQ~2ai*&Jr zmPJJ90z8n4x_17r%dR*CgirFBqrp7UqO<)T0H(>UOoy2-K;|dW#ReqIF=*$aVtw7= zu@#&i3#Q2jsD@@Y$O$bczF)ccKb5nM?c_3+$t{I+Odq!?xHhK(>1;m6d0G1AUR&@= zZP-Y6*hsd|Xx>-J=oippPLS>+!;^zEheVtKN7D*DmFW>z;M&gida^wn#S>27=S=%L z6zw$U9b8Q3-Gv`!;ejvF#wMFMGNul0ZH`xyQ`QXzE~8CTdo{?ao~B!;kcr8M|AZg& zy{N=irGHgpZ7yqPcXr3_{;R|zSi!bh!xMOurFttazu2B*k5+UV@Hu{Y$;xjbKfA-j zF`_f&<)M5m8)1u$-|KYsv(w+%e*RllUafCk9#31Jy9pLkm;JgGCkN$c5-3`Icak&b zTt|WYDQLI>iI)y;yV*_7OKQ?2)qHYCJk^*E*qf|6w2&n0*cKmw;FD;3AebEuI)^#K z*@Dl#y$Zj-%MNtDn65X;pYI4_nL0C-o{5LDgxz!iXvOwRwpy}G6Y))Vp&jtmzBp<} zoOP7d)bP1R9!+riX(UWX5N_=M^mf^Yj_wtkjs@pf4xWI)Tv4S z$o(depmR~uovt`QGs=E z%1C&=4-Y4ABb6+%&N_Rf+hRPmuH<0{G?hNl9njZJzTJ-x^91@I##(rY9C(^Nn|S|F z|6S+3OMSW}{yz>ZGyCRiT#yRUiQsd75s|8;#&BnuF6qbecG*l3ltnV)AC~Fzf|}kJ z9@l<^#}O zx~d%sKH=B4VA|AQ$9gSQPV3-)9X?q^CXRvYZ|v?3(EAnl{fT;01yKp*ZNU5S^c&c5 zyz2DL%(U4^>TlgZ$o@>DT=s>1)phv6Io*Kk~go(y!g=L z1&>7c$64K-DC=4@b2}WJi?Xuw26T92W&Hh>XmwL8>Jj#PCLEjw-`nBqigMomR4wtD zY^d3&Vy3$A57m5sCAYJJXyGY%t0#Ns4)Qd4d<($stKvjwb3S{?wKvgzI@+~I*{Otj zkXJdtx-zG40Z;NU^l_cZlY>hSmR=}5XWHA+(!Zr;qJRTRqjbCKphsFg5utZT{PChl z-R->=e7%o{AE8@sBx{n%w7aO~hO({Yo^Gwj(6=c0E%`752Mta2c2+7|`4<}vjk$1kBBFJg0bB0a9QhSr5w zelF|eFfgrSFI%dtJAy3Bxp+I;sV5KQQc~yM;uPW=T(OELlXLFOkFP^E9tEqXk(lY$ zlqgE3-6p%e4{el=Dvz@gpDXbZ$Cr@Crze3tQ3C{Q8 zyt8mX7kZ(MM`KsojMeZVo}KS(?G~DEDtUYd?7oDba~3~?CVYd=Pv4hROQixL(*hG= zI<%0Pjrmo_;gLWvy`3BKv@5elw!y)vp2)na)a+&s{Y{>23Vm~k+Sl+&Inrn!klBZJ z-UHq)=IgX$@jWI6J;}FMx?dT2w1TWjE?ja>(g8UUi=FXHB*Y)?@upM8O_O#@?wzhW z>8rCjnZ7s8e5&0Z4_>FRelH|7$CCZM@zW#l)}Qs!i_Yle(H0G~Kp9mFk0MzviC|_D zceYecG$2H_D0MO+1BkzP;SOZlV2p z_&*bTV?k$f*8uw*;~VLw8OoeRQ+@`@$#p!C#!p||bX>jCy6>dNo}jlz!|C%h+BhfH zb9iKP$gRm9)4Vg-ZjuMoo0Q8e@-ywYBAgr!TFF1!T`Xo7T)V3XL$I_J82*Z`X3_6& z!S^E`nZ@xGZp+HAvf?YOGbdZ=ul$f5KjQuLRR5Ti{}V6$3N}l~u{9*^QmbDBV>|PJ zcSCPicnl_I=b^+I);r0m(zAH$KGL-ZYRu_J&ao5eNS;mNL+OgP8S7~!Kk#4rs`!K6Yy^dSM-vNX3Z`Y8NN6VD)LKT)st5xKjrY!4dmcy>m7nvYq~>2Ft$3_p&a zl*nl!kC_{APNB;JolGd}OdCIqHs;~i20uMKssVp*!Ydrd@TEQPd3$VYBs9UzQ=lA>XR3f2%_!v-9deDQQjO4pxlz;dl8Kw}9DWV0d;QUzX`uw_r93H!1bdfCI>=qGg@ z-9e!<&OQ%~x1pQUA1J3cf02<}sMGnxRRXoo*#uu$$M>$iifu5}s?(kAOYqJNuyj;> z(VCNm{~G<6%)eLtKaJd7h&vy)+f>u079{6^FVNOsTj_6X-BgTc0&i-Nk_nR&q2sJJ zod%kd9_`7*bhOG@Z)Ypcd2dVdu>p$DNq2d?mu}7TiaVz_NxEm>OKyw?^HgfxWd;37 zidVolQ`u5gTHQXYgJxnh&2V`~_Ft;S2I2oPg^!nhR*OjdiRd|5?=xtzsa~0c8lLiZ zKT;%9LXIO>Iu|QG1#QH8I>@@>m%k4N&)Io@k2A>U)Y2bZxmf#A2{7*-6gmwn2gE_*0KW+R^VF zNZYEg|2GMkiRI}*zn<31bdmHs{fRvI9R^~jehJIp!D^!9zj<$+E2nQ-;&|!37wMFo zycPc670eDH3%9e&WXd%Jx6TD0Url13Ct`Fm8rq%Q+29VZS?#B2JM(;#bu!Xlx7*wO zyrCiJW&!JT5qzv=xkW<+mpjt?In~KDkjyvO+~4KA7Hjlte4KeXmF#R6d{Bqov}xx2 zC486ki9DzHKQmhz`d)RP$xO%tVB`cVOXW^#zmH}?hm+GKQ8_lmt&fH{} zxQ#9vLJ!@2*3E+m_43 z!>{b(HLzcZqf;k4*Y8)*>e50=rbF9*h4q|nG38K1^7pN2I;(v0Z}gvOhimXf&gCmx%OP|}Eis^+fn1O8Cl_bOZ#(&& z!)i@N`m5~2*LW7mPk)NNFu=~*p|V3!>(3xKSahc~dw)CL?;4b`SY6pP{hj;hX?1;B z>#}xb^~-9NwUH^`sqDP6uBJoZ=$*&S%$m$ zJshNSF7z4vr=R3HR%fQ9BvxDBDjI@NZO>Fk)`#`T{U+9xo>%p8Mng1mioK?f#Yr?% zZSS_H@v4wynZ~k(bN27i#V79YHad=!onZZ$PLm4KK~{68*!gXR+`pDA?OZ$u(7;{o z?sh9pUTjVQ2Kr=w5XfAJIjG}R+_S)SzeZ=vJ=c+c>4umnYtDU}+eZ!b99hx{-zGZP z3$OO08=mFSOhpr!lr^pJjbC9y+<~hz+oA_5YGL=8|GpV%`W2dd8GWZmSMp8o!Go8O z7uQ;26IlYsiD#|n^?u9#nOimiO$_xMqt4eo!#{S(6wXDW@>AF6I6ucPJPNT8Xb@phU#}TCArn>doB6FL`=h}(=_9;4EtPjyr zF~hAk{vu9zFsay<2RD|inTzMvi7qst-H!sDN5Sb9I(UXC(_M6HIH}7vEQ>4H zVz+?f)gI@f>85se4ys5F#0FAsI%*h9=C);V9gXXfJ(nqxb)6E{6JIz1t?tIMJly{^ ztT&lQhp;7&M9ZoEt?#>Mfcq(^Kbg{l@W!`jU>6!G9@0th-OU;vfz`J~n`Yv%pFIZS zv+=BkoTooSc8_E&Jmj@WEcB~!Uw>;)y?QH@ldPF-K(mr7E~j_P(>AM7bEdvNLo%nL zC$-ys@KXgC1KHKhKE{&=-;iLdMA(!2dk7A{ zh$e35s@LGCQH-%D95o2z!2$cGz3+jelJ1MRz_$8V^8hTa)dbkP{4TI8j&zIfp! zobfDfSb)M3d#M1+?S1n`*t>^Bco1*Jj{eR0QBHzu(l_UjHz%@Hnv;m}|C4o;h}Ll2 zkclR#HcC&Y#EM3vz+P;Nf#5LRvmf664R3$TL-^g#`93qzqbD2aBK)?k$7*sZ+A>pc zGd(963Af?U)6v#tWY48uyUqR{^O?uMZXBK(%}cn4hjSJAFSgNn{#S;HtF0ZMk zDCm}AM{8v>mWgkCEBD}cx?}-g=qJ0&x#XtFedEw`x+eV(pB>18+!^(3#V2^5O_Um! zMMYfV2|p*cTp#{LiBD|JE@M#GL!aV66+$6N%z4` z6*N;&$y+R?bdC9wmd+IWx+vvjR_CE`x04xO$@5;~@hb`a4Y^#NEt+{c@jG*hJsgf+ zfX69?MxHBTlN#j_H1trgXom92aP;d|+R*BbC@R1|5^a7?9CvwiFwVZqO`yGd=}%F-n)vEX z673zD;WD3V;wrze+LFuD9ZnNT{vB1nN21?rw&&QQLh41epMRBY#;*LVY#%Gy2UHKi z_t)`gQm68owf#Y>ri;qqv}818TlAdM%H*7MEp$z0g&$Y=G2JvvB=)&^yMh%Wi%VxXC@i(5Q_K|MiV7SbS%;JsTY{V6%+1FpK7y?mFq5lYWmmv1r0W*pI|M7+m%Nr`?^MZ6mU=8W&`y zZ(SPaUQ%-;i|Ir7Tq#nuzk7ER4UO!%iez~YUIyCh6g>POuX7CSo=9r;vZt!}F`BOo zX8xx2Q*9WoSj6%^0I%%9kJ-h3lk1ZCuW!KO7AQCR+NZP^nb~D#Si%E>%!Jf4&>|Ta~jL+ zG4knbG;=a&)S+eT;{QL(_7*SSooq|Ca%0(Hok+a)e2_l4;yN<$Rvt&DI6lfEdl}Vz zfzyA$X_*q&5Jj}X$MvlveFzg5I3E>UV0AZ;;v+nhOWG6ml9_gqpUG6oEb(pignP_2 z-}mz&@+$q_N0C0mLFIiqIN1Ro7dx2_nxB(4FXOkzKqs?YC)iVZOwT5*$NTFGR^e|l zDcZZ+X{e+dzUx4rByQ2D@Yd4J;Z>h}o@JL@>`U1fXVPj7?KttvD_ngd{rR2GWa?+8 zmL`iPS%zDo>0McgAK3fOtlm#?#cJ{{XE+-?Gi@i6bB;s_`-8$IBxD_sNfyJ&JgrP- zJ_@BI=kQu`F*zHHMcb3VUXlD??3L-HRicRxvb(MU!`(n?7g5OApgJ$r7Q{O9SaH?L5ge4E_;q%^kP{Ihi}px3&49Cyl$G+8y!594nCGNszo|AW1a3y79B;8 zZbl#N@2_KMt+V}3j&}<-NBS-{FQo3#wDDQKbCU13p|v{ES-nx`7_>bFZf1FWieg?w zIf>FtWpBI;W8e8Noy;o1<(}|y1?Z+)Hg)Ciu^fMp#rQp)yUKHsSEiFL_o1K*$m=7? z-EDL~Os~Ijtn74!ILqB{BOlXw=y`X0m%LeAP;4aDUZ^6O3+a2cSjOCrpfTQGnRc03 zYw6$nIBpt)c1Pj+$3QJ!@iN?=$k5?*Msh@x`_bD<5+Q#Mmgcf=J}>vBp9`(|8=B(_ z7~2l@Os6lBhu+8ck~vtN?_I9kht`$uR>{2>>`v40S2A2;-CQG*oIWLu>DKCW*?L~h zOcvZYJy&klaphrF-BbQsKo{qXY#aDWfAC~peE^b~SFs0*J{e7C3d~KsgL6^sRdoDq z{=blZxWT(;d35zo2im@spXc~=Yn;)FMCj@Bsq~30-q~mBkstf+AGQA<_%)xv&0nsV zX%{)aSpb)lT>BZH7zrMa!%2Ma5h5B(@Nv4=r&mF`7G-w(k@Q$&vMybOjuX8)z$=G( zre8-hdpeoayPh`cMCx5+N0}1aijHgNSp!CQq7%zp_cOGZbKpcfs*#vgSxcE75F2q< zuWg_c){|nN!Q6XRcqc!3Br0!@?vvBdfaFfrStI++`C%6^jfZ)xnJ@j1NPGIhXU1yk z#<~}td~!z90V$brXM3hf=LBm$kA%u(m6ra>6s$~$NiUZ}$-95)tYn)kqnp!l?PKdn zc5V7MWNL0`Xc%qT8x^&OhvP}4Ot?FOhHYt`$C6Z|Slg*Qo(>b^VBmF+5Aoq|u9q3n zS>us7WgmB~3+t&Ts843@Nk0Y>$pfkh>hYWp7yW5Q%OB?VwsyA)CR0E0k+q~!GMVr1 z(RP1(e1^v}zw|d*CcCgM!udbp-Ar}A8l@yp;zip@*X;iF;rox9LZWshw4K~#OmyndTh4q z(DGO``xA_$!{Z)Bq;*rK(b;$>9aEBFeykmw0*1A4S|#v36%>yq#k!FrnKPRCQbSPX zb$sMx$>;S8X@Mbrw?N04q;??~+*xSqyZj%|FjM6-YapG%((QgKhz$VCOf{)%)$K^^ zDpp(HqnW*&O+wxb5`##+Ofbrv*hk6X%t)OE)}NEbzj@>&}>uD?G^i$(Tv)Nh%)>1NCD>C^EJ60Mw9-I@VyA0$@^tQJ1t>FR?K9>!6 z61i}z|GVP&(H@_$)hoJpeVlL}NK9ajzb=b?4$Qwqzr}wXU^Ok=?;w`d7hoUxd7qsg z2d7O?`o1vN7|l1}F&siS9RU&txYI#iKNc>}b+gi zj_0eW^LJ6Bk7=;8F)EA`h``s5r_Xt_GQ|` zha}EeT4^wzO~2Rv<~Y;ucns;w){$Ho zTu9%0UGa04(j0fn#E$v+^(_|a`(BCHG|JVVBp-&r+Ut1rXL6x4nR+>`H_`6ir|&AL zbgLo~b|9N%Ij?vnTWPMx6tvu>&_0{G)F(qTJ#^Nghs=We7o~oVIx>;!SGxFTl0CVh zKjGMUc=Rr-zruY7!O!uywVR#Yg~uKuLq1^9?LhWaBY`@g#8eZv^hh3M^41PV8GF<2 z@jR!v!gP}Qn!;Ybi=CV^^9lTjm+Ww%=XiVj+(jig?Dt8~>Ps5DSm?{Yz&4ScJ;>kWd~ZjJZ_4To%<2_q zLOq?Nq!un++Pd+kMzL(37x~J(sDH#hR^F!h7L9dK}+xaMXF0AFu^;dd(D`))4h|UD@ z^GKG4G~3Pacp?t&j;4E(+1*&2W7zk7acL%UEEWS8R-6z#?e_z?`99F;!ka#l2b0sL zmgMo(^v+!N-e2;Ms+vrdDUns3G3;nU)#fT!*5ZnXd}f%tHFTHbar*acoR{q3PE?*A z*3I$oX?(tPG-?amn-z9f0M-Gz6ugB==mF z^)g!Zz`}b!$EvR38DD2#$w=>R&57EzhUL5Hj1%F$9cf;hj%e=oSI z=4U!o)&r$_g^X%V2PL+5z2Ax4&MwY&pQej{k^A$x|K{O|%=S%3#9=7^68AX^OfPV) zz1T2o`O81zsZ5Kxzle+6KpJHR>*?sc2`F?y>ut!_Tj0F|ejn(W?itBO>PA<+fDW!g z*U8#`0qh<^J@JF@Mn9Rnk(p@Y3L7{*9;T6@@6bG%SD(uLE$p(ol{CU*XTrnP^6on0 z&s1}avFed1J>3VVq2+PlHjboz+$)(acs<*_2b{-GYyw^p)r=L+k`YJX^ zVxKwrx|s%;L?)!a_tD_GKfS-13XWygGZ#lrF3uzGWc&2DvrH)N&1$>b-cv)7X`mha ztVVC`v~iiZ&uUXUCeT8!ir-H2oFLQV2QwinvQ*lF+$@~+4V{#1=S!@!5z4$6y@kUM zL+?39s!VUU_39ONo2e69ifQgDPwwJUKb@==m*$p!C~Z-GSA+F-FTb|*b!mC&Q@yXg z((S6PEWg|IRIVc4{Rez!l4WA`hdMJ)f2hvZ(Arx6DDQ$=kMEjz^CzI#*0vYUsj1UJH+E){QHRKKzxx7HgB=T_h;EuCq0LQ;Xsz( zhxja!oi|9GZ&`j@SkHm@Y^{2?{dmL&%Vj;9WL?KDEU8U>nS6ec?J%1a(HrNU0(TWz z^tFrgiHd%{%H~Kf?y0owCuk>C^z-O|VOH@jT786EN-g!KGqX899le_Bs=iiwd4a8q z>D{y8tS@Tr0)stJ=2Kwupzps&ldnLB6`XMHMEa$>d1kfzrDh?VRSjP>AsLTHVW*&| z<~ZhfzcZ;RUBAy@GuH9f$tdU>E3M3;P2Nr-E~&wfAHBriD^d5(Fnf(}G$eVi!+W>l z#euGK8fxg|ZcY8{ffn~>TV-}^P6-?MeEQ*cM<0#+A1gXpiJ4OU6ey=t{YzljhfI7% zbYV2wsmsQ0;rj=mrB3KSoqKM?=jV|QkNWEx{Pci(UrEvoK>z2vZgY6tC?97%f8bHp z>4ju{3%cwdSpZArGWXNf>T#LPSIPVw>0XH>bs>wdW&5W)`*4(fpIx2m{#O*dnLZ<@TXW_LUH~$gXb=fD9;fyvBYUBY zZuqq;=v)ZqgZ+P<9Vdo*nVn`H`p)3Ko-|3n?%!#Z+x?z`MrPoKkm37YqOJ`652GhIREPEN1G}KCqps&Ij9N zFZAD>EgU(M`isNi?;oGrsAqCjS}lFlenw?0aen5i{qD}Y;`!7Po@md<;QiR{7Z&@z z12(^e+i_&wB(i%HK8|;hp3vzswIh3att)5h$k%2BAI>lS&=niG-e0J8d$bylrIVHH zXH}UC5I)++y}#v2BquSE%=<{{>u^HyB(B62{qV-)KTw+k4@s!qyrEZM^ z>iXQdO04Czx}|<@og+x)!EkvMA0xdr_OXIhU=)k-H+Oj!U;Pc1>7Ab$b!%9Ye~AcG zv5O-?Ad|sb!~OGU;u5qmjdc==>-La+k?sRLJWoqc?C4&<@A1m@^z++x{s<{{ zlXv4&JnfxpVKzO`j&;=|!74Sr$!d!XTxAzatTEM*d*Z;<71sra)~9o(s_DQR6Mx}nK0bh>+K8Kn+lh5_$=|RIc(M0cK9@je~;(=Wd4;rt$y}Ch~&T9 zE5lL$5H|M&@STZ^Uo;l`Hr1i&kdfZN$yACQ%Dl|QApRcgE^yy3$d;$zHd8b{ zqc@TlnR%t@o%iJfPz)E1~|d)!cuOvy`U6Tw9oVc?E|}+Pw+M^{3c)FV~@{yOUcQdK(=1unRBO1 z9XN}$J`#40;V0|@>PNWxmZV&Cb!rB8#}hTtcjk{A3!kZ~Z)OddTb^^T^rMQL{SPH% zGI8Qf>G{484S!EIE&_w}-TT4sKxYA2oeuA*A4vttZ~V@Z`>gb>1MMPN-}TYX!LFXO zlY>Z-W5~k4eQJBF`Hd#{9gMyJwN)#SkW>#WO zA?kuePByE6R&{>e)>c^sFI1H8I*p|q-n@%kx)ar&OhR=je5Q_mXVPOj4NoHh){~&A zB;KLW94CWvb&@3Ax3}iGFGmZ1_?cd2IrVC4&1YF_Jf_$1=4(8Fr6S&6l-sKG72Lnb zqfKVY6F5AR5-!Do9a#fiz1EjDy3X%RQy5fOW~nf20ZQrQRRhnhg|~0e@B?W0E>WvY zGR?W@K_WS|MF~$}A791u$;|eC`0x^+9L`(l@72qE<03nJ1kc=rV?V_!d6hvF0z@y>jR+9X9m^lkx!g(Fw`e1*zV0AU~{&7~@0d6|`{Jow-Nu($2?`6Aw z9c^b8(=4{>G+tiv`tK*9uJerxT_^dU$>ytqE|TGw$=I3dJj1$Xk`@ormzgnsJ})QJ z*^*b2X?Q2_drsl^?ke_ig8gR4Exmt55set;`}15Uo#xgR8QNR33TmT}#EIj*t%s}h zubXGjKim1|Aeu_eN~k4MX^ykLZtSD3_o$* zdT8?kpMQiEn4UU|^qE+p9&&dX5C&@83)R<%}g3O6EzYW9MA-nRB2CVo@)F%wX12GF_7OatjJ?iM}sH zAt#G(*2V?3JP#M?Daj7l!W6>4oi6@U_BC(tZ;^pzy7aGQId3RS2ke?)SO+y7!#_LQ z{)W2KI1(Z8wl78FYk*edQ@TH1N=jX7kLQs-nXYg%9ep)SW*%WWeZQeXb~(T6r*i3* zQ_gwgTKM0*vfrKF6_WmvtNMX&tm_VVEK?D zinZ;=KV&3*r*HFXYAc>+{XfRfohs{Lxz|^ynl0y(O;AZokVuvPC{P+oJC1|J@vfh| zg7hu>z0g-Xk-(RU_Y6X-sl5KR+I!b_V~$efE?>V>TfZ zD$v%usr1;QsFrydtez?8Kl2M-EWF@QerRK~-KC4hD9=am!AO?Ot1SADal~}?#2oM* z&RU*=kLP&2>9r5(*qpCMv%X71tffts;+%B(-vQrm!Hd|(Uzs7a1MRt%H2AKF%lu6X zzh79S&-(c~t{h|kuj7a}@M^HR!gbQQa{5%h>74Lt5wo5!ejoqi ze)_8q`iZCbP{9Y8N;4fzP4bC{J)ic88+P{{e(LNerY@LB=tvGUUSw-rwAW=H87dpiqytaZNaYQgR!kq&_D3}ulpuf{9zu`Osl&Y_oX{Sdbr(4 z%H)*y3X-)yTFPX}{xCHQ{bb6?hwQTSze&||^x=-|&rB81O!1${iFDVWiJ#}ONgkxt zpW*or_1*ACY7?Ka`=5Q{Ikb?Pj%2y5bIo^r;vo{DKe#_^J$I9yy}@jtb!LXfa?zv) ztjF|T>p|P~WQ}wt-MY|)4N3Pz$*Q8)A6#!an)?Dwr@PN9V4r!w>Cc^PmWl3??tD+8 zywv7Q^L*TEPgvP|u#w!9r6@d=!Rt33R`_G-Mv<)SQ{6k~0yRDAf%XA3)GqG$2Yy%! zI!oL;orE)cB(OD)U>RE#|^l3PwU9czf4a}gy>S%OAi!tC&@XG6?#7{ z&;pbiku-;(tZiAAORVo}uzA}v+!~+$WmnET?RQC(x4a+ynpvf{I*X{qg4w3XB-ub? z?I7kC4OWdtyMou;p9Gu+OYeZ;^RDtO7^bgs=7Lv%lkoC>Ru~`mR8*ag>f?A3>Bcpl zwz<_BZ?XDg>~|~rW`R4WqUko;;sl!Scu;EWud8X(E8TAvSicC;{}lV*ooxJtkCcgS z&yst|FYkqGTe6QY^u6>|OmDg~?BgPutD|f5M3b3+GS+p*kVr3~i$LN7YtJcBs*Ea; zc0clcbDq9s;h&_oC>BXow3beN=aTDf?CWB(`g9zdYOTxB<{9|*WH3Lf*w3!MQL)^| zyp-fUr&q=|1wDV|x+`%|x=?3k;;&$~6lOjE#q{sZ9Es*2oP3=lX`Ex&Rt>#&1+Kgj zMZJVtKQ4U!^iSA{g;y2b9E@64$~`(1uczzNdeSs`3*XpDGUc|%EeDaEnRIm&JF7Bk z*b#;*m5Y3S7c^2yknT+B%+wk5x)t%Cfnq`v3b~PKa(mfzJG^%{>wk!Il}xst#y0Be z|IuELyl#qytB|DIvm*Wm$q&)UY}{};Zo9vZ`rugPzIS9Q_P z&P>Y57yY06p9+h&TUYq@15~>j)ILCK$(vn<*Eit0)Ss*XgT&J+vWd2~!gM02gFiRI zbUkRL4(1<_`xC@67hw(gl}Q?bMe1&v@JEy9QWdrD z;M)~=k2{GDeu-C-X^=ebdtu~$KhtL?eeSOH|HJfK`hKL|`2&=csQXeFPcC21d_(n_ z6}M9TLOMib;&Ugq_+BmeZvx+ftnr@DY_6B9YNEtbcL63c30UQ$qz`E z$;!^ver7NKWm3!@MI^cs2)6^Dzvwnh6sJidm-Sc@OZ=ZI z*^}7gsS8Z^&(-eq6I%TaucklFBX%*x^8vbXD#;pM+r!Vce8mPhJd(A#4DwyBr7jGPSL&mI~I2kAm^qdnYe^{HrY0r$;mlt_*AqFDul@7X~z3Lc@=o?{*KF7S~m zo`H1z5U_m!Wju&BrjyRe&z}nOnfH}!*;I4B?+Q!tO0@r1{!1tRe@WVk?y(gsC4JG; z2lGF7TvM^ZoXjda5b@OXr=Zz#_5Lx9P1|yvf@C1+~YLja9eKiZc^q3%r)zK&b&v=aNK9 z>cUi_8K+p+aaI&LQyZVBllJ!B-{?w9(B4wK^RD~MwVHI$O4Z0T)O;VVi*5FZzfxQM zj882m-TonU6Xi*7g=8LG074g#Z1Buph zeGz??evSY0yB_XM_EP5Kb|SsH@_Z)Q$xJv+WpXrgRgZ(&4ymSYMBCgB=2y#K7zG2f z{9qVp@ZL`#*IqHDxWF$UojzJi5#o(Z^0;V)?a4 z0g0|=YG!Im$Aj`|G*5H#rh@a{ZTRr1SKD&qw`H4)N&G0=xoVNe(Z*T+B{1hdrnNK2~J8E%XE^v)2iIee54V4d?<=t4b|uFuh*D-UPo^nk&S-iJr1EVY z4U#zv|M_h4!M-9hlC3`Yzz#bPmi8@9eK)Ehnj^yVlqksqa_lC_Y3Nrr zQjYUjHr!kpO^Zdfwx-9bvw_aR^%scgW~NG}R4gUe)}hEH_&6D;_2DCQ{{;S=K{l_NFuH#dhCYIyRjmKu z^h9`!SA8D3I@fw{r1Nig*G#j$&$|17bf)NCjOW_>^#AZlW`LxAFjLebhgx~S}TVWDT*u+%$RcYp92#0E=Wm~Yfct@ZO8 z^zl!*JxYJe)L16s@Fl64KBTWY7kEq6*^{1+;HJC4pq>3(Lk_PcBPz0b-bKaz*|(kH zeHYc>n{E6`mcS2kpJvE|pNd9iviH9&`%Mm1I-YgJS5rvZoQsqLxAecrxk+YlWpYJF zaLEk!^oURO`Fs>Q-im%w-I0F%+sf0bUpl+gr!=TEx-?l#aC&KsnQyan!GEPRqI5&) z#?sYAr{{{LPep(F!&L9Ws&7b3Y{?hMbdb03{V=@V3D0Me?9r_B>ejc_##L&1NApX5 zmK`%+pRTV}a?O*!vrMdKZ&@?Ppri-s!B;&0A=6I-kxS6>8>H}3&|ELevW_Wpr#da{ zSn5@}wsdpp_Tt&cf47$|)w8XZjI*st_OC%X{d#Vsu};AA+j}ghV`lUBUf=~iXoY9X zz35<_=@ywf-_&5${Xc8*ME+qS#qB{gbw-_N(xL3LQM`*x(28##ZMCWM{YpN-L8vdW zy+$<9X(V-e0iFv!_pk_4N1ZH`QQ-aGN-LRFEF_kU$YJR(e#68mWQk@{V^u7@8;35)2#1fvhYdv@;vXP zZ)Ykk2eB+}$G82RHoWDXx5(YX_G+uVVs<7g7k2T$9NEa+X27)ZJnw1ddXVSpCS2vU-F@=u*r79 zY3b&e`n!1k^{n6uQl~xbb8UgS9{xKO^`>s)e7~b7Tj1$huDzNS@ghz95Lwfuuy@*% zJ%`hDjp)6qe1Mv~#pbM}9;8#U5fay(=KhIFBuipXcdzde|F)%jCjOV)+4L*V+3Ml` zYQWCi+UkyDdFEUxJz9>p*6iUE=~!Cf!t@P^-DE6B|g|k^uR(n6O-XR zvs1c}F7YLgahFVQO3dkB@@ZFh$sDaj%o06bz*ZOqTLZ|~n+p7;JLMhhzTPmNX~@0c z`dR@T9gd0_mC6(;LEvl}j_KceH0BD?=*RR{QHCIxJO`^>Oj$*0OR zr_7*F#6I!MMZAF*tgVl=br$cc;?%JM@AwlE>pQWO-(eoJ5h{w+o%8S&B2FcA zwIknkN3@X&-CglzWjmbsw!$o?^eZ`tgobhj#T5@7GO?!+_-5q6*M8Oa6b7Q-F;OBER(nBoVCu!)R z-b+PYX5?i;L}vS*f+J2sS2akwqYLUtU#2hY^A+5lDTt}{uFNV}!va{Qs^mMToL@Kt zS|A!#g%7o#v;R}c^CskaGShmZpnF-BPoRs`!lmLXT$qk{Z-Mq~|9$QMH^DEbt*U-^~pJ>SYcB%vpP_&gNf zhc?bU$EWRWy4}1?0)B&UDuGiXGX$abYxSxjlY+b zNT(HL73FW&WZ|7adUh4PxS5O^fupBeb83737N4zC*aAnAO(&q^WCtV;c%~IJ@Y=~% zejI7u2$aj%x5@IH3}&B#>s0b0)=nk`WCCe2)FxYRq6|;L{A;i@zp(xiO-)bCiSC|x zQg@N|>HTpTTkljpasxk)vDSLLt@KVgxTqPcP5w6W$rIRu>4JIzIeWD|+yU;Hdz#qM zyJ#W(&vHtU9(tSeG|G`gsi;q9u<~l8-$jp0;QsBxQp;?y_kC+UPkduxvu_ZS`3E$X zlU^%GfaH~B;zTOBUxW3J>?!fz#W3+>;rD*)zvQT`1CcG9w`3;Cb~>%D<`JguZ=LIZ zU+_q}nmkN%Wt!h*aM-Ny#_!;ZUPlUyW);5bb92#Qs;*XeNg#MqvQ*+4sapb}SY{)_Ugb_6D5GxpoM>4Z^ zgvWh8c~_xl$IweX@ncVR><3YbhPS1#?)|TdZG-T3eK_i&{^P19`L&|2PT#cmn%O^tfUv}80Yt+>OwMQ?Gso? zcf@mH?MamVEj#rbIpdz^F1O*M7EYl~BSGrHed;J{ka;!H%Dz02$|}`1 zqL--jr$%}RxzmFTZHGo< zLDZv_od9h;BR5Yw6-!k9JRX z@CI7>JJLNpP~UgwS73J(t0dD!df8P+_&>48KiJ!fSJE4+J$sT?e13Qqk zYe4Q@M9*ZFQ%;DVByktgz~9mFIp5odEqII-+zkHl;*aF{{KHyZ#Tr=1CQQy(vS})^ zloA2Vtm->G?tzt5x%4HE$NTF(lH_vlUEm6bqT7GyjIkhosVG<-I&2xO_KhgxcV)Yi z2q)2G$I=3W>98p*m2`DZR!`2>s<~Sx&wb(wn-_EV)_8w0nD2tB>!YswNYm%VIhX1J zvQ=pXS^7Fo`ktJB7 zj{mZ(33-%$sj2NwZY3hqPmK7iVL(ze6NvhJv&3z|4x*1{QLj_H8ageCn~*`Cg8 zDr{VivXAuI3F4-w`2H=fcNq=yslCOfPh~)Qmz_Y$XJYvYWJO1OdkfCI5za3pX_IrD zs({Q_>WGie_Z)@_2H1D%G9M#{JApzfK$2OR`jHYWeui`A(Zo5690!Y;xwkEkp%$;Q zt1Bmy>jE@1h~3o08q)nMnR6%Oh5xZ@Hp2_4LCBfZd&Psg^>AoIDyC~xCN@R_T*AVgNuOOzqov9>r)8~3?J`_i<~whb z0-4p2IKj(o$YJ36GH#9xf57zz!s-Z;?|jzQdAP0(O3nnBBS?v+;^3Wp;&C*cY>-UL z%*2a%RyGw@GRyW;ucj+?rcxb3x*iFaHxxLjhepqXqf4x(2kD&2sQ1JANZ3nv;8a8R zF6{S{!J{F&r~>(%t`{GA#*a=%`g!<#8h-q>;PrGh-bP$tAv<#g4`nUfPDds0vr=9s z)92e|>ZzXh`&U%+J$o+G%1=R$nJC+orJJc+#}*!K4f=i?bP_my48oaoJJxQ~S2f)L zN89Cx-rW`cGI1aoS+#k=$redo#)*E`u;bIbx&bGqroPN4(-me%ujF**RG(@FBEwPV z+4%k;@@_E94MQ~#k%CX4o%uY3&t*iH>AqJI-9DV2|Acg!MpljY>}Bm|xW`7eNO}3X zuao5ICeefcchJUv%j)CON;viac4>7QY9A7OAHKnEVj8bG-H%y@R?lNutat;#Mq-A{FK~XW&i({+svfs ztx7+`%@=I0H}H5LSnPs^4&_PzV@L0!w9x4Y((yrBX)v2+Axc_7=dU3Dw-&u>!m8Sr z51fu0k=W%>`9HMj%b@cHeK8QXzlGX{puWTpZnwYGnSDTWB{MbAw#(5#FFNXenm-e3 z(&1sMRVKRshm8O9Z2XSR@H4A%4NdZty^O|X_Y_j7CAv&xa-*yMhI3vf57M+g@)3;#LgYeGh}p=;)lFMojOAuwMP zG&6f`FnYKVL|?$&gGu=Jz%-qL?jxbQkO&<;Z}amwk|LA5hgfN9ta5tupjVQ^o%)x{ z(NkTIT|`+|6>+ln$gb36jKu+&7&(R<8w&H|i@gjf_R||ZjE0p==Xesm%=DY-z9C;GTU^%fVvDnMwRBj3xW_Z7Yj)F%FfbvqIvKXTjMuaMz8!lF02Wo=WB| z>;)UOtttJ&Zr~?ek8UQqcX#W!9wpsG3tw(MIo0ZD4`+FHDs*&m#xjL6k?BO8cV)q) z%I-T_)z(#_<>${T%B~1^G=@zsb%Y2AVxQx}x#( z$>3A$c@OZajNiAhy7WQM8Ng2DeGR|!|4OpvQPguEtLIvFTMHg>Ix45esS6nPC6|7o z&kl0E%Xt$ost(;yu98!chUO`CapGIs-^cRCDwooc_HX<)h>RWro~d0(7SlS^mRzx% z&dz|PrgUpMYh28Bx=8e{A!~6T8IC)$E*p~9=aYKZpyJ!y{bm@By`K7lIj(TA@8@Lo zF1Fkw;5E_j`|0lRaZZk^kZXscr<3W|WVg2S>`cG56GOPrdK&P14kK@NEMl$)!fkqO zT``KwLay=a}GuieN7%tI})#=RYzTX<(w6@~p`t?SO zt?hLE;*d8F4z@>i>R6aHQRN|MWFNTQg)(*3-{Fd1@bGNBlo)ts;Z>&^>2{5^Kr&AF#8z( z%@m-H@LwN+LC@iW(t0xs&;(q*cf8inbrm50VBb|kNqtij?Gu^0OMdi=f-RrKC>BX6p zn`sNF!oC&dCbx0`4LQNiBb8rc1-#(evq+N9d}{(|KH}$C*Gspcq1KeMtYm<1PPSfI zoa4MsD}OJ8sFsc*9ZEf%Tb^!8UK9C5yUP~&0L4yci(iV0PA3oQ;QkHrtyZ~4CdB2$ z7*xo`$qCOeY304kr#_4w80xEql)X%Lvp2_2aB=jA+huKeeW%= zeTzyn>!cFT=Xa~!jxAgfr|jy^E%89QKqba~5xN|QE=SmFf70+qnldxwZ-U(pKHGxS zx)P4B@!5OD!#`kE?o!&hw7y&oeM!5N+JjdOd4@ZeR`Yxp@Lzh8r!8^S<~*fIWKbqi z#{#R1wrlW)R-wQuA|BuIfWKBfx1|`@3Q>>0ob)EbTw-UQS4g#HzWt~j_OZLq&{t0I zQURSEqz6jsrdyF4m(qrVa9c07!DL)M)_%sbAfGOL^F+%Rv0=XT-aOR&w`aN;y(*s} zlWb-<_nnCLhuiT)(6|=_qRp-(nUb^71V);o@q=iN)Y~LyZ(Ew`Gju(}&rfNdaqyej zBLm>6oqZ;+>3AN`|D)+Fz^$yBhJ7~8fkSt9ry$)30wRr+D6OCf5&|Nnhyns4^(rVK z(kKWB(%qfX-Q9i8f8Wpk{%c>mpIB>Z&CHs33+>ZRp-JFst)KG9X1k>{U$1Ra(gOO_X zryjxl)zc;*TYrUy*J3>`1Tw}(4MhstrOeu^Ru3}DX)v1h6yUH2Zn#OVZw}}`Bk^;E z(aj6;Ej>E>b+A4kn2ux}$Olbjk5~^^u7f&T;JIoX__A{_%k219uAt?m<^3!>WpVIx z+-2_|4t9eTa$n=)IF3DBg4HS#e_ur?yFYXN9tb*5`x@&9(9-V;g_^U|?$7*`P@PSbh@36SH)w`g1CfKIH3UKdz>4-|d^8hVPB zAma`Y6S)Q5cPEs+7*6;RP8bR0TQx_XG}^-+tk3Y*UV!uW!rOBKj<#2=oq5eDsE0S> zJ^V=T!ISUv+7DSajL&u~_!10%#xK@MvG>vhq~9-Cg9DIfc9r=V>HQtJw`avyeA1`= zIeyiiXb-(Pelo|*fupUpc?7xeloc;M+HrGcR)tyh2(*{3taE#qfz{l0L(%psSjwy( zAg#CY&wO^cXF(HY3id(w>5L3$6s+e}q3P1llu_WlxoQtMpg$|uDk#M|ab3`Z>Hv*y4PnHCkgqd&ABUb{kF}Z+7Hwk3i?>fb7!NLBGBR9=J5s;@hw<0_c|6j zRAZ#}c(QvBgl>cq$GQ=E9x4vONF}gSB|b3T_!pz`p)YsucM-qMdyvqOkbLY(Y1IT zj6eDtqFC=?2fvICch0DRXUzQa4|q%iYg!VESql=NHRMLlHU{3EpuDBPbPm{Aj}*F% zXYLBCYBtu_)bL#*_$>|=q7mHBhzYy~Rj!9B$3d+N;NHGKrZ$vq9D}v5qp=^BLq+D+ z4S=#HAVyzPst0h}$Iu4zK?%=O@%}R3@>--^9YVV4y(3O=) zlA%wPKtob1Hy-R9T(ln8q(rVg#17eyR`s0oW5dt=kZyJ%F2T8#kptbJ^W0E;6!d4N zY9U@v!SR`qdurh3P2}Xf7ErOCuYO3gk!XPf;f24U{Tpz^RwUa>sAeg;%_`Obvlfhe zHa4&uyihTCwKL3z{Pz{VwB`zpk^6CxH}=1B)@y*ucwpKZO*AumUhXnI!kcVG(f#as z&0;+}P5rig?9*L>-p?ZK65#dgfzRs)_+u)ww))O1Y`#=bcR5B;hdXxUy(#y#E|>ka z-(%M9!WCu3c?;@K5A?Fa2Td4HeI6~LgQ?Ka3Le|g*beeOBamlHp^{n9%M@0+v2gnr zPGRPsA!rHc(d9TB-VineP ztl8fKH)~(mOC>q{Y-E*@^K0OXb==J?5&H=q=U3Nk*M&k*i~EV@{gr{6q(HNC)l%&l zuT`vlHJWcfv(9xw4;cxE*cWUbJh7Uq*_k00GQ=2E2XP*&dttfYCd7?Pn`HO6n%nBT2_vm0JCpR*SdWH@xOCRiJ`qxtNBI)*|U z_TM+cSqX-S$@ zu?7yDiA33lbg|#?D7*)wp;2wk@0iB?YUmW=rP_Rygdsf5X`fw!G12{&` zG9P?3bF^#33b^4K_q>8uQwAAO7HTbueCr0j)L_hIF!H)RvY-lunnG=@ zStTpO3swqx6ws*qb~6L(=_`B`&EWmYtakO-p>bcLC2MahG!83o+GS!fBe9B|T{Ip8 zQLAtj20uN~6#qqA+Q^#qii(Vpcx@XHL0yBDwE$LB$ZuDF;;;n1<94uM=?Fmpgj;uJtD2y>&4!*SD%q@!n zpVl%m?)@IG39!1Y_kWu64#N5FeXrw+%Yzwg4tT)oPx{ZL;Hr$P8ndzUn~_&#nO`9& z#f(U+aM;;Hop>tx&um_2@c9p(-yloBg#zqu_8By5POX(3bE1(X13wv%=hj$_MD|5; z?RapF8mgJ$R{BfDqZIe@j%~QR9m|{W-UK{aflB|Fok@(;v;N;+#;=@R`hPnK>zLgc z;BpYzVMVIL(65n#`;i@|1KrtLEOFt&DDIJxQIueQr5ITS#%fNMl_D!}hkX2#38=lq z`cH;lW#;)2I3x}{cbQ!mBa`i4Xa`&MK5GM%M&cCWdhRus=CuGLvI+B@#I~FiXHZoZnIS zehKsY6}tQZ?f8?xp85z1Dhm}4MGlWan^_39k3pyE{ppd6))Xzw)$H+CJCFed_*RLT zUS%E2faP``=vuGVX!RPwn)?kSosYgS2f4U|J>28y34g-ntDyIvnB5%Kn6b=k44iHD z_t*UT3;0{eEZuir3!RLG)2;GuRnHngsQ}b$4CPofhS97aa$HH|a~izi8Hr8_F^@V} ziRIB7&3LSeF7`7rw>Q#f0^0uH@Q1bCw!<4~z?pTstR`57+2w}Md!i-0&luE@zd#za z1H;Yv)Dr!nJ+pcTDz}TRnfyihY!=ab{M!!M^A*;BKF>wiK+Aai%MAVnSIc=-H`Z^} zADj(9y3|KUDTfu580^?7I6stg68^i+O16rXdjdM6-NozUeQL-mR1ldGi)R)*pmr*Y zLrg<1Z2J7{1Ad6UF%(SxjU3(te_IDW0xUHFW|h!`%;Wi(8MVN!F9(GQ#R#Y|TR^9; zV@mK#KJe>^?6Tg7S8IQEWL9;!PHp~61K-8rc>D3(X6?F$zGcjBW+Y=LRvUYH+#`D- z20uo{P;GXfYT(D{Lf%aW9*wYh-OIm*Zej1buerWHy|mDzb+*nRSGJ(r&%{#e$836F z&o^bgwc2Sl@LL>9zZ$r%faOyk>vk|w)w+}G137$>sM|=Hk5ANayz&k#ACaS|I9tG65|vo#7yAnP=*!Y z^b^eDv#Z$mtm*@pL2ImXdu-UF)=C&@;pIfw;^ttxUu+$c+eoYh$nj-pneH%thM%A| z*1y~khx!gT@c*16R{9qEi0knWoWxT6kNrd|Aru0Z)&>6pjm+LBTLbQmf(lE4b$5ox z20HIy;`hz#ISu!&<7sbV>w=DBPqfKna zcE21E#qK~N=yV{tVEscwLo=yibv?8tG$u49v?H`SvpFa4ZQvWroy|wE0MG#k#a5Ikq&Tr0oJ8dtlgL3 zL8Aq%;$p^4Pqc}#@WC%YbQqszvt}#>u4~W=UqORgIX(i}>He&_kY;cdLq_PcHO|zy z_ZTdKWL()=m{RLiG)Z^$jz(u-T2rJ4_I5`bA9cP zG~A&A>q`+<$TwN*+}k&E-*qzq|Jpb37}t(wbvGixik0CgjT2CUebbF}x4N)Ze(iiy zlM&nJtqWJFiiGHc9?}MHjTB&sy96t&C42JIP0O9^&V&upfpwSCC)`Jx{Gx1&b;m*@U=beRH zZ@{JJ@!rJdcO!i4-5o{jOEzetb)c!Yn@q$mJ}?dL!UX1?rJ>FKCrj}%&tQw&%$G1 zH=2RWeG*)5M#mav{uG(6zcU6uMj=LMFNHF!YB{*3RSEPE+f~(~qo^~>p!t9!7OoI6v7to8%8Zm=>KKDEvJfAU_(@5t;j5#jSIy2gl zJrk?~lRfyH1uE9svkz@`?%6Dm^DWWh8p4n2$5l9^2#~OEQC7}NjZR>{Hfw%`wbq+_ z(kHMBNa)wIE`yb9?Yi+lV4}y|;F`ibUxR)M}w^*!jlp zph*J@@htFl2giyxdT6XXV4bDyND_CJrH{@~btBdzd!`jc1AT{e>HA>qozF2Vnd=Ky z+{8R$SOxO|OS1}c!26|vS!+1UZoNMu(|&Ojl&IzLE>T zNncz~?(%+MjoV|`U3q;b?s*xf_yFq53$JE?g3R4cgM3NK`g9CjyXG!}ju)UQ%nW=w ze?WnAcq|3p>dq68V+*mA4#DN?&?;7fflGX|PqkUkFR7?j4_c@I{_6r=`wUxC?Q@_w z9vxAA(S4SgaG*KYi_sZIBSV((c`m!1lbO$8j#c-!J7EFF8J4zp;PnSc-$cw+O*|8} znX!MFxvT!XDCp@D6m_04EoNlvp_~oS&T2+(hSPl@ZT>(O&apa6VJPxV^pKJ~%JEwX zt{fN5F#}p)Y$UyXNv#5wfRQNu>!S}Gn5Qy$5wQ3Z3i}+Yor?diBl?5YrhB2=nLp|dbt1;3 z1lj}T+k2-SdO#c2(Q;^w6``sO#15pui<2E4@=d6%33|_$*m)D-;2oT&hvhmWNeZT4 zgEcE*zF?%+kULg}zXBB|W4;kUR4d3VU~{&ulwKDO>44m@Zm}Iw>mcc^&2FEYiSUdy zOy{u%@5Q>m1bwpB#IV=fQYwLRc!Msi*1B{V(C+If#zW^N?DRP>-RAL5s zn1^*-1|b{1g|e(wBh0U1wMcVTt+fZWJ#FR%-2M}Ks=Jr&b~Qw&Y7Yfh0~gh~o>f(f z@=OZWrAzCwCxK_JBAFcM*-h5ib#YgSBeFAsbR6X@wKwlE#(j+R5G%Qr;p8JbYlZ7& z*=5e2^5>zyY)CD8!=(cIRtmOGL{&6PBM2ul{=eC8-vaaxvLYV=_Sb?KiVdu1GmsVC zIoADWvtmwhpE<$GI*N6xKeF?4Jo{aP#}C+##(PX=y_L7e0@XQaMcQYRvCsx1Z>^W( zYHSAQRlM&}aCZ)Pp)2DU0o5*L9EX6WIW1PR?ZOC0K`l*y-COWZd-O2tNe)0eo`p3q z9f+@C{8q&^qkjRv_k_lpa?OTtViCAGHhyQln>WDk4&;{J<+Y69FW&#*-AK^ojB+6Q zu+gHmk+E-MPrilL*^F6^30CI6;F+BPMLfcWlD3Ube~UY1L;i(h#d8AXSNH%5@~_=B ztW}_OJ{~!>jL|KIqK}{z?ZxUj0j1mR{SU@G9hv+!y8aK$Y#{VyM4p`k-(^*?I*VDk z@gvrur(FVW3mExstS~z@j{q0_SsU~c8{er{`zL662FK{rvX`wnXVznMPb3*Mpy$I3 zNNcBMgA!bI3&Ta$G%k)-Q5sCA;eM~6FsqD)b6vfHeP3Uqjl4oS+XLD-rc%sPeeH|D zTCtb;MDR8U-24o8Sk-oMpiz$pw^Nv*RbhI9ziwy_p8W=+(RNG1EF<7#>G}d(xRqD^ zud9IjEM)It`1g14_9t|-nDyKGm}^))&T+(IAhVCv(zyH6Hf}N+OlyH6z!u1bk+l%LL^7g=;ySViddKHZ#ID^VrTE z_W^19d@W^-P*NY@u@|`!gE!GVV6SbLj6T5R3#7m>pfU;RU^ln*@R`|AdU*~6n&wgT zV}JVxt@B@K`xhkpAY^$6L^uQ+W2`7IIbO-QkgFQ6_y}K(K#sny9 zEdTX^d#$Ry2tVgN*1soIvM-0uXZ*3zQ}!dadRa$kwjl5`SKNAr>3B_u)V2bsUVwp6 zzzXJRHLC;c1Z;#S&hdRa`qBb8c@(^0rTSuA!@Tmf%xDZ$Jp`FO2reDKD0}m6Ep~J5 zerBHbT>B;%eN*OG7Q4`{;jzgG33q2thnK4e^R#YczksIpA}M1-aY?z4RVUpcwQq4_ zM%V*QvlEb?i|$~j-hUa_QpUQ45iUjFxC008Mz1@`V>`cJ`8r%*krj zQ;}@OuT|j=u_NpkU`^=}j9x#D^%h@X&peAxfdz9TIw98ZOYE4G_^(ppoh!hY&CqFx z-etDSQ1H8umDu{R_uv#Y_p&_RfigNURy!VBEujT8SDdx7G8|o!Z%vRSW)U@;VS478LA=~&N*4CxyxOf)KyRY-ZQ%eF) zcmOxt!e@{G4!#Gc=>xt7b*PUg0OKX$fzt3*P4M4@S91uO!x4oUT^*hU;I>$3HR`|r zz*CF3?gB zUC5M|NR=n_zRerJy&ZJ=CGzeEWc+Ye`uWhxByg_Z{wMhL8?=O>te;;Y8@uzE!Ww72 znVCq)8Nq7jif7Ns-+8qowDA*jkPJTte9{dprb9kG116&wHB&WA|bd%_E(eFmLiS8KPBKk`@V|6Cx?iXV4{=rs0k2fVD98noqzQyw==GY6F zV6{eTbGswC3z>B%rV`qQbv4{gnhvG!z&5xQksM36B%K7yQB$KDHQe$OFOZl=>d-^@ zb{QP}8}n2{vo2B@D6bsSG&iHq&*(G2UwYQl1p9nq>3jIFE2~y3Fd9jWe||W@&Zl{K z)+4g49kzZ)_a4{%yyaqz5+>*T0UwZ*1@eDC`$t z^%JA9)3g;PCPH~T0@|C$zeZ*s;=7b+ouCD*83$R%;^T$C&$?TJQCjt(Dp$8NU^VVo z3GMg|;B8lg22h5+aXVPpLD+1Pfl$t9F!3uA$sY3^kV@J=HMqiith-?zH$$N?b2HyD zF|U6yS~CH^gio8{A2Vk*Hu-Qzhl)Z&}s%`<3qeU z=ZRz3O(nNKh;HZ`{RK4t8*|vicpnnCp9tD1h+W(b-WbmM=lXvYj9bU3I&f)#rZI}W z6MN=OL`Ofydb0_+{FtbW|Il+E;MKXo`y*_QbI9#Y$oxOx+Rj)j)+f=I`~p~P37(r+ z|Ezeh6e(`ZN)x=1&4{+L;%gE7$*GW=>5-E!*;{!`Ev|Fq`8^`$%qk5r(J7&CI|0{$ z2OB`)MrM7--1XJ$fuik*6cbYt8mY%h)E>>!EdTyUf@#cPB5?Z$DKmuEiO_~|bZU%d zR}X+%tO6X~KP-y8{Tfcxe`9W$Rf1l_+je0&j7*yb2mZr~stq)Z`@4J69iFsWit!3d zk)(fd)F?hrg&qb$+xFV}h%@v$)MK{o7!wLFy3bU%R-#Z{ph54p1ZL6wE_})HS7?QgR9%| zG(L7w1|(feW?q1mwiCQvi@P?#O4O%je=jrGYH)mFj;M@H@`SZJ1(G2#zm{aY1v#e@ z$Mj+SwmRQLY=Cz7RKI}+-sMW~@Ti2Y5*tgP2)v$8n0UTRluM}5y_EAJ1YK&7jBkIp!n&c^~MGfhyeX9F4qlmA4Xc zG~)2gdk`#RG!q!rXvWtDUBy0~ zy`jQUyc)&00L{og_uHY1KRHAHt~KP0Mm`qodObx;u_s|Ow2)qKh@JdKvfdgCVo%l? zNVIWS^_>{6o&W5V7_I`8gAr#0*3Vi0mFim`CMxb3Rtg zUUo1x(urX$vTA+wnjpIPDep-V*K?@7n{n~Njbpyl^weC{xAVP5O8Zeoy4d(d005xo_t zT1V|Aw%IOZ$X>LnOTj*y8h?6Lvu0Rz-!ayi=rWJUGI<=43_U^b{|ol|ZovO1p^(++ zIzKY&2FN%go8N^eQp2ISfbbb4)eX4+Ft{{t(R$-QFpJ*6*WKIhXlNfIwWgyD4`Gbq zii5ME*7+P~g^pQBwEwaG*)z_1@pg6G4^3F}*xe1gOy5F}yN{)w7Qc(sW^L7*c#BT( zyLCzQ{cM1u^h#P+&k8h`@o_!noN$k_7T~QfoM0d0-;l!-@JCF;YFmgtG!Fjw4E}8o z-)F=AG*|EyHwle0)jN0nsbHUSm^oXAs-5%&^7SG+>`7CRU*$X>4#>|=3%xVc| zeaF21hh!eZ_f^bz4!Y?)u4Jz~y*}2lj{`R)L%%VW(Tea{p|Yk(op61hYRHjVK-)f` z)qs3m=)4G7Z!lL%x3m@eFr(+(3q3iH?GrOYna?WVV?| zqk!EY;Ql|>vtJqg|A6g2V7rwou4m0tzZ3RHpmJ+jy@VFjB=l#hJLaHX+-v0DF|cUf z&3bsi`X6TIt_5Q|0*PR((kcEu%K5jUmNTpj4;gg?vcs4%_cx-z%`;@2JL+*+i_#&r zBawpowkm^%K0vNDkf;G1&CjuGxm6an!ws9buW`Z?8S6y&U?A(_D5%4l=1QW~(C7_l z&HV{`>1BbVN+L__7L$#q^?g&L)!t`T?it2Lx4F-}ta=lhBdm#2i$_~D%%0$G2;PTr z(1#T%%=fPi4He*gqaxg2v=d}?@MVnT$H?~%*Z_mkR>FAzt&yXxq5s0HPHDJvJo4>M zLrp(1`myLuO<0A>5p$9TUvoaNm=NtL6OkGZiD`9bFCNkSFVIN~p)plJyQ;;EJA=;& zoWGE@P06l*HVq@U?@DV%XtzbZ%N?Pn&(WMdg0}lJ`XP+13jb8&*tE#;C*bcnw6qvK zXEPKr5GfPR?CHzcTuo*|2Ub2?0bgF`KDUt>)=)SNwfqxsocZkfr}VY0fG2r-A&}i{GtcFbK>o0BdWR!xdi7v8L#qyTcA!c5r0P?X2K1 zEgEQU)<*NtvjuB@0Y2yA2&?59S7lG3(#$xVJ+J-M5NtGpuS#!j& zAQdiRQJi8;Ta6vv5v%6|=9rH0or0SdB6E6h-wJ5axq><+3yEqNNCm5=RI=(tWPD?6 z-}>0V9iodwe@ez#TY5wfrJB*-RNJ_OwU8T}e1hDxH^)_Ug8b+Mb_ra-I2NOg9wF0A z4K)$jL|53QzK(rhKYI7`W?`>e>%-19_S$TT%zO{H*WsI$5zL7%3Ln|u(2luwLD&aW zR)OicK*=Z#BgpJL-WsZ?3q_mXUKER`4DS`t#5$l;H{!K3tDRYqzXJ1dSV;R=l@0-4 z@osJYd;xxIa7XJ*bmj3q5d8+Lr5!8L82rrx&>E~euO?9wDzKiidDqpD2qnNmODy!V zaC#G{rwFu`5o)`K>^cdRTXo|Y^uL^O&qk_PP12lKqbq*k)e2Q+{3&~uz(fCH4=Y=z zaI|>`|FEY02DawF?eqA@N^z^;lqJZ?!|;w8s%xTA^F|zIg^Jsm4Hem-q_020-6Th*dOb2&um}Mon#4bLGu>wwWk8RxdPe$7d{_70y zRlr6pO;%JAG_xeE7l){gyp|aGUD%cL(9dq-<2n?b7+F(_ql`o~viC4FVxJoOG*&^6 z4R^8r4%s*Zy;m!2E0nR2z5o3@*07>&#j4qcwR0c)GMb7k8S#^3BnHNczBl>hIXkZA zQ%pkpG|saQxN%3$`Zjv?M?jlnp>chiL!go`0?V=j^pPJan4Vbwr_}Sfihc5m2&FU8 z@zLcnB0+Nn89!fxi&Myu3`pqm(Bm-Zq8+QR5l35r*%`*Rkx~B3DA)1Je6+MlKzu0F zqWAtwxUCJ(N}QXU_5UjVguP&TIWn(1RzgcCHDO@q=EI**6j-H4uWXMDuZ^d&1{Q56 ztV6r4d<5LJC;Bjx&zOaEIff(euA#n)d!J%!*e6*%(Q;t7x+K-BEkS>T4e4 za%5guU!RAZHZy7{GWkPBS&v5rG==8OqaHh;mB9TcNQpMQ8kIeQwZQ&}`ck^_J_!uX zWe#>0D*_*>yZp@PcR-&B>HJp~Y-c2Itu@h4BSIs|V#`Z>axN-opMxr%qWP?3ul+Ro z@Ok7(e>@Fiq4<`_59_PPM&3T+>bu}9YlqGRX2XGG8=%?(i7=9t<`DYD4dlUf_9Wfc zsLh(!fc32x5HTCJZLkvBd$=6FCZiE!h_pBdN7y0ai$KDZWUakNZpc- zPKaB1tix_O9aI8L#VT$6l;&V@7~F1F!p30ls|q}473sc6n!l0a=CjR!6MhF0JCU24 zumFr?vs=e%v|{^8+Z+3D;B*u@FBfd#IQJZv2aiKs$(Y^WxYpND zkQ`0wC~Lq)))+hWtU#jdX6>GhHn0{hy$D?N>R#Y?dtI(U9u0^7K4u0+@72I(nwZ^y zl*9%VB1R}D^qVb+ge!-4tU2_XRJh8S?$Ug?yu0NK1QOr zb67U;RT`D~5S%^;c8}~eX*VHjicP`N83(wMu^v|qG&T7}KVunqumUsF4`(Ikclf4X?=6lRgf90BwtKjr z-%7BwmswwgYBz$bb6|8f*y#ZW+b1#^6nhQpd=VV-IXTZOM^e**~*6_ zpqpNB)H>wbQqHx*v^w#Vm-t!#27|A;fh>b!gE(^}b6>89X?t8&pMp@(N^tN837inGUKng?-H=E8xOE`$F?)}@yM>B$R$_4@vJPLLXC3+ z*`u{!EfI5MK7~(41-3)YfF61Rlg_OEt$4IY0@g%(d5AwCMZ`YnXbr2}G)8D_)D$$L zZH(awqp%-ML*QAL+5Z6V{|GhB3jCk5S&2_05smNHH$5BdxX){xOFpD_VmKl{9>z#C zY3;yuXj*Go8RoE}O~KBvvTjejHkH^l$%kU_`mQG~+|i`oFyHN5ixZxIU>w zP_0@Mh_{A zMtUBOUIk}cgR2=B&A{BNfCqD85(0(&@Xs}1d=E{19~MElPTdoD)Bo)qtW9U{?>hn1 zrvd-p!T%?$vwg8@tQyh`ZK@_K(qLp%Z|JcNp1bx~1y;Bo$Qt!C)Y%=kX#%JSg63b-(zq9l}J@A^-mRcjW<#B12(wX&KaT9TiB0Af=p&6)0x4~$e_MxhEn83Jf<0;)vBzcV^}lHn_kKIB3Qld zGs@yb#2Z6goISJTME&3ApFPN>jT~uh0JQ@%N;)%w(%jRoX7wu9WKrns47e=~v0WQPPY>c-27uhu&?wAHOegxji zvZ6<^n%W`$HgIx}c@^uwHsEw*q!D>@dEdnMwb+aH&s~I+Kf@7r-Ppp>D>yz}soyTV zkI>(5GjcmXz6Qo-D%@i(w|G6rd6&UsX8w)f-`Koo=a~p=pa^3rfTXSu1-ygC(u7YR zLP_1hvl7`T?{AUvpCa2nLw_ECeDA|A=DGF-9|KuG>>Xyko=-WtCg+*K*9h5CldBiR z$5sZqv6_4_q*-<_l@cmeP8n}-fI0rje5XVCMrZYg|Ls#c89k%}{*;B_a}ij*iIjf} z*p>pSJ-Kslo|6JRjAK036}0+M8%Eq62sh-nuNhBIH0$wTU?3y>CXi?!Fwz!*m2X6q z7QH=O^=mF-EwD?>UqDw2!|eMF$m{QU3}bBFu`|powoBq)c(zucm8?gzws*W<#j8jJ zyUjgA9#|nh6Dw3!W?cg)Bm&0Py?-0X6zBOi-`)h$&5-tXfAO^g(C-aJHH8x^0wJRw zt>~2leoBV*X1^A9EGrP3^&y@ZNKJi5-N_tz5p45e);9Z@~#ht!E;~$?E=3 zng2vq?peq$^@$HyUGpNR)1hY+M%&hQsL1!i@Sb%fje#7CL>tbLb_V>K$ESEU`@nDR zwKYe38-$dzN6;o_psjzMsHyxwW&n6HcK1gx_Zy$|;q*h>Xa`?5gVKx;XpgR(i+NQ8 z79X;jbVN4T1@kA?7wh5egLkLEf1Balh3Ljsiys|W(^JtgtQOycYjy>q*0V5D`4Lz= zgf_4i+Osx@6`zc&$;r&z1ue$6{Ad(b6t$btRIqOk*>kKo#yh41cScoy2sQR%l%F!z zzT9H~__e!<9k3=K3%c;l*tL=TYdqI`a7;~pZOBN9G8XY%8vG^%_PNMoc?=GZLzkxl zt0mms{R;PefQD?%t5nF}9Ka;~|LGLvfy#S8>I?KGs}~K1SB!!l$h?QZyT;5_1J3rk zvl`JTR;R9kB~pz!#sia)*a%s$0;{u98Nb>ah}i#oF4DvDh zE9=V&uM9DEu314G&O&^Ulc>cp89(DO9yg+sK_Tx#A>XkwuH#Cvm}5pHQ4#R*Lty)u z?fDC|c#y|_EMfbEWn?XgMTA6RR)uI*ikH}wJAvlUK=FN`mj&r!w)=5LcL!;?65pI1 zP-ZgfLvZbIw7v;Qm6puYPI0x-1}osRY0CTCaLhZbQD3lj*z%(I7q^D%M<;N?Mgx+`+@rT3@y^hEUPbLl>gL3WUDK~Tlzg99Rg`Q_t<`ZPQ zvEAF?OtWjvmpaF2r=kCjhx11wv)spS0zTeDD#(5F8Hanw?orw?rz*732^ux3MoXg% zROAkJE@Y$oFFSzZPUfM1RzG%I;P(ZbT?g6n3E#B4X9jwkabec5o5+2q!P9EH*3peX z)*ofow~^pRUq^6=I#a&dT`{JUS5XP*t=pV>1e~my&>r z^TqIW+>K5Mt-oSl%zg^lfm1E)r4NBtOU@Y^%wz(XH`m<`FOSf|%%n`jvo`!*f;-s( zwlVOVguEHW2)AJ;tic|?j`UfIrLZ5*z)|esIB1qH(Zr5K#HHWTW9-HI9219Xsz*8M z8b|L$4>H>AS14bp)R>1c(sjXkF{DCntjD;>g1dpOw+W2zM=Gr0n_XikLTmHDhINbD zLeH&uZ;za9gA8j6B{YWK+pv$Lm+o7*v>(3>gsZ>jwLg429?G|W>sL^#ePHc|WKP3j z)@e0B_vdngjXKDsR#5M!$d^x{l$Lz{8Oi$ra{3eAzXw-Ck+Sv+Fg~L{(t9r0`vk4w zTcluPB$72Vd-Kn?$fNmz-)9yw+s=iLB9g%0k*r85(bqE)RaYC1C>hGc=j5T2f!=EG zFKdbxfdkcOSE4IgD zZ>=>ld?0dRB@h&04cx-KL(FeSYJ2cKiJP451>mVBmSZYoLP?EpQY%H*n|kT zc34~uu>{K#C0RSbX))?3={GqI&x~PRlIlN3k{fZH70ITYiv^Y=km^T~b{FvmtwUQ| z8fcDol>P_Yk3=UM0B!e#f?GiOeORsXqlYww=kvnv1=s^fflt$F_a%b$yCNR0ig-xO zd2a}$I>HCv;T`%NE#xvhb&*|PYj~GITG_WVFY>wuD|_BRXLA?TTD2q5GW6k|Mh=za zQIJ_SqSDpy(640w{uA02IvP3_x=a_#Nuiygzxj1k=og@n8LY<(y}~Zt0k+>oU&usM z;^6?o9nq|!kWSaJRAy7<>bvLS@no z7+$u%P~a3k*$2b8(BW{$kHC2d)btrx8o_F@m~+NKrF((=ex%Gjti#mE{-Vg56iAxN zXrNj7Y(_>Dm?+7LVBBJDa8nE&$B0Y0P@h~=EEKz_^ko(KiusN}pK8wWYS&fJbnNqC zL|OJgueM`G3hrd)oPC1cL5EC-1suhim=0^w3WC@03%-peR+kwU=30ZecIRNNv?r!9 zy<@PlcOb#cwp)yqXJx;+_yb3w#|%e$cjEmcMs3cl{R&cWr}C_C@1Xxz1g`EVb>q5v zzSlxa`&kXliL_7aa#qCSaK{Gb@*VIuzV&U!VdiH%VuhCix#4hVCuCU;bhRW%zdS@N zC5OgK1<%%S!Z@BIkwz<+O#)zGY<2}CQc|u|5%}3z)?N}<&|;P&A+{k6%mLL_U&-@Q zzzuqyvjKIpVC%s1#$UEaLd`=8&V%#q%XSYvCOOod7TFy)^nh=vL#OcU+tce$)--ou zd-3?3IX4Ga9|39Wlo*AP8LYUco|kcH!^Yy78V%$qito6-h4lDj%d;H)RmQyi>bv%~izxy(aZf~;Er)$K%kj!*X5>zH!P zDi7T5PLow1teR>+(v9HKintGuYxjAaWQB@{5BoKrPJx@NcvZF{2bQvSug2Q=1}QcT z%4-3|wS`}-kx>(t`p&;&=x49A#8?A{+yE7_;SjSl_nStGmsQ z@Gq4D`g(-FVGS~J^f&HmM*cs%Z-*j(hwfcJtc^L1|3?AyNkDuEBQr9#Gxq&^T%kUA zmnIYPNPy2XF8q}NACA!t{5F(0XHWp_=4C*Sc6* zASrlF3050|fdX8mI%l^ArbdGGVl3v@YMJ+hZ{{+l<-BeNvKP_hV~{yl*&|HEYWO0e z0QL5g;|a=&rj!hQ_z)1@2o?Q=R2t3oE5iRd81W_6gI}TTNyu=sbjlGaU>*F#!9GiN zyomYOZ>a=N`#UD2&ap^c4&DztAm!K`hP_cB+kNE{?|Z0dj8$ls6h_ zxCP(%2Iz4syuX~^r$Y4ux%yD!3d(jnlVQCxQ70`GJ%YGm+=_K(h3g#nDd4K%L#lQ z4V)LD#V!QC)3}-y`^Uh4y`l2=;TSs@+AF0r+!Y^;MRDyn7;g&h_bz;8RUNZ{tQXOP zv-*JvGn8u}W$paZ30Y~?9l6N9xn20B3EcE0oMp##?fWjl*;XfP%sqR+b?*l<%(>QM z26gzQBhsTTR}jDQrLj=;cyGcL>;uu9Z~D2L0wHVRy~hZ0K(G0rV7t=Q0ZMN`b*X^y z3$%;#&~O4O1#X2p4+ol$F|!fSi8RuJ`I<}9miIb5%r&nJwOa?F0gx%j zc*-!kH<+1mP)0Qt1fqArK^FA!Q>?fTna^IZY;3_bq8?I1DP~_604Eukc|pcklWTas zHoO^jRT+5^?qJ4UHU7~zS`pY)2Ljf|$in^1`l$}?9fegH8U)->2fFzm5I5#ce$mIQ zm0y-Sq+y)N;VmnbI-cuDRr9r6=Z^sM1AIQtSob1B_n-w>A>6Jp#-|=<^fx%luFU(< z2k!FAPS%sn*g1Bfu^#1F=K2!5z+5R~M2)z$ika~=Zvi{I$;pXh;CTIQV}bSr=*Eg< z|6ep!xJayryyXIpzgb>Yz&Nu)lgZ?g&4YzN%T95!#XIZ!Zy z#SD<sVg|-XKXe0Bv#eB{1JBvY)W2sZ^0?DL!d~^udqw2_C9} z_;H8fANdWO&S0Hg!D_l0_}FD>A1iLS-_mL<7poo4#e(`5ec6gQD_NIU0=*N!<_h?? zlj<|{$pY|qMli2tq?f8W+|&i!bU-R}g3kN!&E1f$&__r9w;yp^DA%aSPXqd`#EN4_ zqD-taDX}zOfPp9APfxG;aL3RJ<1*J+JR{L-J>oHNw?@8uYxXAD#V2b{yK}UVQEz5m ze?s-nZ7IKRVQ!N-(v0|JXgc$NnOWJ!<8I=ZMVxsM$~b`4c7m&);qKPHwF9YjO0-a{ zkK|a(F>?2U?cUIlIk+Y%KR)8Ko{}E$bUz;MD7#Dl37^_>} zgcP~QO5tv!Rbp>~&V2IoJ)%xKJzG>yxv3QMzD-dc@I5*#Wx6nc5W zFS8LIbR&Ju`7MA4=qx}9Z zV~>Q(Q-JeWaIif}^8_}pyZ?4W4)0pI-&_ycF2gVN!MV2J$ME%6aIbx2KZd7=Lj7I9 zd0#lZJJRq&sN#KOe<384wYIV`Yj+@%0X26d?Ix>l>I%?04g})TlV>-xKFD~LTEiL^)zVciF}HHv*_1;yAr#r{SMHoNLwLQ}CX(6|Fr}n5+N)9#7esSGcxR zGTx0*EDD8`CNAMjr4d1by( z47j<@YhK`W84bdo=<1Tz&?o}j;s;m1%s9_8zB^#lS}yKL{RmwfYa71058w+p zVk#72jfn5@8GHk${Ky`mxx=H7qrLfWG<>lP_+N#F?M1Hl$O=pa;eYE0T4hF$q!CJH zVKm~kKeS*xy%E!u8Cf={!Yq|H;0_}SGP6b&t9R@f=z26r+h<`#D|m)BlO#*bR7_za^`{^)0Z4ILx_ zch>h;i#x{g?)bX*qW!B~778S`HM`P)Co`<`gR5e|#cTv4z^rDYZIOhmfka&SGUr61 z-Nt2QJqI<|L(Nau19+T49w_%;GFl_%?Xzr@;12BFxlrd+@M{dD9fKx<(_TC}gU{hW zWGGm7C*Ad65fpArzi|KunfE2GX{2a0^kp?5?;D$SJ~{ZZ4oXV!n1+Y@)`bEIY!5tZ zCFDZu%FVB(`JSGmiXpw8VFx;w7$6skhs}Ii;b-+Db0dr*Ilw5awQv7$HLoK;{W7nY zIp-wrmzbXw$;=`-%?vN`JOk&O5w;SH8ZEz&dHld^|6uOFaJ{Ke@?<2yKRlOnUF(W$ z=gj-uVGCEWF3|?wm3Wu=Z!`ZqJ=mHnEzlhfgLWbKJN38>kvxXm*(q z2zwcoeeIqzKl5YFs)+?Hq=dK4d`tsW%7En(@V^$Fc2+~=rJ3p-;gNQ%kYmumdm|y$ zf$Uw;g!Qp*U}2_XZLvPv4Y0k9dujczW=5;{w1Uw%jx9jqFJ}G+a99I|rTuT&+4&4S ze*yP)NB`-}_a^u^K4k@L!!dK9gT8Re7M@ll-H7BogPc1EwONPiA+&Wfkc{!6XLGr% zifooZBvfn7ncVQP)nDo{*0!tzP0(-ZLd75Q{UbivGg#Z-j)(6B8c=zDX^G5t{ci|t zDgu*o@OS~B74Fq#{Er#NPZ`Gzs6?(_1(eM_n~MA~OJWt@#)E6K6lQ>7Or)Wf>q`@Qa3idnhBgm zV_g}c5+D9_Kis+-xfr>Ynf)GX@LGa3@m;VOj@z(GP7%(q19w&=v2_J514%m$S!Ksa zhHJojDKoR$plkV&fIihEt?qXb4mb&3%tyNq%^u^`Y#1fd1>kaoXV^caru!>6HtOdu z=>1nTfuTUAADC{3cH0A=R$Fx4*3iJ0=mj5x^ZsZG!#TDe5L*)L0a*#(3{@r7LnNBo zT{du?1OBy3Pi?T+9UA=-4Ym(dsy{)iV<79g-CgW2(hLX`4s^l7fj*QOzPG+jYCJEA zxZXXkZ*9(l0k4GnbDFcY7Yv_dUZ(^5<1GAl8je~AXRd*|-MtxsK{(O9b^p9({XX4qL% z%^LsjGpBAmK7uPdGnXcT{Zt9QO9v)nBY&SmnfkeQFv6!utxe$I7INrNux9+ntC_J@ zA+$EmG5*^MXI?;p?%@|ZFmDSyWK!Wm&NG&170(&StvS%8*2O_)y&gI@H`UmIS^TyR znqCH-{=p1)fTv@8-p8jcz{!XtqlGVlL2X;J*dw9UG^`+5nX&m!dK$~{$vrbIB%{!L zCWPl6F{jMP=6Jx!x|LVaiBdrIR!df2GGp6bi0alUfWdtp_J(-MydDvmo1CMpc57u; zb)FhX1+VUIWLkJ67uR{pkuQMECg8C#;LHW+A9CV1;C2F>JD%4$;LutYBYC$r*RQ~3 zQXt)|9KQ%I*bQ#&Uv9TH^?bXD*!#^WSZm^D@y1ndQtRou zj|7VZgO|_}%~v#wz+BrHFsbKO?a_MA=b26Xz`nY}e_H&nS-~EFIqPCRhVtxgdKc;A znz|JlRO^}s1^*qu^ck{SdJK&!-G$dZG=S^eTU~`CY167(kd>q_Z7sej;>iNq83BM9p&Igu8=`BGPu7g&t z15MXVv2XQAZ5sW@QRr6Iv$2XoZmuo0Ct=>^c~}Mb9wR@>xK0H!+t_GpDI5c4hoL(= z2>-`1c5zqNa_?mibK1se)}xE8=G=3El(sXvaVALfQ~$-$1^8Vtr~Ptp4F)s_O*4Q ztZb!@ZB+}q`P>DAW+*bb;E);c z`E*{bA-M)DE(VIb0vV}IY+sra0sO5KVcp(%h2;X-C11mV|y%f$H!2UL1{s%a^0d(yD z_!qO;i;Q+{HCxg;P%hp=*`@73siC%yy#6l z$h@C$r|ZDWOw;E`mKbIo8*Ii$X2pS$;A0%#sok^-P1FY z!@Fr%b&PvW%DXZ@6Yus5x4KIzR=T_3BE((v%c)VSNv(lePcSoU@h?Yi*=uzq%;ryM=n$9`uQ&Mgj9ENEZlvZ)5!^c!a@Dmk5AR0J$mvp|I>J@B{R)`V z8nhz#GhlrkoZaQKRkQWHs=Hg)S&yR8t^0uGK_GqrOz1J!sz1-GwTP{;{hWI|02X@L zj|0OfA9mll zz%^bni>Ex4$Woy7R1-txFX3Xl@mZPfIV(pD$638WzPm6Tc9p`!Y>5-7lK(`145UXO+xr60{lcXtEbq`*4@Yib=$ebolw`?41BxK8Z50C z<#U*8oCk7xGXG*0oA_)@|8nklf;(G(#0u{Z!G!Cm`cGW=$LjFz7FpRW8x-U2bRixw z%vX!;0keJ#p2JrEb7piu;4kx3t)Q;tzX;X*3GBBqpWV!ABG}vphm7ZYIEQ-&@La;t zW>#u*9^feZpzej|w6phdjU(WGKmTn7W;>uq<)XTc6`uF;pOww6ezrJR&Fmn$7RXuo z`v{{u4HceZj<*=MJG@z-$JAI{`k4wMr}D81R7Y9UC+dJ=I*-|j1Tu9+)X(Ilum&^CEG2osBbO~6jG4+WneD) zSgpfSGSJzppowW)Rz~Nk$Unx*IOni8O*~6X$H?*JX^ydPt6OCo^|7+6P8(}g|6ztl z!M8eq^49FZ)lfsY*Q_1|t6s~i&w$o5q*)ZQBQ5wzi}$NC^Rs%0{h!)1n@{nKx{qwt zCwGm^_G}D3yYXsPjh*GJ$0gTT&-4XUZ@lL@?!BLTx&m6OUA<^tAY~RZ-yeYdYDPRC zdfUm|)XOgLejSdtK7!SW)UA#}r^lGXQE2xc&REU0Ht^|JXmc4?mZMh!tyP@Am)FZs z<5}ir&xrI$W^2T%X_*6LOi5!j*S=`0)*rXRi8&%4^V*kxD)8C|Y*ye{^S!K_q1G20 z{9OlDRx+|DvE53g9&7pN@tMYNO0gyU>u44-Pqk<(tjbTTnBie&CC{m+Stn#Klx39- zJxtR4TCV0CtyHpsIUeC%FTqZ*e~y2Sa)te1%~f8%aU#|hdm&~5`nBM*d{CU7#c>gr;Lpm^D4-3TX-BQqgCHSC5@Y(9SMfk;BRcp1W7iI}o$mD?r9f$Gj zMYT_Zogr>Ph3fb2V_pPCC-_bM#NK6U=TS%)_3|v>BgFOI1W(yn`3myMiYo5vX6N1N z?6Fy+waxBBi7zYsgYN zc+4Y;wZv?y3(Ui41TE)i?&ofBxK6&+^V5MhuVS@(qfXxDy*}8pMuw4urNQN!{9l%5 zUdCk4u?&na2~_toxW2m?_M<v48eL3SWN-cPMNLW#)2$;7cT}Ei#J_R|Lf7W26+Q#`g zA~U~L2VTYoxno}+AIdwtTA{22Gi%7)-sGNkxUhp+R?e}ale-9d`$NoB-%}n&^8`KD zaYrC0pR=M!LpPYSk!bE?yEpX;8oI%8*7VaBPtJ(Vh_Sy^A?{p(Sv26z?#2`YdpUV8 z%p)%|)>rKgiI%(_B=vY`P1&15y;5z*bu|N{(6^C=tLNohBZdkw&(Z;|8p73|a1XO= z^=S5k$2-C6o%n3;3!{%(0s;GeHssZDROZ|wKsqJxipQM2yIqfsggFo08cFa33c3jP zPB8a#{1QP&xZ`||12kfo^)sL(KR-vq(Yqe5)cpb~f5CrGfRTO)b1ffnjvDE0etn5- z4(}fuk7s`hca(&#ylTC&m*_+ZzP^Z3?zM!9gL}Q$HNmBk8Aeyx zU#lLl&|lpFDcBr2_&zg!4=B_Lq`&bP^`VX8Jd}Fjeiy00n>D_*zr|syK)$&mTD89@ zbF}hpHK10UzD?mBBNB|Ht{ALw1%SOXwI8CLIHS0qvQz6!t06g~xXp}> zuel3N+DRfFa`zT8*?6p%K-!f=ScmtGlp{uiWZ{2%VT+wu{GSXQor0@V((UIeYqFIUcHBVs8Ux!u&CZ!oLTCtYX}zK0t&6b+Ph%4K61e> zJ^7fQ6}7Fz=ANnWjO03wB2GXfsTlD^=5q-=8WCe0(0SIXbI9}rj89G!lSw&O`m$DF zQJ`Czr~R)haBX`UTc6YpPp0f%=<92Iw^)`xEh{b0d z#zY!JmzU95X*>sKycwKbpKF<=*ojy7q6VSQ*mb@yRAQBd54f^gM>X)51+KHQ_c?Us zjp$!i%pc0GLoK2XDiUv-o?NQ*LC#-V;%v*2PiKXouknLg6Z!%Y$lYXjf{gUBr^zH@ zqNWl9WoN@>#8)37|7kO^qlbvS-9*gPHeyw05ks|x_$V`r-9xi4XEA1`xB3*+VtmIq z_B}e0=Us{WrlnrME4qEZBnRj1dRCo{BD7;M&nO6(d~5nMKehAV-=B3!#9GfN2W-h@K**GI7)=*QlM&T22Jzp-X^ zqVemI4F!I~JAp6DTr@l47`1B7skMdGaLPl0)p)n+cM(RM4aghOVIKnbJ(`1IGn~F) zPx2RVF`8M9VZPQ#GmfnbV|$Zp6=1%}SW_;6_k(C7#v1*DB{h}ttj31?g>S2|r?e-H zrPu$z8)*1A2{dj28|}0ge7Xg-zksjxc0A>qIp{{_TUX6|`*h4fx>pM{8{IggRsn6w z;dWrPOMvUoXNG2V}R8|JCV%BJ)G$%3}t6EQBKK=2j_eEznRw-v(X(NMfN_P_kWlVYKY*tG z14iztnx%Ui+T6ph&-nfX7~Ei;u$H8CE;GSNxxtqDySt}#8BZ(bWN(EbP@J7V%whi& zEPNd38P2*gc&o{y0#MQ)Y&5HnOsuPS0)K2QB)6WC%h2CF?jP`}|eeBC9=j;t~g!g!U(JdHrO0G;cHy3L%P^XVn;u5gjeNqsvt z*G?6{&izxhE@h(;Us)J&jo^D#D6&4>))H#;-F3J=SZNGS?ICV=U-uz}iQV|jIIuc* zL8PuV{;kksG)*e75CPs?5%hfL=Qs7}Djbm#ZnLJJomXnYvr5QtSF%DJlZpS;w$+mD zc48;41mNQ-@UgGA*VPhpjne}YiH4;9nGU$T1{P{)>M`1F8H06Rx#)VHIpDsk%&Zaf zk<;uvtiIofccb5mBg>5%vPO$qo&LS6T=5P(eHI$J0X4agvLC*&2cDQb#cJs8hC8cD zU$Zcc8Z%}lA@|J2C;Ph@H*dA;?2J@RJ1hUGeRB~{#T{C!rkV*D_U4FB z{Y36vq-3VXsE75EB3#ibnaV5m;#zQ9B_L~5p!H`1{Y};@6a~A#M9UkefZn{PwUtP%-tJ5hjnE~ZT+0v zp>;hZ$KmG-JPyFm?&G@~<;u25m1~O?hED=9sC=Qc___{KuZ8t<1;F|Sx9pkp~`%WTCYVd-tEB9mKhpD&=@|q zW{lZ8a;LtHY|PTy6zYq{L&k-}VqxDHqpg?0)m5GBCM(e8U}ZI8%P7f9JgsEtY{Pyx zdvAwp4qpNKm%!2u&bGyN|x)iv4 zl4iB~jD$MR+T{MXyEKoWOKSp~ZFVk@XU1IFQ_;0q+KLVDC*iv4@#z?=b2Ta^C1a8s z9h>vehv3SQDv*Ef*y#09x)|?uJAkBdnXZ4<1vf7C8DlhZJXT;a=?!u%()0Ejsx;bH zKSl!9D5=gk`>-`^?>c)~KLj&UxHxo2%&INNz~L4!>F%I4*v|wUdz`!Jhjq`xs@PV- zaduuY3c7WzG)hhDIVrHyHqk>zVk|SUj*GR`l$6TG%lu*`C;e#dqbLuIUDI-~pQd$e z?=v^|HS8XzEo6*exL>>&uf-~4?6X#g-Q1J{S~A&r=>1L$tfdNbg5)sA=}xG-F?tHz z^>jzy*pySytGhybcvse3=G%Fm*7`B3^%iinPrO)-14i`QCtz0gihK<`T*1R0g%=!c zoab5Yq~&vnYrD62oV%#YD(j4zx()2z=XR&@HrLlvqowQ4p1#BAKw4<0CE&AiLXVC9 zI#)X5I$v?@eWb?=#%VUj0Y++dDECO+sSA6!+^4c5+c_R~5OXH?xb}0-*BhsIR1cS& z;M$jor#1Al2EW)-+{ms(%<~0jJ>%?W+}*u~TR`(H*F4GmZgEc|49_wO*IVVddygl; zp|mSsJO&5CC?4}k#2m~VHgi`$n(M1`HBwjapE){DfYKe#wkG~9u6h#s*v}^|y))d& z$P9Nz^=wJ+w>kO||KEcH%$(3W7YX*`qls8QU)@06GZ~bSlGkT1lKhK!XK$9mJjWGw#LI2A&zNvSs zL3#W=9!6ED?TG&n*U}el4$EE6^|y#%xA_d`oA=^+ZZw%5fa73F_!#?eipLS4xS3b0 z)*C(Ks$!2Ux#liV_VWoiQTgV$+!52;b`#oo#c%rStg#!`qm2|Xi{ugK`kQjiy;ft& z{7o)2lU{k^yo@h7!|2?3QTuRxJI?*Yno+#=Ww4)RSQ6R)TKYT8c~&I)kKg~}T2`F7 z!xfDwFz!$&2~o9qE679yaWmRzMqs~&PhIDY(~-Z8)l?E04Pk6=dX5lt#zws2cRd?- znWwo&%0zcnr72_9t}=@Az``}>JimBft)%PB=o(|QE{r^KjqkTORv0)wJ=;oLchu#o zivfm=iw>_=Pxw`8wEuvxGi$=kL-hjv;?lLzDXszT2i@aozZ7FJ-OF~*#oo*2uqi*i zms0;0zk65ZzPg9lvyYq|%`Wrme@{3^oVf?$KR0>3&p*ZpD6izq$H7>nT)P~)zQuwM z^a(02^+LObXGC_H4`Y;ro=vrAeS7+v^lByn!e&Gyh?t$&aRzR}mHwvYhaxPz}< zYK5?5T+c{7`xqFvZ!gm{tlaA5UNZro^+?6%n_e)x(#kJJq+H}72Uzp(0-wU4&6amn zc?5)v4!#C{T#Zh0);T`8=O({}< zC8)bJM&IbEk7QL$14P36T53co19_`t@v73cBX$?lctIn{m2O5KoMTL`k@9Dl<}Ncf z>A|(k8XNBUX>Z9lNBDk%HOCm9BX}?DRk4B3W(3)5c`f^>#*i5Eu!8s1$kK4O$YJiJ zBs~lTxHjKp?#}lvpOrvXS5ii*Qz-BBwMW4@dQQV0_{3l+yx%CUt*!f#RZ?H_Q?4o1 z83Sp~%4x24hO1qK8m|EZW9gnjNwI-cY@n7HT)KW_1>-6CW`>F$s%*Tcp~fwD_|(Ey z7ght-TkWpTo9NwdplO#x1J_?)f_oM~M=gfNYKM8PZYx&mE4Mpv*n^OYZ`#M%c+C#1 z?B<>oZp^?vv^?EkwGV@Pxcch!xLV0pOVb^^>ip(CJkEWtG+ZSaFmmVE452Xg!uy0_ zIisgrJ<*&CC6@Ktqz&_mw3FrPOo29|-@g=dF|*ZvC$)fIEud-ET^&a7Hot0JlxGyh z`9B}yFek_^S1ALY7sBE!9NUwaE4z=X|5rJoG*CvEkDY@@9;m|Ih8$eUXgDEkl_z}^ zR!6kMcwDGfjWVpws3rQ`57+F+PBwDH2zB?@&9XhntFi7%0Cfy^wxu9FeEM1o0o5AJ zp%!*-OYFf`__^BQ=lU3*S6BRzUD>7R66~^>QR&Rz0rusXYe{Bo7Mc5ddK2<6%5dg| z{T|ZuTN>_=hkKY|T#ko%7}oW32P<3y-rWJ|vjvc>$A5L89KB5DZ0U)0&s%?kxr%z> zvmvA1XUWQ4g>@;8D8u!oy3&lRDpYUf0CVp3Ah%{7op|=ZHyN(}ZVmphsS5J-ZK(2j0;>&mPQ6jA(1RPoP&)Z;=w$NIUEMyb;XT7>3fp zNX_MbAO3Cy1V3W7X0di)ZdNU5&&=BL+1&)IExa4dOKPpg`4t$yzFmDq(twgAA5U#a zYg_7zG#X59b(cafP!V3uiqFf;b8=<@{u3+V3^;9Y_t2C}NuYK$^GMz!pjYc}UW3Q% zd8ZX`FCncs^}lDVJ>jUT3>@$7v^&3f8K2o%`q1Sqcesl~AMUW!08eFscy(Z19{iit zX8$H7jJryipt=-ruvWA3Tn|PJ^3~7sk1K8xjtTpf?Pu^5Tcm8x8~HqfmNCECr} z9X#u=N~d;#t;v7l)@q-{8C@yHR~#&AIqGrC7{E|?D#3(aPDZE<)8C+<&Y0oKT<6{3 zs*SlrQ$95fUd>*9mtXAAZS@=d?n*i1PQsp3_p`PCjXJZQi}x0P;?mvPyv(8yzsM7I zihdKkS7G$ExQAX`D=S&g!O9X1xRa|w^ME>q&dXOthfQBGmFUYVSNNz20T*0c%$qy`#58U0jcJIJ)k2 zU~}pdautZdN;S*J>zgGhB@geRY7KR?)geuDLSxIijp1G<@ce5Im^L7Bp!YAu)*y}tkoRywyxlivp zs^?8TOPu-HF6u^-7+oIjuWJR5OdJ!IGMV@{9pClyq+@J3xt3S+b7rpR72SPNH;eQJm?njuIy!*HwG+T?lG1SRch`FRk@N z_$-WxeF>KIaH&;aga*!_Cu?u&_c78?&0ab=6WDOZ4cqJ9-CTW1+PB(*O4SG?uYM-I zHtwR_X53QJO*8>3PFxADW&GJ;bcB6@rf?7);s~0!Rr1Xj)5~uBwEG{%QmQ8zWfhMx zTVF}W<3{LyLv$N{O;$~7(p{T^nPgl>E|-X!@XOpDQG=D z44`cOm-g>N?y5en|50gR_P7yAYUg2F`YPucr>j-&&cIG)v=ML1R`j54Xhhc9*v{)e z+;u14jRtoV<{|yZ_>3KY2**D~57!zp-p9&7u1c;Z(vtYpYSZuI7{vW8;BTygHkwst z52AVL3EYG(X56Zk47Tvum~8K>H)sb>J*D~zjMkPy-4OiWv*JJ?$cQJZ5y-Q#ruA~{|8)+kJG9*7sr@6 zqxGGKTD5UD@{s;IqdgKrKjEDiC7f~k`j3q?F_URifUji%6ju>(x|IEFvn~JSvlV{- zWiNg;HsDGiu!m!fRIy)@6;kw4-2-ykWBQ%Mr;=A6q(0{GuB;gf`XjWf_X8vSaEH0p ze^88BR_>kK!_^%qYl4{txEJ_akwaYQk8>Z^9Z31iDu$8FOUbM!%{4=r`-E}H&r!MfCD6{A2@CmtBQtp$9 zyIS)iB~nGtb$q_X9l64B!&2pnz0Oe85CZj7A^$r}fPjMT`L|)gVx4X(>Z3_e+4FP!Qok%B}eQ z_UA+lc;9=2Tj8vb0isi^7PUCwpqqbfN2+0=3aMSl4+n#PD(}$U zi&?9MjQdPx^F&6@&WA~`Ubq-cP*DsKmmmYL=GhY)0Cze;P3NxYw#tt{P}b@c2+a?z-1C316U z(c5x0WSjVizoH&|dc1P{K>RxVX?$(uK>dC7Zj$wQHqQkNf*g$pdFPkq3F0xjlD_kt z@K?)(QKJdnqs61)TL(g$>fZ6S9LgD$$rtiHKmA%>_#TeEk$ciC@C@?tpJ8S4A&W@T zts0+vo*7qdMRW;*O^^Ao@E5XQ&toV9x1gMTH$Sy6|fm9;jxj0XWxoyEafn-C(>%4boFccpvvIV;9>GtNDa?^ zo}@pM{BJ$SYWhw-%7ajA9@RBBvNbs_k2yhm=186y*(bkaHTM;3P(eqf3Hb-G`Jy|g za`tqtp0=k|+eN!!=e|g1K-ojzk3TdRtSvg{&q~czcJGZtl3J=qXvFe>n3}Q9)RPbPyFeR z(JK~+&!YBZJ!vHd2SY{kv|5wU&vNc_Dxd9ra2-vCR@I;9W45su!t861>YftUfCm1Gm^#kpZp=-KAatj>?aQvm0(SAQ70 zMs6rRyOMYQbaf6`1!DR4BH91Uc+?|fyV0XmA(#ohG;fiXem|oW+Y}wg<@x0o(&8j- zEM{NYlg8#br*fC*T%wiNa~%oCDivRRHheVv!(vSA74Ix#8Qe2tbn1aW&&*dwnj3lb zGL_{%sebl-q=%=f9*B-ASN2HqR)5LoW9dQbI*140Ked19XJS4$Zv9B)2(8A!B4{kg z-i9d=Pk;_mS4f=Ib8HvW?n2r&g+|?TE7h@Q%3iJoIRFt&u6swEm&MNx69*#=TRYmZ-Y%}g!C$iKUNz*ubrWC2+5!>;%Y{_K6a&winI|s59aY+`pe#P z*Xs4Gdm7wqG!1s{$I&q8b1WOWIc*g>LsU&=4Uzz-XVFw%;^)URvuFj@Y9Ze*rmZt+ zb2Y!ZJZJzH-OAYLE_76taX-wVDi7J5b6mQ?~}sD2zOCchgSe3JWaZ@QjHxV89#%igUq3Ci6P^+4|B(BSrb~fj9@Yu zg&qnGs?c*VuOGATJT<@*vS~i795qfR89#X?@)?*kv_<;hrdRn>QS7`Vb^Fu)lZV29w*KPRl{z0nHJ$TtS6n3mdJ0$XM*qO z0bam%)v|s2BzF@ngKAPs1I>K9dz=N*I^&P7YVcD?eY!Rm&N7c^Q3IL|>cDuJ&B> zN1+$Kz!lXs=90mOyLrsDS6p%|RWHb6tHvt?{br7`xlN(OB0*ue2B1hAi2$xj)@Sy$SPr zJ^j6v{zE^}rqt;pAy5L_R^A%Ez}}@9(&b>I8Yd$&>1T8^s1Rr;)x%l~3Qu;H-N2@R zBq?S=TpJDnLd5eMSYYVR{A!sujeaWLY`;KDK*h-Ly)j;FZ56(F@z(NLT-J%=9H z2{i57In*gZ$+8Xcd{T)FU~7nCkQ6+TJPpO>@QK?O&{11yUu6yG713iB5g*JnGaLs} z4-+0n|IM5I%ZtSp<|m#>3vcD%Ti7*trF=Qk$-bnc(m3>sKhYgqAC6Ct<*Q=vg?uK1 zU~%9U&;y(=8=BWtJG^+j)qLeprnA|P_1=DvaY94DKJ(VdJTwkD^Hv8voDGds#?x2k zLp_HF-K>U&$awnch4c)jg=U~O1P;|1-AHfnOEv);5r*e{u3>$W%`~Lrq3^>(3-lDg z`SDWM^hEZ79l^e0HKH8&xrZ4m?<(J|XgRE}QSyaC@I!F&Ao>h-QJfm&r8R*0AYb^E z%3F9vWv*OIf7r035PpI(NE4nlwK{1JYFS$)K3OPuo=aGjJy|dD0Cj|5+RP@N$Ug&b zO4?n@v6A(|jXk*t*I^UbDV}3yt?J8H#~niU)A-)MN>oM6Y=2@n`xE7nAtGnviyZqB z$B~o6S41O~?P$i~!eF{!M4$=T@OC#@$E(&ho5#yP1L@E>40Hq?mu}4?&mX@hqn1a_ z!^Km~CTr9LQj9EPJ*yw^b7+MwPxpc+flpBf2(C;79cB+7M)9&x)&7-ZB05Ajl9#}v zwv~#Q;!({agf4=?tNc-6f<3)9_#>Z!yqn`WvpD*2G;MlbIa~3^k2A*X;`_wfuqL{OLnLRj(M`H%BhBEkW;X}x8 zkpaIFZG#wtj*`{)`G_KkhuIsS4E+=SLAIgTh_&67onXF2-q>dFh%)|5v)UxXYsbLn z@zwL;@%Or(g@E!!_r#3EDP_ut{)tM`Hqnt76hDGkJ04f;-ownSTy?lGF$eY;+a99W zeA64?#BPOJ*g0A)mXnuad*+lMSf)!4^g8g22kc=rmp@CQRQqT5%B^S8Rs8M0Yjc))clH z-oM@qoXHjFULJWPv$&~IkO691p{ecp%p!Gf2;(C(KgLlI(;iyDc8|@|aMBR7i8}TJMzGPx!F|-=mwn%vEnA_9FiaYXi;FAuL z6{m%RQxjDs1pPsCT_>`g)6} z9Q4$k%^M^-8=B06=4ZXyiG9H+LIYb}^#W;UkgfGon@RU&E7II}Huw_kX_+DVX%D^1 z6+Tp-q!)MvpPMzolSRdF6Z}N{L7sn~^U=78Z>@%Sm{D6Bdw@^S z44L!l(b}{tD?eo3!TahPT@~JSr}-`Yt&OO<)eJpNxIf zT%TGwoO5=I@%FA=Z+FyRvu3T>;poRb9n%(Z7nZ`V+(BD5Thc6WdtbQbWIzR-MW z!u|T{y^UCHBL5?WcaAx?ihMZe(hGN@AK7`@=@S%GM>FQ`MOl;VTGyhP(lu(}X(EZy zik0&2aFsrqwQ{`adR3Mgm3>&+oYHJZ>AT9j?w8)fT({q-!GX&;Tkhd1hc(l4Gi6u6 z+t548+wG6sKHvK16Z$C)nO!mf1-U0)+SQ-FyZ`_D*nDZvU%zlnM_>;+>)Yu$E2>9! irylsEul_fkd)NA%_l}}>FJCMrXr(>&TXO#O>wf`eKfwtA diff --git a/samples/Panasonic_JU-475-5_5.25_1.2MB_seekdown_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav b/samples/Panasonic_JU-475-5_5.25_1.2MB_seekdown_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav deleted file mode 100644 index 3330fc7b81530eeb01729e5387e221b80a414630..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27378 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 diff --git a/samples/Panasonic_JU-475-5_5.25_1.2MB_seekup_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav b/samples/Panasonic_JU-475-5_5.25_1.2MB_seekup_40_tracks_285ms_5ms_per_track_48000_16_1_PCM.wav deleted file mode 100644 index 620a32d4d61a573e53c1ef8864b2bf9aef83da00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27378 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 diff --git a/samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav b/samples/TeacFD-55GFR_5.25_1.2MB_motor_loop_48000_16_1_PCM.wav deleted file mode 100644 index bd8a4cce5cb4eea307257e06fbbd3248f8bcab81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62924 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 diff --git a/samples/readme.txt b/samples/readme.txt deleted file mode 100644 index 5aca6b001..000000000 --- a/samples/readme.txt +++ /dev/null @@ -1,13 +0,0 @@ -These samples have been edited from Shiru's recordings. - -Originals can be found here: -http://shiru.untergrund.net/files/flopster.zip - -Original samples can edited and used separately from the Flopster VSTi under the -Creative Commons Attribution (CC-BY) license terms. - -Installation instructions -- Currently these samples need to be manually copied to the 86box executable path under '/samples' - or to the same directory with 86box executable. -- The samples are 48kHz 16bit 1 channel PCM WAVE samples. Immersive86box code does not make any resampling, - and assumes that the host audio system is using 48kHz 16bit output. \ No newline at end of file From 288b6454eeeb0cbd813ad75a94a6efdc112d6be9 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 23 Sep 2025 17:55:46 +0200 Subject: [PATCH 232/274] AdLib Gold: Correctly use separate pseudo-stereo X and Y for the two sources. --- src/include/86box/filters.h | 18 +++++++++--------- src/sound/snd_adlibgold.c | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/include/86box/filters.h b/src/include/86box/filters.h index d11e79512..3e20fa653 100644 --- a/src/include/86box/filters.h +++ b/src/include/86box/filters.h @@ -75,7 +75,7 @@ adgold_lowpass_iir(int c, int i, float NewSample) /* fc=56Hz */ static inline float -adgold_pseudo_stereo_iir(float NewSample) +adgold_pseudo_stereo_iir(int i, float NewSample) { float ACoef[NCoef + 1] = { 0.00001409030866231767, @@ -89,23 +89,23 @@ adgold_pseudo_stereo_iir(float NewSample) 0.98738361004063568000 }; - static float y[NCoef + 1]; /* output samples */ - static float x[NCoef + 1]; /* input samples */ + static float y[2][NCoef + 1]; /* output samples */ + static float x[2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ for (n = NCoef; n > 0; n--) { - x[n] = x[n - 1]; - y[n] = y[n - 1]; + x[i][n] = x[i][n - 1]; + y[i][n] = y[i][n - 1]; } /* Calculate the new output */ - x[0] = NewSample; - y[0] = ACoef[0] * x[0]; + x[i][0] = NewSample; + y[i][0] = ACoef[0] * x[i][0]; for (n = 1; n <= NCoef; n++) - y[0] += ACoef[n] * x[n] - BCoef[n] * y[n]; + y[i][0] += ACoef[n] * x[i][n] - BCoef[n] * y[i][n]; - return y[0]; + return y[i][0]; } /* fc=3.2kHz - probably incorrect */ diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 360da3fec..3be304cd7 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -82,7 +82,7 @@ typedef struct adgold_t { int treble; int bass; - int16_t opl_buffer[SOUNDBUFLEN * 2]; + int16_t opl_buffer[MUSICBUFLEN * 2]; int16_t mma_buffer[2][SOUNDBUFLEN]; int pos; @@ -829,7 +829,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] += adgold_pseudo_stereo_iir(adgold_buffer[c]); + adgold_buffer[c] += adgold_pseudo_stereo_iir(0, adgold_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet @@ -944,7 +944,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] += adgold_pseudo_stereo_iir(adgold_buffer[c]); + adgold_buffer[c] += adgold_pseudo_stereo_iir(1, adgold_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet From 236007b4c8571c474fc808c5bcd875390941a4c8 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Tue, 23 Sep 2025 22:31:12 +0600 Subject: [PATCH 233/274] Force AlphaBuffer to be 0 (#6217) --- src/qt/qt_openglrenderer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/qt_openglrenderer.cpp b/src/qt/qt_openglrenderer.cpp index 7f8a5e2fa..492acb630 100644 --- a/src/qt/qt_openglrenderer.cpp +++ b/src/qt/qt_openglrenderer.cpp @@ -832,6 +832,7 @@ OpenGLRenderer::OpenGLRenderer(QWidget *parent) format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); format.setRenderableType(QSurfaceFormat::OpenGL); format.setSwapInterval(video_vsync ? 1 : 0); + format.setAlphaBufferSize(0); setFormat(format); From c71811f57994e624ae1aea55b05e2b845af1727d Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 23 Sep 2025 19:36:26 +0200 Subject: [PATCH 234/274] Attempt to halve the output volume. --- src/sound/snd_adlibgold.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 3be304cd7..e41726b00 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -853,7 +853,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 17; + temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 18; lowpass = adgold_lowpass_iir(0, 0, temp); highpass = adgold_highpass_iir(0, 0, temp); if (adgold->bass > 6) @@ -870,7 +870,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 17; + temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 18; lowpass = adgold_lowpass_iir(0, 1, temp); highpass = adgold_highpass_iir(0, 1, temp); if (adgold->bass > 6) @@ -968,7 +968,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 17; + temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 18; lowpass = adgold_lowpass_iir(1, 0, temp); highpass = adgold_highpass_iir(1, 0, temp); if (adgold->bass > 6) @@ -985,7 +985,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 17; + temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 18; lowpass = adgold_lowpass_iir(1, 1, temp); highpass = adgold_highpass_iir(1, 1, temp); if (adgold->bass > 6) From a87bcd410e920a6269a63b828b1237538b0894a9 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Tue, 23 Sep 2025 19:49:15 +0200 Subject: [PATCH 235/274] AdLib Gold changes of the day (September 23rd, 2025) (#6218) 1. Make sure the check to the Surround module is properly placed when disabled/enabled. 2. Replace local adgold_buffer with opl_buffer from its struct to improve the audio output and less clipping. --- src/sound/snd_adlibgold.c | 71 +++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index e41726b00..f708b6967 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -291,7 +291,8 @@ adgold_write(uint16_t addr, uint8_t val, void *priv) case 0x18: /*Surround*/ adgold->adgold_38x_regs[0x18] = val; - ym7128_write(&adgold->ym7128, val); + if (adgold->surround_enabled) + ym7128_write(&adgold->ym7128, val); break; default: @@ -782,34 +783,30 @@ static void adgold_get_buffer(int32_t *buffer, int len, void *priv) { adgold_t *adgold = (adgold_t *) priv; - int16_t *adgold_buffer = malloc(sizeof(int16_t) * len * 2); - if (adgold_buffer == NULL) - fatal("adgold_buffer = NULL"); - int c; adgold_update(adgold); for (c = 0; c < len * 2; c += 2) { - adgold_buffer[c] = ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; - adgold_buffer[c + 1] = ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; + adgold->opl_buffer[c] = ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; + adgold->opl_buffer[c + 1] = ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; } if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold_buffer, len); + ym7128_apply(&adgold->ym7128, adgold->opl_buffer, len); switch (adgold->adgold_38x_regs[0x8] & 6) { case 0: for (c = 0; c < len * 2; c++) - adgold_buffer[c] = 0; + adgold->opl_buffer[c] = 0; break; case 2: /*Left channel only*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c + 1] = adgold_buffer[c]; + adgold->opl_buffer[c + 1] = adgold->opl_buffer[c]; break; case 4: /*Right channel only*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] = adgold_buffer[c + 1]; + adgold->opl_buffer[c] = adgold->opl_buffer[c + 1]; break; case 6: /*Left and right channels*/ break; @@ -821,7 +818,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) switch (adgold->adgold_38x_regs[0x8] & 0x18) { case 0x00: /*Forced mono*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] = adgold_buffer[c + 1] = ((int32_t) adgold_buffer[c] + (int32_t) adgold_buffer[c + 1]) / 2; + adgold->opl_buffer[c] = adgold->opl_buffer[c + 1] = ((int32_t) adgold->opl_buffer[c] + (int32_t) adgold->opl_buffer[c + 1]) / 2; break; case 0x08: /*Linear stereo*/ break; @@ -829,17 +826,17 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] += adgold_pseudo_stereo_iir(0, adgold_buffer[c]); + adgold->opl_buffer[c] += adgold_pseudo_stereo_iir(0, adgold->opl_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet and a very vague understanding of how op-amps work to go on*/ for (c = 0; c < len * 2; c += 2) { - int16_t l = adgold_buffer[c]; - int16_t r = adgold_buffer[c + 1]; + int16_t l = adgold->opl_buffer[c]; + int16_t r = adgold->opl_buffer[c + 1]; - adgold_buffer[c] += (r / 3) + ((l * 2) / 3); - adgold_buffer[c + 1] += (l / 3) + ((r * 2) / 3); + adgold->opl_buffer[c] += (r / 3) + ((l * 2) / 3); + adgold->opl_buffer[c + 1] += (l / 3) + ((r * 2) / 3); } break; @@ -853,7 +850,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 18; + temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 18; lowpass = adgold_lowpass_iir(0, 0, temp); highpass = adgold_highpass_iir(0, 0, temp); if (adgold->bass > 6) @@ -870,7 +867,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 18; + temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 18; lowpass = adgold_lowpass_iir(0, 1, temp); highpass = adgold_highpass_iir(0, 1, temp); if (adgold->bass > 6) @@ -889,42 +886,36 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) } adgold->pos = 0; - - free(adgold_buffer); } static void adgold_get_music_buffer(int32_t *buffer, int len, void *priv) { adgold_t *adgold = (adgold_t *) priv; - int16_t *adgold_buffer = malloc(sizeof(int16_t) * len * 2); - if (adgold_buffer == NULL) - fatal("adgold_buffer = NULL"); - int c; const int32_t *opl_buf = adgold->opl.update(adgold->opl.priv); for (c = 0; c < len * 2; c += 2) { - adgold_buffer[c] = ((opl_buf[c] * adgold->fm_vol_l) >> 7) / 2; - adgold_buffer[c + 1] = ((opl_buf[c + 1] * adgold->fm_vol_r) >> 7) / 2; + adgold->opl_buffer[c] = ((opl_buf[c] * adgold->fm_vol_l) >> 7) / 2; + adgold->opl_buffer[c + 1] = ((opl_buf[c + 1] * adgold->fm_vol_r) >> 7) / 2; } if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold_buffer, len); + ym7128_apply(&adgold->ym7128, adgold->opl_buffer, len); switch (adgold->adgold_38x_regs[0x8] & 6) { case 0: for (c = 0; c < len * 2; c++) - adgold_buffer[c] = 0; + adgold->opl_buffer[c] = 0; break; case 2: /*Left channel only*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c + 1] = adgold_buffer[c]; + adgold->opl_buffer[c + 1] = adgold->opl_buffer[c]; break; case 4: /*Right channel only*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] = adgold_buffer[c + 1]; + adgold->opl_buffer[c] = adgold->opl_buffer[c + 1]; break; case 6: /*Left and right channels*/ break; @@ -936,7 +927,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) switch (adgold->adgold_38x_regs[0x8] & 0x18) { case 0x00: /*Forced mono*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] = adgold_buffer[c + 1] = ((int32_t) adgold_buffer[c] + (int32_t) adgold_buffer[c + 1]) / 2; + adgold->opl_buffer[c] = adgold->opl_buffer[c + 1] = ((int32_t) adgold->opl_buffer[c] + (int32_t) adgold->opl_buffer[c + 1]) / 2; break; case 0x08: /*Linear stereo*/ break; @@ -944,17 +935,17 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold_buffer[c] += adgold_pseudo_stereo_iir(1, adgold_buffer[c]); + adgold->opl_buffer[c] += adgold_pseudo_stereo_iir(1, adgold->opl_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet and a very vague understanding of how op-amps work to go on*/ for (c = 0; c < len * 2; c += 2) { - int16_t l = adgold_buffer[c]; - int16_t r = adgold_buffer[c + 1]; + int16_t l = adgold->opl_buffer[c]; + int16_t r = adgold->opl_buffer[c + 1]; - adgold_buffer[c] += (r / 3) + ((l * 2) / 3); - adgold_buffer[c + 1] += (l / 3) + ((r * 2) / 3); + adgold->opl_buffer[c] += (r / 3) + ((l * 2) / 3); + adgold->opl_buffer[c + 1] += (l / 3) + ((r * 2) / 3); } break; @@ -968,7 +959,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold_buffer[c] * adgold->vol_l) >> 18; + temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 18; lowpass = adgold_lowpass_iir(1, 0, temp); highpass = adgold_highpass_iir(1, 0, temp); if (adgold->bass > 6) @@ -985,7 +976,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold_buffer[c + 1] * adgold->vol_r) >> 18; + temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 18; lowpass = adgold_lowpass_iir(1, 1, temp); highpass = adgold_highpass_iir(1, 1, temp); if (adgold->bass > 6) @@ -1004,8 +995,6 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) } adgold->opl.reset_buffer(adgold->opl.priv); - - free(adgold_buffer); } static void From 087a0056640d944c926f8d5ea15f201add3de8a4 Mon Sep 17 00:00:00 2001 From: OBattler Date: Tue, 23 Sep 2025 20:46:24 +0200 Subject: [PATCH 236/274] More AdLib Gold fixes. --- src/include/86box/snd_ym7128.h | 12 ++++++------ src/sound/snd_adlibgold.c | 35 +++++++++++++++++----------------- src/sound/snd_ym7128.c | 24 +++++++++++------------ 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/include/86box/snd_ym7128.h b/src/include/86box/snd_ym7128.h index a0796b1fa..f0657425f 100644 --- a/src/include/86box/snd_ym7128.h +++ b/src/include/86box/snd_ym7128.h @@ -19,18 +19,18 @@ typedef struct ym7128_t { int c1; int t[9]; - int16_t filter_dat; - int16_t prev_l; - int16_t prev_r; + int16_t filter_dat[2]; + int16_t prev_l[2]; + int16_t prev_r[2]; - int16_t delay_buffer[2400]; - int delay_pos; + int16_t delay_buffer[2][2400]; + int delay_pos[2]; int16_t last_samp; } ym7128_t; void ym7128_init(ym7128_t *ym7128); void ym7128_write(ym7128_t *ym7128, uint8_t val); -void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len); +void ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len); #endif /*SOUND_YM7128_H*/ diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index f708b6967..0181db72e 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -82,6 +82,7 @@ typedef struct adgold_t { int treble; int bass; + int16_t samp_buffer[SOUNDBUFLEN * 2]; int16_t opl_buffer[MUSICBUFLEN * 2]; int16_t mma_buffer[2][SOUNDBUFLEN]; @@ -788,25 +789,25 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) adgold_update(adgold); for (c = 0; c < len * 2; c += 2) { - adgold->opl_buffer[c] = ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; - adgold->opl_buffer[c + 1] = ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; + adgold->samp_buffer[c] = ((adgold->mma_buffer[0][c >> 1] * adgold->samp_vol_l) >> 7) / 4; + adgold->samp_buffer[c + 1] = ((adgold->mma_buffer[1][c >> 1] * adgold->samp_vol_r) >> 7) / 4; } if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold->opl_buffer, len); + ym7128_apply(&adgold->ym7128, adgold->samp_buffer, 0, len); switch (adgold->adgold_38x_regs[0x8] & 6) { case 0: for (c = 0; c < len * 2; c++) - adgold->opl_buffer[c] = 0; + adgold->samp_buffer[c] = 0; break; case 2: /*Left channel only*/ for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c + 1] = adgold->opl_buffer[c]; + adgold->samp_buffer[c + 1] = adgold->samp_buffer[c]; break; case 4: /*Right channel only*/ for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] = adgold->opl_buffer[c + 1]; + adgold->samp_buffer[c] = adgold->samp_buffer[c + 1]; break; case 6: /*Left and right channels*/ break; @@ -818,7 +819,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) switch (adgold->adgold_38x_regs[0x8] & 0x18) { case 0x00: /*Forced mono*/ for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] = adgold->opl_buffer[c + 1] = ((int32_t) adgold->opl_buffer[c] + (int32_t) adgold->opl_buffer[c + 1]) / 2; + adgold->samp_buffer[c] = adgold->samp_buffer[c + 1] = ((int32_t) adgold->samp_buffer[c] + (int32_t) adgold->samp_buffer[c + 1]) / 2; break; case 0x08: /*Linear stereo*/ break; @@ -826,17 +827,17 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) /*Filter left channel, leave right channel unchanged*/ /*Filter cutoff is largely a guess*/ for (c = 0; c < len * 2; c += 2) - adgold->opl_buffer[c] += adgold_pseudo_stereo_iir(0, adgold->opl_buffer[c]); + adgold->samp_buffer[c] += adgold_pseudo_stereo_iir(0, adgold->samp_buffer[c]); break; case 0x18: /*Spatial stereo*/ /*Quite probably wrong, I only have the diagram in the TDA8425 datasheet and a very vague understanding of how op-amps work to go on*/ for (c = 0; c < len * 2; c += 2) { - int16_t l = adgold->opl_buffer[c]; - int16_t r = adgold->opl_buffer[c + 1]; + int16_t l = adgold->samp_buffer[c]; + int16_t r = adgold->samp_buffer[c + 1]; - adgold->opl_buffer[c] += (r / 3) + ((l * 2) / 3); - adgold->opl_buffer[c + 1] += (l / 3) + ((r * 2) / 3); + adgold->samp_buffer[c] += (r / 3) + ((l * 2) / 3); + adgold->samp_buffer[c + 1] += (l / 3) + ((r * 2) / 3); } break; @@ -850,7 +851,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 18; + temp = ((int32_t) adgold->samp_buffer[c] * adgold->vol_l) >> 17; lowpass = adgold_lowpass_iir(0, 0, temp); highpass = adgold_highpass_iir(0, 0, temp); if (adgold->bass > 6) @@ -867,7 +868,7 @@ adgold_get_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 18; + temp = ((int32_t) adgold->samp_buffer[c + 1] * adgold->vol_r) >> 17; lowpass = adgold_lowpass_iir(0, 1, temp); highpass = adgold_highpass_iir(0, 1, temp); if (adgold->bass > 6) @@ -902,7 +903,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) } if (adgold->surround_enabled) - ym7128_apply(&adgold->ym7128, adgold->opl_buffer, len); + ym7128_apply(&adgold->ym7128, adgold->opl_buffer, 1, len); switch (adgold->adgold_38x_regs[0x8] & 6) { case 0: @@ -959,7 +960,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) int32_t highpass; /*Output is deliberately halved to avoid clipping*/ - temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 18; + temp = ((int32_t) adgold->opl_buffer[c] * adgold->vol_l) >> 17; lowpass = adgold_lowpass_iir(1, 0, temp); highpass = adgold_highpass_iir(1, 0, temp); if (adgold->bass > 6) @@ -976,7 +977,7 @@ adgold_get_music_buffer(int32_t *buffer, int len, void *priv) temp = 32767; buffer[c] += temp; - temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 18; + temp = ((int32_t) adgold->opl_buffer[c + 1] * adgold->vol_r) >> 17; lowpass = adgold_lowpass_iir(1, 1, temp); highpass = adgold_highpass_iir(1, 1, temp); if (adgold->bass > 6) diff --git a/src/sound/snd_ym7128.c b/src/sound/snd_ym7128.c index 59e5691e9..08b33f414 100644 --- a/src/sound/snd_ym7128.c +++ b/src/sound/snd_ym7128.c @@ -111,10 +111,10 @@ ym7128_write(ym7128_t *ym7128, uint8_t val) ym7128->a0 = new_a0; } -#define GET_DELAY_SAMPLE(ym7128, offset) (((ym7128->delay_pos - offset) < 0) ? ym7128->delay_buffer[(ym7128->delay_pos - offset) + 2400] : ym7128->delay_buffer[ym7128->delay_pos - offset]) +#define GET_DELAY_SAMPLE(ym7128, offset) (((ym7128->delay_pos[i] - offset) < 0) ? ym7128->delay_buffer[i][(ym7128->delay_pos[i] - offset) + 2400] : ym7128->delay_buffer[i][ym7128->delay_pos[i] - offset]) void -ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len) +ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int i, int len) { for (int c = 0; c < len * 2; c += 4) { /*YM7128 samples a mono stream at ~24 kHz, so downsample*/ @@ -125,13 +125,13 @@ ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len) int32_t samp_r = 0; filter_temp = GET_DELAY_SAMPLE(ym7128, ym7128->t[0]); - filter_out = ((filter_temp * ym7128->c0) >> 11) + ((ym7128->filter_dat * ym7128->c1) >> 11); + filter_out = ((filter_temp * ym7128->c0) >> 11) + ((ym7128->filter_dat[i] * ym7128->c1) >> 11); filter_out = (filter_out * ym7128->vc) >> 16; samp = (samp * ym7128->vm) >> 16; samp += filter_out; - ym7128->delay_buffer[ym7128->delay_pos] = samp; + ym7128->delay_buffer[i][ym7128->delay_pos[i]] = samp; for (uint8_t d = 0; d < 8; d++) { samp_l += (GET_DELAY_SAMPLE(ym7128, ym7128->t[d + 1]) * ym7128->gl[d]) >> 16; @@ -141,17 +141,17 @@ ym7128_apply(ym7128_t *ym7128, int16_t *buffer, int len) samp_l = (samp_l * ym7128->vl * 2) >> 16; samp_r = (samp_r * ym7128->vr * 2) >> 16; - buffer[c] += (samp_l + (int32_t) ym7128->prev_l) / 2; - buffer[c + 1] += (samp_r + (int32_t) ym7128->prev_r) / 2; + buffer[c] += (samp_l + (int32_t) ym7128->prev_l[i]) / 2; + buffer[c + 1] += (samp_r + (int32_t) ym7128->prev_r[i]) / 2; buffer[c + 2] += samp_l; buffer[c + 3] += samp_r; - ym7128->delay_pos++; - if (ym7128->delay_pos >= 2400) - ym7128->delay_pos = 0; + ym7128->delay_pos[i]++; + if (ym7128->delay_pos[i] >= 2400) + ym7128->delay_pos[i] = 0; - ym7128->filter_dat = filter_temp; - ym7128->prev_l = samp_l; - ym7128->prev_r = samp_r; + ym7128->filter_dat[i] = filter_temp; + ym7128->prev_l[i] = samp_l; + ym7128->prev_r[i] = samp_r; } } From d99d052b826d5306679728cad3b8b30967f7348c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miran=20Gr=C4=8Da?= Date: Wed, 24 Sep 2025 01:16:20 +0200 Subject: [PATCH 237/274] AdLib Gold: Use the YMF289. --- src/sound/snd_adlibgold.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 0181db72e..4c4c17341 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -1063,7 +1063,7 @@ adgold_init(UNUSED(const device_t *info)) adgold->surround_enabled = device_get_config_int("surround"); adgold->gameport_enabled = device_get_config_int("gameport"); - fm_driver_get(FM_YMF262, &adgold->opl); + fm_driver_get(FM_YMF289, &adgold->opl); if (adgold->surround_enabled) ym7128_init(&adgold->ym7128); From ba9c1732f7907d99b6986aadb95bbc183b586bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miran=20Gr=C4=8Da?= Date: Wed, 24 Sep 2025 01:41:00 +0200 Subject: [PATCH 238/274] Actually, it's 289B. --- src/sound/snd_adlibgold.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 4c4c17341..870e473b0 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -1063,7 +1063,7 @@ adgold_init(UNUSED(const device_t *info)) adgold->surround_enabled = device_get_config_int("surround"); adgold->gameport_enabled = device_get_config_int("gameport"); - fm_driver_get(FM_YMF289, &adgold->opl); + fm_driver_get(FM_YMF289B, &adgold->opl); if (adgold->surround_enabled) ym7128_init(&adgold->ym7128); From d29b0d51833584fd47a6111fd95bce1c6e456311 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Wed, 24 Sep 2025 17:59:29 +0600 Subject: [PATCH 239/274] Set up default surface formats before QApplication creation (#6221) --- src/qt/qt_main.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index a48fde924..d667cfdb9 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -542,6 +542,17 @@ main(int argc, char *argv[]) #endif QApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + QSurfaceFormat fmt = QSurfaceFormat::defaultFormat(); + fmt.setSwapInterval(0); + fmt.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); + fmt.setRenderableType(QSurfaceFormat::OpenGL); +#ifdef Q_OS_MACOS + fmt.setVersion(4, 1); +#else + fmt.setVersion(3, 2); +#endif + QSurfaceFormat::setDefaultFormat(fmt); + QApplication app(argc, argv); QLocale::setDefault(QLocale::C); setlocale(LC_NUMERIC, "C"); @@ -573,9 +584,6 @@ main(int argc, char *argv[]) Q_INIT_RESOURCE(qt_resources); Q_INIT_RESOURCE(qt_translations); - QSurfaceFormat fmt = QSurfaceFormat::defaultFormat(); - fmt.setSwapInterval(0); - QSurfaceFormat::setDefaultFormat(fmt); #ifdef __APPLE__ CocoaEventFilter cocoafilter; From 01c410479b57b9fdcbc7fbd68b00ac9aec8766a3 Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 24 Sep 2025 15:16:59 +0200 Subject: [PATCH 240/274] RTL8019AS and ISA PnP fixes: fixes PNPODI.COM. RTL8019AS now detects the card but thinks there's an IRQ conflict. --- src/device/isapnp.c | 60 ++++++++- src/include/86box/isapnp.h | 30 +++-- src/mem/nmc93cxx.c | 1 + src/network/net_dp8390.c | 2 +- src/network/net_ne2000.c | 256 +++++++++++++++++++++++++++++-------- 5 files changed, 274 insertions(+), 75 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 64f6a1d5f..675d9adf7 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -25,6 +25,7 @@ #include <86box/io.h> #include <86box/isapnp.h> #include <86box/plat_unused.h> +#include "cpu.h" #define CHECK_CURRENT_LD() \ if (!ld) { \ @@ -42,6 +43,11 @@ const uint8_t isapnp_init_key[32] = { 0x6A, 0xB5, 0xDA, 0xED, 0xF6, 0xFB 0xDF, 0x6F, 0x37, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0x58, 0x2C, 0x16, 0x8B, 0x45, 0xA2, 0xD1, 0xE8, 0x74, 0x3A, 0x9D, 0xCE, 0xE7, 0x73, 0x39 }; +/* Required for the RTL8019AS. */ +const uint8_t isapnp_init_key2[32] = { 0xDA, 0x6D, 0x36, 0x1B, 0x8D, 0x46, 0x23, 0x91, + 0x48, 0xA4, 0xD2, 0x69, 0x34, 0x9A, 0x4D, 0x26, + 0x13, 0x89, 0x44, 0xA2, 0x51, 0x28, 0x94, 0xCA, + 0x65, 0x32, 0x19, 0x0C, 0x86, 0x43, 0xA1, 0x50 }; static const device_t isapnp_device; #ifdef ENABLE_ISAPNP_LOG @@ -85,11 +91,13 @@ typedef struct _isapnp_card_ { uint8_t enable; uint8_t state; uint8_t csn; + uint8_t csnsav; uint8_t ld; uint8_t id_checksum; uint8_t serial_read; uint8_t serial_read_pair; uint8_t serial_read_pos; + uint8_t is_rt; uint8_t *rom; uint16_t rom_pos; uint16_t rom_size; @@ -109,6 +117,7 @@ typedef struct _isapnp_card_ { typedef struct { uint8_t in_isolation; + uint8_t using_key2; uint8_t reg; uint8_t key_pos : 5; uint16_t read_data_addr; @@ -166,7 +175,7 @@ isapnp_device_config_changed(isapnp_card_t *card, isapnp_device_t *ld) } for (uint8_t i = 0; i < 8; i++) { reg_base = 0x60 + (2 * i); - if (ld->regs[0x31] & 0x02) + if (!(ld->regs[0x30] & 0x01) && (ld->regs[0x31] & 0x02)) card->config.io[i].base = 0; /* let us handle I/O range check reads */ else card->config.io[i].base = (ld->regs[reg_base] << 8) | ld->regs[reg_base + 1]; @@ -288,7 +297,7 @@ isapnp_read_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint case 0x01: /* Serial Isolation */ card = dev->first_card; while (card) { - if (card->enable && card->rom && (card->state == PNP_STATE_ISOLATION)) + if (card->enable && (card->rom != NULL) && (card->state == PNP_STATE_ISOLATION)) break; card = card->next; } @@ -374,6 +383,7 @@ vendor_defined: default: if (reg >= 0x30) { + CHECK_CURRENT_CARD(); CHECK_CURRENT_LD(); isapnp_log("ISAPnP: Read register %02X from CSN %02X device %02X\n", reg, card->csn, ld->number); ret = ld->regs[reg]; @@ -397,7 +407,8 @@ isapnp_read_data(UNUSED(uint16_t addr), void *priv) card = card->next; } - isapnp_log("ISAPnP: read_data() => "); + // isapnp_log("ISAPnP: read_data() => "); + isapnp_log("[%04X:%08X] ISAPnP: read_data() => ", CS, cpu_state.pc); return isapnp_read_common(dev, card, dev->current_ld, dev->reg); } @@ -432,12 +443,14 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) if (card->state == PNP_STATE_WAIT_FOR_KEY) { /* checking only the first card should be fine */ /* Check written value against LFSR key. */ - if (val == isapnp_init_key[dev->key_pos]) { + if ((val == isapnp_init_key[dev->key_pos]) || (val == isapnp_init_key2[dev->key_pos])) { + dev->using_key2 = (val == isapnp_init_key2[dev->key_pos]); dev->key_pos++; if (!dev->key_pos) { isapnp_log("ISAPnP: Key unlocked, putting cards to SLEEP\n"); while (card) { - if (card->enable && (card->enable != ISAPNP_CARD_NO_KEY) && (card->state == PNP_STATE_WAIT_FOR_KEY)) + int is_rt = (!dev->using_key2 || card->is_rt); + if (card->enable && is_rt && (card->enable != ISAPNP_CARD_NO_KEY) && (card->state == PNP_STATE_WAIT_FOR_KEY)) card->state = PNP_STATE_SLEEP; card = card->next; } @@ -493,6 +506,7 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin card->state = PNP_STATE_WAIT_FOR_KEY; card = card->next; } + dev->key_pos = 0; } if (val & 0x04) { isapnp_log("ISAPnP: Reset CSN\n"); @@ -566,6 +580,20 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin ld->regs[reg] = val & 0x01; isapnp_device_config_changed(card, ld); + for (uint8_t i = 0; i < 8; i++) { + if (!ld->io_len[i]) + continue; + + io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; + if (val & 0x01) { + if (ld->regs[0x31] & 0x02) + io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); + } else { + if (ld->regs[0x31] & 0x02) + io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); + } + } + break; case 0x31: /* I/O Range Check */ @@ -578,7 +606,7 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; if (ld->regs[reg] & 0x02) io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); - if (val & 0x02) + if ((val & 0x02) && !(ld->regs[0x30] & 0x01)) io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); } @@ -601,6 +629,7 @@ vendor_defined: default: if (reg >= 0x40) { + CHECK_CURRENT_CARD(); CHECK_CURRENT_LD(); isapnp_log("ISAPnP: Write %02X to register %02X on CSN %02X device %02X\n", val, reg, card->csn, ld->number); @@ -670,7 +699,8 @@ isapnp_write_data(UNUSED(uint16_t addr), uint8_t val, void *priv) } } - isapnp_log("ISAPnP: write_data(%02X) => ", val); + // isapnp_log("ISAPnP: write_data(%02X) => ", val); + isapnp_log("[%04X:%08X] ISAPnP: write_data(%02X) => ", CS, cpu_state.pc, val); isapnp_write_common(dev, card, dev->current_ld, dev->reg, val); } @@ -1163,6 +1193,22 @@ isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg) return isapnp_read_common(device_get_priv(&isapnp_device), card, ld, reg); } +void +isapnp_set_rt(void *priv, uint8_t is_rt) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + + card->is_rt = is_rt; +} + +uint8_t * +isapnp_get_csnsav(void *priv) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + + return &card->csnsav; +} + void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val) { diff --git a/src/include/86box/isapnp.h b/src/include/86box/isapnp.h index 6f7eb1185..2ffbd2d66 100644 --- a/src/include/86box/isapnp.h +++ b/src/include/86box/isapnp.h @@ -57,19 +57,21 @@ typedef struct isapnp_device_config_t { extern const uint8_t isapnp_init_key[32]; -void *isapnp_add_card(uint8_t *rom, uint16_t rom_size, - void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv), - void (*csn_changed)(uint8_t csn, void *priv), - uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), - void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), - void *priv); -void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size); -void isapnp_enable_card(void *priv, uint8_t enable); -void isapnp_set_csn(void *priv, uint8_t csn); -uint8_t isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg); -void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val); -void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config); -void isapnp_reset_card(void *priv); -void isapnp_reset_device(void *priv, uint8_t ld); +extern void *isapnp_add_card(uint8_t *rom, uint16_t rom_size, + void (*config_changed)(uint8_t ld, isapnp_device_config_t *config, void *priv), + void (*csn_changed)(uint8_t csn, void *priv), + uint8_t (*read_vendor_reg)(uint8_t ld, uint8_t reg, void *priv), + void (*write_vendor_reg)(uint8_t ld, uint8_t reg, uint8_t val, void *priv), + void *priv); +extern void isapnp_update_card_rom(void *priv, uint8_t *rom, uint16_t rom_size); +extern void isapnp_enable_card(void *priv, uint8_t enable); +extern void isapnp_set_csn(void *priv, uint8_t csn); +extern uint8_t isapnp_read_reg(void *priv, uint8_t ldn, uint8_t reg); +extern void isapnp_write_reg(void *priv, uint8_t ldn, uint8_t reg, uint8_t val); +extern void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp_device_config_t *config); +extern void isapnp_reset_card(void *priv); +extern void isapnp_reset_device(void *priv, uint8_t ld); +extern void isapnp_set_rt(void *priv, uint8_t is_rt); +extern uint8_t *isapnp_get_csnsav(void *priv); #endif /*EMU_ISAPNP_H*/ diff --git a/src/mem/nmc93cxx.c b/src/mem/nmc93cxx.c index 17d97591e..a0d24b872 100644 --- a/src/mem/nmc93cxx.c +++ b/src/mem/nmc93cxx.c @@ -29,6 +29,7 @@ #include <86box/nmc93cxx.h> #include <86box/plat_unused.h> +#define ENABLE_NMC93CXX_EEPROM_LOG 3 #ifdef ENABLE_NMC93CXX_EEPROM_LOG int nmc93cxx_eeprom_do_log = ENABLE_NMC93CXX_EEPROM_LOG; diff --git a/src/network/net_dp8390.c b/src/network/net_dp8390.c index 7f3490877..0bf3e9d3b 100644 --- a/src/network/net_dp8390.c +++ b/src/network/net_dp8390.c @@ -514,7 +514,7 @@ dp8390_page0_write(dp8390_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned le { uint8_t val2; - dp8390_log("DP839: Page0 write to register 0x%02x, value=0x%02x\n", + dp8390_log("DP8390: Page0 write to register 0x%02x, value=0x%02x\n", off, val); switch (off) { diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 0ce0190ac..d7e4e28a0 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -65,6 +65,7 @@ #include <86box/network.h> #include <86box/net_dp8390.h> #include <86box/net_ne2000.h> +#include <86box/nmc93cxx.h> #include <86box/isapnp.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> @@ -85,25 +86,26 @@ typedef struct nic_t { const char *name; - uint8_t pnp_csnsav; uint8_t pci_slot; uint8_t irq_state; - uint8_t pad; + uint8_t csnsav; /* RTL8019AS/RTL8029AS registers */ uint8_t config0; + uint8_t config1; uint8_t config2; uint8_t config3; uint8_t _9346cr; uint8_t pci_regs[PCI_REGSIZE]; - uint8_t eeprom[128]; /* for RTL8029AS */ uint8_t maclocal[6]; /* configured MAC (local) address */ /* POS registers, MCA boards only */ uint8_t pos_regs[8]; + uint8_t eeprom_data[128]; + int board; int is_pci; int is_mca; @@ -121,6 +123,9 @@ typedef struct nic_t { rom_t bios_rom; void *pnp_card; + uint8_t *pnp_csnsav; + + nmc93cxx_eeprom_t *eeprom; } nic_t; #ifdef ENABLE_NE2K_LOG @@ -144,7 +149,11 @@ nelog(int lvl, const char *fmt, ...) static void nic_interrupt(void *priv, int set) { - nic_t *dev = (nic_t *) priv; + nic_t *dev = (nic_t *) priv; + int enabled = 1; + + if (dev->board == NE2K_RTL8019AS_PNP) + enabled = dev->config1 & 0x80; if (dev->is_pci) { if (set) @@ -152,9 +161,12 @@ nic_interrupt(void *priv, int set) else pci_clear_irq(dev->pci_slot, PCI_INTA, &dev->irq_state); } else { - if (set) - picint(1 << dev->base_irq); - else + if (enabled && (dev->base_irq != 0x02)) { + if (set) + picint(1 << dev->base_irq); + else + picintc(1 << dev->base_irq); + } else if (dev->base_irq != 0x02) picintc(1 << dev->base_irq); } } @@ -236,6 +248,7 @@ asic_read(nic_t *dev, uint32_t off, unsigned int len) /* If all bytes have been written, signal remote-DMA complete */ if (dev->dp8390->remote_bytes == 0) { + nelog(3, "%s: DMA read: done (%i)\n", dev->name, dev->dp8390->IMR.rdma_inte); dev->dp8390->ISR.rdma_done = 1; if (dev->dp8390->IMR.rdma_inte) nic_interrupt(dev, 1); @@ -262,6 +275,11 @@ asic_write(nic_t *dev, uint32_t off, uint32_t val, unsigned len) dev->name, (unsigned) off, (unsigned) val); switch (off) { + default: /* this is invalid, but happens under win95 device detection */ + nelog(3, "%s: ASIC write invalid address %04x, ignoring\n", + dev->name, (unsigned) off); + break; + case 0x00: /* Data register - see asic_read for a description */ if ((len > 1) && (dev->dp8390->DCR.wdsize == 0)) { nelog(3, "%s: DMA write length %d on byte mode operation\n", @@ -299,11 +317,6 @@ asic_write(nic_t *dev, uint32_t off, uint32_t val, unsigned len) case 0x0f: /* Reset register */ /* end of reset pulse */ break; - - default: /* this is invalid, but happens under win95 device detection */ - nelog(3, "%s: ASIC write invalid address %04x, ignoring\n", - dev->name, (unsigned) off); - break; } } @@ -311,59 +324,137 @@ asic_write(nic_t *dev, uint32_t off, uint32_t val, unsigned len) static uint32_t page3_read(nic_t *dev, uint32_t off, UNUSED(unsigned int len)) { + uint8_t ret = 0x00; + if (dev->board >= NE2K_RTL8019AS_PNP) switch (off) { - case 0x1: /* 9346CR */ - return (dev->_9346cr); + default: + break; - case 0x3: /* CONFIG0 */ - return 0x00; /* Cable not BNC */ + case 0x1: /* 9346CR */ + ret = (dev->_9346cr & 0xfe); + + if (((ret & 0xc0) == 0x80) && nmc93cxx_eeprom_read(dev->eeprom)) + ret |= 0x01; + break; + + case 0x3: /* CONFIG0 */ + if (dev->board == NE2K_RTL8019AS_PNP) + ret = (dev->config0 & 0xc0) | 0x10; /* Cable not BNC */ + else + ret = 0x00; /* Cable not BNC */ + break; + + case 0x4: /* CONFIG1 */ + if (dev->board == NE2K_RTL8019AS_PNP) + ret = dev->config1; + break; case 0x5: /* CONFIG2 */ - return (dev->config2 & 0xe0); + if (dev->board == NE2K_RTL8019AS_PNP) + ret = dev->config2; + else + ret = (dev->config2 & 0xe0); + break; case 0x6: /* CONFIG3 */ - return (dev->config3 & 0x46); + if (dev->board == NE2K_RTL8019AS_PNP) + ret = dev->config3; + else + ret = (dev->config3 & 0x46); + break; case 0x8: /* CSNSAV */ - return ((dev->board == NE2K_RTL8019AS_PNP) ? dev->pnp_csnsav : 0x00); + ret = ((dev->board == NE2K_RTL8019AS_PNP) ? *dev->pnp_csnsav : 0x00); + break; + + case 0xb: /* INTR */ + if (dev->board == NE2K_RTL8019AS_PNP) { + ret = (pic2.irr & 0x02) ? 0x01 : 0x00; + ret |= (pic.irr & 0x08) ? 0x02 : 0x00; + ret |= (pic.irr & 0x10) ? 0x04 : 0x00; + ret |= (pic.irr & 0x20) ? 0x08 : 0x00; + ret |= (pic2.irr & 0x04) ? 0x10 : 0x00; + ret |= (pic2.irr & 0x08) ? 0x20 : 0x00; + ret |= (pic2.irr & 0x10) ? 0x40 : 0x00; + ret |= (pic2.irr & 0x80) ? 0x80 : 0x00; + } + break; + + case 0xd: /* CONFIG4 */ + if (dev->board == NE2K_RTL8019AS_PNP) { + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); + ret = data[0x03]; + } + break; case 0xe: /* 8029ASID0 */ if (dev->board == NE2K_RTL8029AS) - return 0x29; + ret = 0x29; break; case 0xf: /* 8029ASID1 */ if (dev->board == NE2K_RTL8029AS) - return 0x80; - break; - - default: + ret = 0x80; break; } - nelog(3, "%s: Page3 read register 0x%02x attempted\n", dev->name, off); - return 0x00; + nelog(3, "%s: Page3 read register 0x%02x, value=0x%04x\n", dev->name, off, ret); + return ret; } static void page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) { + int cfg_write_enable = ((dev->_9346cr & 0xc0) == 0xc0); + if (dev->board >= NE2K_RTL8019AS_PNP) { - nelog(3, "%s: Page2 write to register 0x%02x, len=%u, value=0x%04x\n", + nelog(3, "%s: Page3 write to register 0x%02x, len=%u, value=0x%04x\n", dev->name, off, len, val); switch (off) { case 0x01: /* 9346CR */ dev->_9346cr = (val & 0xfe); + if ((val & 0xc0) == 0x80) + nmc93cxx_eeprom_write(dev->eeprom, !!(val & 0x08), !!(val & 0x04), !!(val & 0x02)); + else if ((val & 0xc0) == 0x40) { + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); + + data[0x00] = 0x80; + data[0x01] = 0x00; + data[0x02] = 0x80; + data[0x03] = 0x00; + + dev->_9346cr = 0x21; + } + break; + + case 0x03: /* CONFIG0 */ + if (cfg_write_enable && (dev->board == NE2K_RTL8019AS_PNP)) + dev->config0 = (val & 0xc0); + break; + + case 0x04: /* CONFIG1 */ + if (cfg_write_enable && (dev->board == NE2K_RTL8019AS_PNP)) + dev->config1 = (dev->config1 & 0x7f) | (val & 0x80); break; case 0x05: /* CONFIG2 */ - dev->config2 = (val & 0xe0); + if (cfg_write_enable) { + if (dev->board == NE2K_RTL8019AS_PNP) + dev->config2 = val; + else + dev->config2 = (val & 0xe0); + } break; case 0x06: /* CONFIG3 */ - dev->config3 = (val & 0x46); + if (cfg_write_enable) { + if (dev->board == NE2K_RTL8019AS_PNP) + dev->config3 = val; + else + dev->config3 = (val & 0x46); + } break; case 0x09: /* HLTCLK */ @@ -491,6 +582,10 @@ static void nic_ioremove(nic_t *dev, uint16_t addr); static void nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) { + uint8_t irq_map[16] = { 0x00, 0x00, 0x00, 0x10, 0x20, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x40, 0x50, 0x60, 0x00, 0x00, 0x70 }; + uint8_t ios = 0x00; + if (ld) return; @@ -502,10 +597,22 @@ nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) } dev->base_address = config->io[0].base; - dev->base_irq = config->irq[0].irq; - if (config->activate && (dev->base_address != ISAPNP_IO_DISABLED)) + nic_interrupt(dev, 0); + dev->base_irq = config->irq[0].irq; + if ((dev->base_irq >= 0x00) && (dev->base_irq <= 0x0f)) + dev->config1 = (dev->config1 & 0x8f) | irq_map[dev->base_irq]; + else + dev->config1 = (dev->config1 & 0x8f); + + if (config->activate && (dev->base_address != ISAPNP_IO_DISABLED)) { nic_ioset(dev, dev->base_address); + ios |= (dev->base_address & 0x0100) ? 0x00 : 0x04; + ios |= (dev->base_address & 0x0080) ? 0x08 : 0x00; + ios |= (dev->base_address & 0x0040) ? 0x02 : 0x00; + ios |= (dev->base_address & 0x0020) ? 0x01 : 0x00; + dev->config1 = (dev->config1 & 0xf0) | ios; + } } static void @@ -513,7 +620,7 @@ nic_pnp_csn_changed(uint8_t csn, void *priv) { nic_t *dev = (nic_t *) priv; - dev->pnp_csnsav = csn; + *dev->pnp_csnsav = csn; } static uint8_t @@ -525,17 +632,21 @@ nic_pnp_read_vendor_reg(uint8_t ld, uint8_t reg, void *priv) const nic_t *dev = (nic_t *) priv; switch (reg) { - case 0xF0: - return dev->config0; + case 0xf0: /* CONFIG0 */ + return 0x00; /* Cable not BNC */ - case 0xF2: + case 0xf1: /* CONFIG1 */ + return dev->config1; + break; + + case 0xf2: /* CONFIG2 */ return dev->config2; - case 0xF3: + case 0xf3: /* CONFIG3 */ return dev->config3; - case 0xF5: - return dev->pnp_csnsav; + case 0xf5: + return *dev->pnp_csnsav; default: break; @@ -550,9 +661,9 @@ nic_pnp_write_vendor_reg(uint8_t ld, uint8_t reg, uint8_t val, void *priv) nic_t *dev = (nic_t *) priv; if ((ld == 0) && (reg == 0xf6) && (val & 0x04)) { - uint8_t csn = dev->pnp_csnsav; + uint8_t csn = *dev->pnp_csnsav; isapnp_set_csn(dev->pnp_card, 0); - dev->pnp_csnsav = csn; + *dev->pnp_csnsav = csn; } } @@ -913,6 +1024,7 @@ nic_init(const device_t *info) char *rom = NULL; nic_t *dev; int set_oui = 0; + int use_nvr = 0; dev = calloc(1, sizeof(nic_t)); dev->name = info->name; @@ -1144,26 +1256,33 @@ nic_init(const device_t *info) } /* Initialize the RTL80x9 EEPROM. */ - memset(dev->eeprom, 0x00, sizeof(dev->eeprom)); + memset(dev->eeprom_data, 0x00, sizeof(dev->eeprom_data)); if (dev->board == NE2K_RTL8029AS) { - memcpy(&dev->eeprom[0x02], dev->maclocal, 6); + dev->eeprom_data[0x00] = 0x00; + dev->eeprom_data[0x01] = 0x00; - dev->eeprom[0x76] = dev->eeprom[0x7A] = dev->eeprom[0x7E] = (PCI_DEVID & 0xff); - dev->eeprom[0x77] = dev->eeprom[0x7B] = dev->eeprom[0x7F] = (PCI_DEVID >> 8); - dev->eeprom[0x78] = dev->eeprom[0x7C] = (PCI_VENDID & 0xff); - dev->eeprom[0x79] = dev->eeprom[0x7D] = (PCI_VENDID >> 8); + memcpy(&dev->eeprom_data[0x02], dev->maclocal, 6); + + dev->eeprom_data[0x76] = dev->eeprom_data[0x7a] = dev->eeprom_data[0x7e] = (PCI_DEVID & 0xff); + dev->eeprom_data[0x77] = dev->eeprom_data[0x7b] = dev->eeprom_data[0x7f] = (PCI_DEVID >> 8); + dev->eeprom_data[0x78] = dev->eeprom_data[0x7c] = (PCI_VENDID & 0xff); + dev->eeprom_data[0x79] = dev->eeprom_data[0x7d] = (PCI_VENDID >> 8); + + use_nvr = 1; } else { const char *pnp_rom_file = NULL; int pnp_rom_len = 0x4a; switch (dev->board) { case NE2K_RTL8019AS_PNP: pnp_rom_file = "roms/network/rtl8019as/RTL8019A.BIN"; + use_nvr = 1; break; case NE2K_DE220P: pnp_rom_file = "roms/network/de220p/dlk2201a.bin"; - pnp_rom_len = 0x43; + pnp_rom_len = 0x43; + use_nvr = 1; break; default: @@ -1174,8 +1293,8 @@ nic_init(const device_t *info) if (pnp_rom_file) { FILE *fp = rom_fopen(pnp_rom_file, "rb"); if (fp) { - if (fread(&dev->eeprom[0x12], 1, pnp_rom_len, fp) == pnp_rom_len) - pnp_rom = &dev->eeprom[0x12]; + if (fread(&dev->eeprom_data[0x12], 1, pnp_rom_len, fp) == pnp_rom_len) + pnp_rom = &dev->eeprom_data[0x12]; fclose(fp); } } @@ -1183,10 +1302,22 @@ nic_init(const device_t *info) switch (info->local) { case NE2K_RTL8019AS_PNP: case NE2K_DE220P: - dev->pnp_card = isapnp_add_card(pnp_rom, pnp_rom_len, - nic_pnp_config_changed, nic_pnp_csn_changed, - nic_pnp_read_vendor_reg, nic_pnp_write_vendor_reg, - dev); + dev->pnp_card = isapnp_add_card(pnp_rom, pnp_rom_len, + nic_pnp_config_changed, nic_pnp_csn_changed, + nic_pnp_read_vendor_reg, nic_pnp_write_vendor_reg, + dev); + dev->pnp_csnsav = isapnp_get_csnsav(dev->pnp_card); + dev->config0 = 0x00; + dev->config1 = 0x80; + dev->config2 = 0x00; + dev->config3 = 0x80; + isapnp_set_rt(dev->pnp_card, 1); + + dev->eeprom_data[0x00] = 0x80; + dev->eeprom_data[0x01] = 0x00; + dev->eeprom_data[0x02] = 0x80; + dev->eeprom_data[0x03] = 0x01; + memcpy(&dev->eeprom_data[0x04], dev->maclocal, 6); break; default: @@ -1195,6 +1326,25 @@ nic_init(const device_t *info) } } + if (use_nvr) { + nmc93cxx_eeprom_params_t params; + char filename[1024] = { 0 }; + + params.nwords = 64; + params.default_content = (uint16_t *) dev->eeprom_data; + params.filename = filename; + int inst = device_get_instance(); + snprintf(filename, sizeof(filename), "nmc93cxx_eeprom_%s_%d.nvr", info->internal_name, inst); + dev->eeprom = device_add_inst_params(&nmc93cxx_device, inst, ¶ms); + if (dev->eeprom == NULL) { + free(dev); + return NULL; + } + } + + if (dev->pnp_csnsav == NULL) + dev->pnp_csnsav = &dev->csnsav; + if (dev->board != NE2K_ETHERNEXT_MC) /* Reset the board. */ nic_reset(dev); From f04346bf31b2559bb08718839746b06cf6ca47e9 Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 24 Sep 2025 15:19:24 +0200 Subject: [PATCH 241/274] 93c46: Disable excess logging. --- src/mem/nmc93cxx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mem/nmc93cxx.c b/src/mem/nmc93cxx.c index a0d24b872..17d97591e 100644 --- a/src/mem/nmc93cxx.c +++ b/src/mem/nmc93cxx.c @@ -29,7 +29,6 @@ #include <86box/nmc93cxx.h> #include <86box/plat_unused.h> -#define ENABLE_NMC93CXX_EEPROM_LOG 3 #ifdef ENABLE_NMC93CXX_EEPROM_LOG int nmc93cxx_eeprom_do_log = ENABLE_NMC93CXX_EEPROM_LOG; From 085b0eb30a4b87cfece3c04b3b9b76e5196f6990 Mon Sep 17 00:00:00 2001 From: OBattler Date: Wed, 24 Sep 2025 23:03:08 +0200 Subject: [PATCH 242/274] Temporarily force Qt 6 on. --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 29fedd6ad..4f10a1e56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,8 @@ if(VCPKG_TOOLCHAIN) set(USE_QT6 ON) endif() +set(USE_QT6 ON) + if(WIN32) # Prefer static builds on Windows set(PREFER_STATIC ON) From b2b5acfa3f91311b3d99add9a027e8b8f8c20824 Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 00:53:16 +0200 Subject: [PATCH 243/274] Revert the Qt 6 forcing. --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f10a1e56..29fedd6ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,8 +67,6 @@ if(VCPKG_TOOLCHAIN) set(USE_QT6 ON) endif() -set(USE_QT6 ON) - if(WIN32) # Prefer static builds on Windows set(PREFER_STATIC ON) From ae369dc34d8b9b43b112b1435dedaa4c18eb6070 Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 00:58:53 +0200 Subject: [PATCH 244/274] RTL8019AS and ISA PnP: More fixes. --- src/device/isapnp.c | 103 +++++++++++++++++++++++++++++-------- src/include/86box/isapnp.h | 3 ++ src/network/net_ne2000.c | 86 ++++++++++++++++++++++--------- 3 files changed, 147 insertions(+), 45 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 675d9adf7..46cd52c24 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -98,6 +98,8 @@ typedef struct _isapnp_card_ { uint8_t serial_read_pair; uint8_t serial_read_pos; uint8_t is_rt; + uint8_t normal; + uint8_t multiple_lds; uint8_t *rom; uint16_t rom_pos; uint16_t rom_size; @@ -449,15 +451,16 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) if (!dev->key_pos) { isapnp_log("ISAPnP: Key unlocked, putting cards to SLEEP\n"); while (card) { - int is_rt = (!dev->using_key2 || card->is_rt); - if (card->enable && is_rt && (card->enable != ISAPNP_CARD_NO_KEY) && (card->state == PNP_STATE_WAIT_FOR_KEY)) + int match_rt = (dev->using_key2 && card->is_rt); + int match_normal = (!dev->using_key2 && card->normal); + if (card->enable && (match_rt || match_normal) && + (card->enable != ISAPNP_CARD_NO_KEY) && (card->state == PNP_STATE_WAIT_FOR_KEY)) card->state = PNP_STATE_SLEEP; card = card->next; } } - } else { + } else dev->key_pos = 0; - } } } @@ -527,8 +530,22 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin if (card->csn == val) { card->rom_pos = 0; card->id_checksum = isapnp_init_key[0]; - if (card->state == PNP_STATE_SLEEP) + if (card->state == PNP_STATE_SLEEP) { card->state = (val == 0) ? PNP_STATE_ISOLATION : PNP_STATE_CONFIG; + + if (!card->multiple_lds) { + ld = card->first_ld; + while (ld) { + if (ld->number == 0x00) { + isapnp_log("ISAPnP: Select CSN %02X device 00\n", card->csn); + dev->current_ld_card = card; + dev->current_ld = ld; + break; + } + ld = ld->next; + } + } + } } else card->state = PNP_STATE_SLEEP; @@ -549,27 +566,28 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin break; case 0x07: /* Logical Device Number */ - CHECK_CURRENT_CARD(); + if (card->multiple_lds) { + CHECK_CURRENT_CARD(); - card->ld = val; - ld = card->first_ld; - while (ld) { - if (ld->number == val) { - isapnp_log("ISAPnP: Select CSN %02X device %02X\n", card->csn, val); - dev->current_ld_card = card; - dev->current_ld = ld; - break; + card->ld = val; + ld = card->first_ld; + while (ld) { + if (ld->number == val) { + isapnp_log("ISAPnP: Select CSN %02X device %02X\n", card->csn, val); + dev->current_ld_card = card; + dev->current_ld = ld; + break; + } + ld = ld->next; } - ld = ld->next; - } - if (!ld) { - isapnp_log("ISAPnP: CSN %02X has no device %02X, creating one\n", card->csn, val); - dev->current_ld_card = card; - dev->current_ld = isapnp_create_ld(card); - dev->current_ld->number = val; + if (!ld) { + isapnp_log("ISAPnP: CSN %02X has no device %02X, creating one\n", card->csn, val); + dev->current_ld_card = card; + dev->current_ld = isapnp_create_ld(card); + dev->current_ld->number = val; + } } - break; case 0x30: /* Activate */ @@ -759,11 +777,13 @@ isapnp_add_card(uint8_t *rom, uint16_t rom_size, isapnp_card_t *card = (isapnp_card_t *) calloc(1, sizeof(isapnp_card_t)); card->enable = 1; + card->normal = 1; card->priv = priv; card->config_changed = config_changed; card->csn_changed = csn_changed; card->read_vendor_reg = read_vendor_reg; card->write_vendor_reg = write_vendor_reg; + card->multiple_lds = 1; if (!dev->first_card) { dev->first_card = card; @@ -1201,6 +1221,45 @@ isapnp_set_rt(void *priv, uint8_t is_rt) card->is_rt = is_rt; } +void +isapnp_set_normal(void *priv, uint8_t normal) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + + card->normal = normal; +} + +void +isapnp_activate(void *priv, uint16_t base, uint8_t irq) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + isapnp_device_t *ld = card->first_ld; + + while (ld) { + if (ld->number == 0x00) + break; + ld = ld->next; + } + + if (ld != NULL) { + ld->regs[0x30] = 0x01; + ld->regs[0x60] = base >> 4; + if (!(ld->io_16bit & (1 << ((0x60 >> 1) & 0x07)))) + ld->regs[0x60] &= 0x03; + ld->regs[0x61] = base & 0x0f; + ld->regs[0x70] = irq; + } +} + +void +isapnp_set_single_ld(void *priv) +{ + isapnp_card_t *card = (isapnp_card_t *) priv; + + card->multiple_lds = 0; + card->ld = 0x00; +} + uint8_t * isapnp_get_csnsav(void *priv) { diff --git a/src/include/86box/isapnp.h b/src/include/86box/isapnp.h index 2ffbd2d66..7d730564a 100644 --- a/src/include/86box/isapnp.h +++ b/src/include/86box/isapnp.h @@ -72,6 +72,9 @@ extern void isapnp_set_device_defaults(void *priv, uint8_t ldn, const isapnp extern void isapnp_reset_card(void *priv); extern void isapnp_reset_device(void *priv, uint8_t ld); extern void isapnp_set_rt(void *priv, uint8_t is_rt); +extern void isapnp_set_normal(void *priv, uint8_t normal); +extern void isapnp_activate(void *priv, uint16_t base, uint8_t irq); +extern void isapnp_set_single_ld(void *priv); extern uint8_t *isapnp_get_csnsav(void *priv); #endif /*EMU_ISAPNP_H*/ diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index d7e4e28a0..2a9f3f551 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -69,6 +69,7 @@ #include <86box/isapnp.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> +#include "cpu.h" /* ROM BIOS file paths. */ #define ROM_PATH_NE1000 "roms/network/ne1000/ne1000.rom" @@ -340,7 +341,7 @@ page3_read(nic_t *dev, uint32_t off, UNUSED(unsigned int len)) case 0x3: /* CONFIG0 */ if (dev->board == NE2K_RTL8019AS_PNP) - ret = (dev->config0 & 0xc0) | 0x10; /* Cable not BNC */ + ret = dev->config0; else ret = 0x00; /* Cable not BNC */ break; @@ -418,20 +419,20 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) if ((val & 0xc0) == 0x80) nmc93cxx_eeprom_write(dev->eeprom, !!(val & 0x08), !!(val & 0x04), !!(val & 0x02)); else if ((val & 0xc0) == 0x40) { - uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); - data[0x00] = 0x80; - data[0x01] = 0x00; - data[0x02] = 0x80; - data[0x03] = 0x00; + dev->config1 = (data[0x00] & 0x7f) | 0x80; + dev->config2 = (data[0x01] & 0xdf); + dev->config3 = (data[0x02] & 0x77) | 0x80; + dev->_9346cr = 0x21; - dev->_9346cr = 0x21; + isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); } break; case 0x03: /* CONFIG0 */ if (cfg_write_enable && (dev->board == NE2K_RTL8019AS_PNP)) - dev->config0 = (val & 0xc0); + dev->config0 = (dev->config0 & 0x3f) | (val & 0xc0); break; case 0x04: /* CONFIG1 */ @@ -442,7 +443,7 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) case 0x05: /* CONFIG2 */ if (cfg_write_enable) { if (dev->board == NE2K_RTL8019AS_PNP) - dev->config2 = val; + dev->config2 = (dev->config2 & 0x1f) | (val & 0xe0); else dev->config2 = (val & 0xe0); } @@ -451,7 +452,7 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) case 0x06: /* CONFIG3 */ if (cfg_write_enable) { if (dev->board == NE2K_RTL8019AS_PNP) - dev->config3 = val; + dev->config3 = (dev->config3 & 0xf9) | (val & 0x06); else dev->config3 = (val & 0x46); } @@ -582,9 +583,11 @@ static void nic_ioremove(nic_t *dev, uint16_t addr); static void nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) { +#if 0 uint8_t irq_map[16] = { 0x00, 0x00, 0x00, 0x10, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x60, 0x00, 0x00, 0x70 }; uint8_t ios = 0x00; +#endif if (ld) return; @@ -600,18 +603,22 @@ nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) nic_interrupt(dev, 0); dev->base_irq = config->irq[0].irq; +#if 0 if ((dev->base_irq >= 0x00) && (dev->base_irq <= 0x0f)) dev->config1 = (dev->config1 & 0x8f) | irq_map[dev->base_irq]; else dev->config1 = (dev->config1 & 0x8f); +#endif if (config->activate && (dev->base_address != ISAPNP_IO_DISABLED)) { nic_ioset(dev, dev->base_address); +#if 0 ios |= (dev->base_address & 0x0100) ? 0x00 : 0x04; ios |= (dev->base_address & 0x0080) ? 0x08 : 0x00; ios |= (dev->base_address & 0x0040) ? 0x02 : 0x00; ios |= (dev->base_address & 0x0020) ? 0x01 : 0x00; dev->config1 = (dev->config1 & 0xf0) | ios; +#endif } } @@ -626,33 +633,38 @@ nic_pnp_csn_changed(uint8_t csn, void *priv) static uint8_t nic_pnp_read_vendor_reg(uint8_t ld, uint8_t reg, void *priv) { - if (ld != 0) - return 0x00; + uint8_t ret = 0x00; const nic_t *dev = (nic_t *) priv; - switch (reg) { + if (ld == 0) switch (reg) { + default: + break; + case 0xf0: /* CONFIG0 */ - return 0x00; /* Cable not BNC */ + ret = dev->config0; + break; case 0xf1: /* CONFIG1 */ - return dev->config1; + ret = dev->config1; break; case 0xf2: /* CONFIG2 */ - return dev->config2; + ret = dev->config2; + break; case 0xf3: /* CONFIG3 */ - return dev->config3; + ret = dev->config3; + break; case 0xf5: - return *dev->pnp_csnsav; - - default: + ret = *dev->pnp_csnsav; break; } - return 0x00; + nelog(3, "[R] Vendor register: %02X (LD = %02X) = %02X\n", reg, ld, ret); + + return ret; } static void @@ -660,6 +672,8 @@ nic_pnp_write_vendor_reg(uint8_t ld, uint8_t reg, uint8_t val, void *priv) { nic_t *dev = (nic_t *) priv; + nelog(3, "[W] Vendor register: %02X (LD = %02X) = %02X\n", reg, ld, val); + if ((ld == 0) && (reg == 0xf6) && (val & 0x04)) { uint8_t csn = *dev->pnp_csnsav; isapnp_set_csn(dev->pnp_card, 0); @@ -1307,7 +1321,7 @@ nic_init(const device_t *info) nic_pnp_read_vendor_reg, nic_pnp_write_vendor_reg, dev); dev->pnp_csnsav = isapnp_get_csnsav(dev->pnp_card); - dev->config0 = 0x00; + dev->config0 = 0x10 /* Cable not BNC */; dev->config1 = 0x80; dev->config2 = 0x00; dev->config3 = 0x80; @@ -1315,7 +1329,7 @@ nic_init(const device_t *info) dev->eeprom_data[0x00] = 0x80; dev->eeprom_data[0x01] = 0x00; - dev->eeprom_data[0x02] = 0x80; + dev->eeprom_data[0x02] = 0x81; dev->eeprom_data[0x03] = 0x01; memcpy(&dev->eeprom_data[0x04], dev->maclocal, 6); break; @@ -1340,6 +1354,32 @@ nic_init(const device_t *info) free(dev); return NULL; } + if (info->local == NE2K_RTL8019AS_PNP) { + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); + + dev->config1 = (data[0x00] & 0x7f) | 0x80; + dev->config2 = (data[0x01] & 0xdf); + dev->config3 = (data[0x02] & 0xf7); + + isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); + isapnp_set_single_ld(dev->pnp_card); + + if (!(dev->config3 & 0x01)) { + uint8_t irq_map[8] = { 9, 3, 4, 5, 10, 11, 12, 15 }; + dev->base_address = 0x0000; + + dev->base_irq = irq_map[(dev->config1 >> 4) & 0x07]; + + dev->base_address = (dev->config1 & 0x01) ? 0x0020 : 0x0000; + dev->base_address |= (dev->config1 & 0x02) ? 0x0040 : 0x0000; + dev->base_address |= (dev->config1 & 0x04) ? 0x0000 : 0x0100; + dev->base_address |= (dev->config1 & 0x08) ? 0x0080 : 0x0000; + + nic_ioset(dev, dev->base_address); + + isapnp_activate(dev->pnp_card, dev->base_address, dev->base_irq); + } + } } if (dev->pnp_csnsav == NULL) From 6819b2c3d0a8b3c0c0a2b27b703e06a7095d8d73 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Thu, 25 Sep 2025 01:01:09 +0200 Subject: [PATCH 245/274] Revert "Merge branch 'master' into master" This reverts commit dbafb3e8a4f9bd8a4ed387e8d72dc68b869a4764, reversing changes made to f04346bf31b2559bb08718839746b06cf6ca47e9. --- CMakeLists.txt | 13 +- CMakePresets.json | 5 +- doc/nvidia_notes/2025-01-24.txt | 1 - .../B = BUFFER. REMEMBER THESE!!!.txt | 1 - .../How to optimise riva 128 applications.txt | 6 - doc/nvidia_notes/Memory map (RIVA 128).txt | 81 - doc/nvidia_notes/NV3 DMA Engine.txt | 54 - doc/nvidia_notes/NV_PFB_CONFIG_0.txt | 29 - doc/nvidia_notes/PFIFO RAMHT RAMRO.txt | 38 - doc/nvidia_notes/RIVA fansites.txt | 26 - doc/nvidia_notes/Versions.txt | 8 - doc/nvidia_notes/gpucompanies.txt | 63 - doc/nvidia_notes/hardware cursor.txt | 21 - doc/nvidia_notes/multithreading.pdn | Bin 106011 -> 0 bytes doc/nvidia_notes/multithreading.png | Bin 117148 -> 0 bytes doc/nvidia_notes/nv3 driver init status.txt | 112 - .../nv3 driver init status_2025-02-10.txt | 90 - doc/nvidia_notes/nv3_object_classes.txt | 37 - doc/nvidia_notes/nv3d3d_t.txt | 161 - doc/nvidia_notes/old nouveau wiki.txt | 9 - doc/nvidia_notes/rivatv_riva128.txt | 2238 --------- doc/nvidia_notes/status.xlsx | Bin 15945 -> 0 bytes .../86box/nv/classes/vid_nv3_classes.h | 1179 ----- src/include/86box/nv/render/vid_nv3_render.h | 64 - src/include/86box/nv/vid_nv.h | 178 - src/include/86box/nv/vid_nv1.h | 167 - src/include/86box/nv/vid_nv3.h | 1764 ------- src/include/86box/nv/vid_nv4.h | 149 - src/include/86box/nv/vid_nv4_defines.h | 4291 ----------------- src/include/86box/utils/video_stdlib.h | 23 - src/include/86box/video.h | 7 - src/qt/CMakeLists.txt | 10 +- src/qt/qt_gpudebug_visualnv.cpp | 177 - src/qt/qt_gpudebug_visualnv.hpp | 46 - src/qt/qt_gpudebug_visualnv.ui | 213 - src/qt/qt_gpudebug_vram.cpp | 54 - src/qt/qt_gpudebug_vram.hpp | 40 - src/qt/qt_gpudebug_vram.ui | 42 - src/qt/qt_mainwindow.cpp | 98 +- src/qt/qt_mainwindow.hpp | 15 +- src/qt/qt_mainwindow.ui | 78 +- src/sound/snd_emu8k.c | 2 +- src/sound/snd_sb.c | 4 +- src/utils/CMakeLists.txt | 3 - src/utils/log.c | 19 +- src/utils/video/video_rop.c | 790 --- src/video/CMakeLists.txt | 71 +- src/video/nv/nv1/nv1_core.c | 110 - src/video/nv/nv1/nv1_core_config.c | 133 - .../nv3/classes/nv3_class_001_beta_factor.c | 50 - src/video/nv/nv3/classes/nv3_class_002_rop.c | 44 - .../nv/nv3/classes/nv3_class_003_chroma_key.c | 54 - .../nv/nv3/classes/nv3_class_004_plane_mask.c | 40 - .../nv3_class_005_clipping_rectangle.c | 49 - .../nv/nv3/classes/nv3_class_006_pattern.c | 86 - .../nv/nv3/classes/nv3_class_007_rectangle.c | 69 - .../nv/nv3/classes/nv3_class_008_point.c | 40 - src/video/nv/nv3/classes/nv3_class_009_line.c | 40 - src/video/nv/nv3/classes/nv3_class_00a_lin.c | 41 - .../nv/nv3/classes/nv3_class_00b_triangle.c | 40 - .../classes/nv3_class_00c_win95_gdi_text.c | 285 -- src/video/nv/nv3/classes/nv3_class_00d_m2mf.c | 94 - .../nv3_class_00e_scaled_image_from_mem.c | 40 - src/video/nv/nv3/classes/nv3_class_010_blit.c | 60 - .../nv/nv3/classes/nv3_class_011_image.c | 67 - .../nv/nv3/classes/nv3_class_012_bitmap.c | 41 - .../classes/nv3_class_014_transfer2memory.c | 41 - .../nv3_class_015_stretched_image_from_cpu.c | 40 - .../nv3_class_017_d3d5_tri_zeta_buffer.c | 40 - .../classes/nv3_class_018_point_zeta_buffer.c | 42 - .../classes/nv3_class_01c_image_in_memory.c | 99 - src/video/nv/nv3/classes/nv3_class_names.c | 67 - .../nv/nv3/classes/nv3_class_shared_methods.c | 67 - src/video/nv/nv3/nv3_core.c | 1567 ------ src/video/nv/nv3/nv3_core_arbiter.c | 213 - src/video/nv/nv3/nv3_core_config.c | 240 - src/video/nv/nv3/render/nv3_render_blit.c | 229 - src/video/nv/nv3/render/nv3_render_core.c | 869 ---- .../nv/nv3/render/nv3_render_primitives.c | 355 -- src/video/nv/nv3/subsystems/nv3_pbus.c | 255 - src/video/nv/nv3/subsystems/nv3_pbus_dma.c | 274 -- src/video/nv/nv3/subsystems/nv3_pextdev.c | 161 - src/video/nv/nv3/subsystems/nv3_pfb.c | 204 - src/video/nv/nv3/subsystems/nv3_pfifo.c | 985 ---- src/video/nv/nv3/subsystems/nv3_pgraph.c | 632 --- src/video/nv/nv3/subsystems/nv3_pmc.c | 274 -- src/video/nv/nv3/subsystems/nv3_pme.c | 136 - src/video/nv/nv3/subsystems/nv3_pramdac.c | 458 -- src/video/nv/nv3/subsystems/nv3_pramin.c | 515 -- .../nv/nv3/subsystems/nv3_pramin_ramfc.c | 40 - .../nv/nv3/subsystems/nv3_pramin_ramht.c | 56 - .../nv/nv3/subsystems/nv3_pramin_ramro.c | 40 - src/video/nv/nv3/subsystems/nv3_ptimer.c | 227 - src/video/nv/nv3/subsystems/nv3_pvideo.c | 159 - src/video/nv/nv3/subsystems/nv3_user.c | 70 - src/video/nv/nv4/nv4_core.c | 388 -- src/video/nv/nv4/nv4_core_config.c | 92 - src/video/nv/nv4/nv4_core_io.c | 1283 ----- src/video/nv/nv4/nv4_debug_register_list.c | 46 - src/video/nv/nv4/subsystems/nv4_pramdac.c | 84 - src/video/nv/nv4/subsystems/nv4_ptimer.c | 227 - src/video/nv/nv_base.c | 107 - src/video/vid_table.c | 7 - src/video/vid_voodoo.c | 2 +- 104 files changed, 38 insertions(+), 24413 deletions(-) delete mode 100644 doc/nvidia_notes/2025-01-24.txt delete mode 100644 doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt delete mode 100644 doc/nvidia_notes/How to optimise riva 128 applications.txt delete mode 100644 doc/nvidia_notes/Memory map (RIVA 128).txt delete mode 100644 doc/nvidia_notes/NV3 DMA Engine.txt delete mode 100644 doc/nvidia_notes/NV_PFB_CONFIG_0.txt delete mode 100644 doc/nvidia_notes/PFIFO RAMHT RAMRO.txt delete mode 100644 doc/nvidia_notes/RIVA fansites.txt delete mode 100644 doc/nvidia_notes/Versions.txt delete mode 100644 doc/nvidia_notes/gpucompanies.txt delete mode 100644 doc/nvidia_notes/hardware cursor.txt delete mode 100644 doc/nvidia_notes/multithreading.pdn delete mode 100644 doc/nvidia_notes/multithreading.png delete mode 100644 doc/nvidia_notes/nv3 driver init status.txt delete mode 100644 doc/nvidia_notes/nv3 driver init status_2025-02-10.txt delete mode 100644 doc/nvidia_notes/nv3_object_classes.txt delete mode 100644 doc/nvidia_notes/nv3d3d_t.txt delete mode 100644 doc/nvidia_notes/old nouveau wiki.txt delete mode 100644 doc/nvidia_notes/rivatv_riva128.txt delete mode 100644 doc/nvidia_notes/status.xlsx delete mode 100644 src/include/86box/nv/classes/vid_nv3_classes.h delete mode 100644 src/include/86box/nv/render/vid_nv3_render.h delete mode 100644 src/include/86box/nv/vid_nv.h delete mode 100644 src/include/86box/nv/vid_nv1.h delete mode 100644 src/include/86box/nv/vid_nv3.h delete mode 100644 src/include/86box/nv/vid_nv4.h delete mode 100644 src/include/86box/nv/vid_nv4_defines.h delete mode 100644 src/include/86box/utils/video_stdlib.h delete mode 100644 src/qt/qt_gpudebug_visualnv.cpp delete mode 100644 src/qt/qt_gpudebug_visualnv.hpp delete mode 100644 src/qt/qt_gpudebug_visualnv.ui delete mode 100644 src/qt/qt_gpudebug_vram.cpp delete mode 100644 src/qt/qt_gpudebug_vram.hpp delete mode 100644 src/qt/qt_gpudebug_vram.ui delete mode 100644 src/utils/video/video_rop.c delete mode 100644 src/video/nv/nv1/nv1_core.c delete mode 100644 src/video/nv/nv1/nv1_core_config.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_001_beta_factor.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_002_rop.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_003_chroma_key.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_004_plane_mask.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_006_pattern.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_007_rectangle.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_008_point.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_009_line.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_00a_lin.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_00b_triangle.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_00d_m2mf.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_010_blit.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_011_image.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_012_bitmap.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_names.c delete mode 100644 src/video/nv/nv3/classes/nv3_class_shared_methods.c delete mode 100644 src/video/nv/nv3/nv3_core.c delete mode 100644 src/video/nv/nv3/nv3_core_arbiter.c delete mode 100644 src/video/nv/nv3/nv3_core_config.c delete mode 100644 src/video/nv/nv3/render/nv3_render_blit.c delete mode 100644 src/video/nv/nv3/render/nv3_render_core.c delete mode 100644 src/video/nv/nv3/render/nv3_render_primitives.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pbus.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pbus_dma.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pextdev.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pfb.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pfifo.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pgraph.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pmc.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pme.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pramdac.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pramin.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramht.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pramin_ramro.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_ptimer.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_pvideo.c delete mode 100644 src/video/nv/nv3/subsystems/nv3_user.c delete mode 100644 src/video/nv/nv4/nv4_core.c delete mode 100644 src/video/nv/nv4/nv4_core_config.c delete mode 100644 src/video/nv/nv4/nv4_core_io.c delete mode 100644 src/video/nv/nv4/nv4_debug_register_list.c delete mode 100644 src/video/nv/nv4/subsystems/nv4_pramdac.c delete mode 100644 src/video/nv/nv4/subsystems/nv4_ptimer.c delete mode 100644 src/video/nv/nv_base.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ba7e9a75..29fedd6ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,15 +139,7 @@ option(DISCORD "Discord Rich Presence support" option(DEBUGREGS486 "Enable debug register opeartion on 486+ CPUs" OFF) option(LIBASAN "Enable compilation with the addresss sanitizer" OFF) -if (NV_LOG) - add_compile_definitions(ENABLE_NV_LOG) -endif() - -if (NV_LOG_ULTRA) - add_compile_definitions(ENABLE_NV_LOG_ULTRA) -endif() - -if((ARCH STREQUAL "arm64") OR (ARCH STREQUAL "arm")) +if((ARCH STREQUAL "arm64")) set(NEW_DYNAREC ON) else() option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF) @@ -192,12 +184,9 @@ cmake_dependent_option(PCL "Generic PCL5e Printer" cmake_dependent_option(SIO_DETECT "Super I/O Detection Helper" ON "DEV_BRANCH" OFF) cmake_dependent_option(WACOM "Wacom Input Devices" ON "DEV_BRANCH" OFF) cmake_dependent_option(XL24 "ATI VGA Wonder XL24 (ATI-28800-6)" ON "DEV_BRANCH" OFF) -cmake_dependent_option(NV3 "NVidia RIVA 128/128ZX (NV3/NV3T)" ON "DEV_BRANCH" OFF) - cmake_dependent_option(NETSWITCH "Network Switch Support" ON "DEV_BRANCH" OFF) cmake_dependent_option(VFIO "Virtual Function I/O" ON "DEV_BRANCH" OFF) - # Ditto but for Qt if(QT) option(USE_QT6 "Use Qt6 instead of Qt5" OFF) diff --git a/CMakePresets.json b/CMakePresets.json index 53ddffc74..be6615088 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -35,10 +35,7 @@ { "name": "debug", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "NV_LOG": "ON", - "NV_LOG_ULTRA": "ON" - + "CMAKE_BUILD_TYPE": "Debug" }, "inherits": "base" }, diff --git a/doc/nvidia_notes/2025-01-24.txt b/doc/nvidia_notes/2025-01-24.txt deleted file mode 100644 index e35818dc0..000000000 --- a/doc/nvidia_notes/2025-01-24.txt +++ /dev/null @@ -1 +0,0 @@ -THIS IS FIFOSERVICE!!!!!!!!!!!! \ No newline at end of file diff --git a/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt b/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt deleted file mode 100644 index e8f183240..000000000 --- a/doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt +++ /dev/null @@ -1 +0,0 @@ -B = BUFFER. REMEMBER THESE!!! \ No newline at end of file diff --git a/doc/nvidia_notes/How to optimise riva 128 applications.txt b/doc/nvidia_notes/How to optimise riva 128 applications.txt deleted file mode 100644 index 004593a21..000000000 --- a/doc/nvidia_notes/How to optimise riva 128 applications.txt +++ /dev/null @@ -1,6 +0,0 @@ -How to optimise riva 128 applications: -* Ensure any set of polygons with one texture is close to a multiple of 128 polygons. -* Try to sort areas of a model with one texture to as close to 128 polygons as possible for efficient submission due to the lack of texturing. -* Try to have around (32*128) for nv3 or (64*128) for nv3t polygons -* Get coding -* Alcohol \ No newline at end of file diff --git a/doc/nvidia_notes/Memory map (RIVA 128).txt b/doc/nvidia_notes/Memory map (RIVA 128).txt deleted file mode 100644 index fbfe9074f..000000000 --- a/doc/nvidia_notes/Memory map (RIVA 128).txt +++ /dev/null @@ -1,81 +0,0 @@ -Memory map (RIVA 128) - -32 megabytes of MMIO starting at 0x0000000-0x01FFFFFF - -MC (Master Control) 0x00000000-0x00000FFF - Config/Boot 0x00000000-0x000000FF - Master Config 0x00000100-0x00000FFF - Also used to define DMA channel IDs? - -MPU-401 I/O 0x00000330-0x00000331 Probably NV1 leftover, NV1 has mpu401 emulation -- no audio in nv3 -VGA emulated ports 0x000003B0-0x000003DF Emulated I/O ports for VGA - -Bus Control (PBUS) 0x00001000-0x00001FFF -FIFO (PFIFO) 0x00002000-0x00003FFF Submit starting at 0x00800000. Used to configure RAMHT,RAMFC,RAMRO structures & cache - -PRM 0x00004000-0x00005FFF Realmode DOS device support - -PRMIO 0x00007000-0x00007FFF Realmode access to PCI BAR (Base Address Register) + PCI I/O -PTIMER 0x00009000-0x00009FFF Timer - -VGA emulation 0x0000A000-0x0000BFFF - -VGA vram emulation 0x000A0000-0x000BFFFF (PRMVGA) -VGA 0x000C0000-0x000C7FFF VGA sequencer + graph controller registers (PRMVIO) - -(All of this up to 0x0000FFFF can be traced) - -PFB (Framebuffer) 0x00100000-0x00100FFF Interface to vram -PEXTDEV 0x00101000-0x00101FFF External device interface - contains straps - Straps 0x00101000 11 bits of cfg - -ROM (VBIOS?) 0x00110000-0x0011FFFF -PALT (?) 0x00120000-0x00120FFF - -PEXTDEV 0x00101000-0x00101FFF External Devices - -Media Engine (PME) 0x00200000-0x00200FFF Allows for external video capture according to envytools? - -PGRAPH (3D rendering) 0x00400000-0x00401FFF - -PGRAPH objects (using RAMHT???) -*i assume that when you submit an object these are the registers used to actually draw the current object - -Beta blending factor 0x00410000-0x00411FFF -ROP 0x00420000-0x00421FFF Global bitwise operation (Render OPeration) for filtering the final pixel -Color Key 0x00430000-0x00431FFF -Plane Switch 0x00440000-0x00441FFF Something to do with color formats and objects? -Clipping 0x00450000-0x00451FFF -Blend Pattern 0x00460000-0x00461FFF Used for specific blending modes -Quad [OBSOLETE] 0x00470000-0x00471FFF A rectangle. NV1 LEFTOVER, OBSOLETE -Point 0x00480000-0x00481FFF A single point -Line 0x00490000-0x00491FFF A line (with an optional colour). Can also draw a polygon made out of lines - polyline -Lin 0x004A0000-0x004A1FFF A line, without starting or ending pixel (with an optional colour). Can also draw a "polylin" -Triangle [OBSOLETE] 0x004B0000-0x004B1FFF A triangle. NV1 LEFTOVER, OBSOLETE? -Win95 GDI text 0x004C0000-0x004C1FFF Win95 text acceleration -Memory to memory xfer 0x004D0000-0x004D1FFF Represents a memory to memory transfer -Scaled image from vram 0x004E0000-0x004E1FFF Scaled image from GPU VRAM -Image blit from vram 0x00500000-0x00501FFF Image from GPU VRAM -Image blit from cpu 0x00510000-0x00511FFF Image from CPU -Bitmap from cpu 0x00520000-0x00521FFF Bitmap from CPU -Image to memory 0x00540000-0x00541FFF Image to GPU VRAM -Stretch image from cpu 0x00550000-0x00551FFF Stretched image from CPU -Direct3D 5.0 triangle 0x00570000-0x00571FFF A triangle optimised explicitly for directx3/directx5 rendering - supercedes UTRI -PointZ 0x00580000-0x00581FFF A single point with "zeta factor" (not sure what this is yet) -Image in memory 0x005C0000-0x005C0FFF Image in vram(?) - -PVIDEO (Video Control) 0x00680000-0x006802FF -External DAC 0x00680000-0x006800FF - -PRMCIO 0x00601000-0x00601FFF VGA emulation - CRTC + attribute controller - -PRAMDAC 0x00680300-0x00680FFF (used for color lookup tables, hardware cursor, video overlay, PLL for clocking and pixel generation) -"USER" DAC 0x00681200-0x00681FFF - -DMA submission w/FIFO 0x00800000-0x00FFFFFF NV_USER - uses FIFO buffer - -PNVM / PDFB / VRAM 0x01000000-0x017FFFFF (actual VRAM amount depends on card, but max is 8MB) - -RAMIN 0x01C00000-0x01FFFFFF (note that this is actually mapped in the last 1mb of vram) - -contains ramht that has obj parameters for submission, configurable \ No newline at end of file diff --git a/doc/nvidia_notes/NV3 DMA Engine.txt b/doc/nvidia_notes/NV3 DMA Engine.txt deleted file mode 100644 index 380e0281e..000000000 --- a/doc/nvidia_notes/NV3 DMA Engine.txt +++ /dev/null @@ -1,54 +0,0 @@ -NV3 DMA Engine -(DirectDraw Driver) - -Initially set CACHES, CACHE1_PULL0, CACHE1_PULL1, CACHE1_DMA0 to 1 - -Same for other areas - -CACHE1_PUSH1 contains CHID - -If it's different: - -If RmFifoFlushContext failed: Do nothing - -Set PULL0, PUSH0, Caches to 1, return false - - -If it's not: -DMA TLB PTE seems to be 1 for direct programming, maybe RM does it differently -Tag=FFFFFFFF -CACHE1_DMA1 - Number of bytes to send -CACHE1_DMA2 - Get offset -CACHE1_DMA3 - Bus address space (Area BAR0 mapped to? Or bar1?) - -TO START: -To set up DMA for for Cache1 Puller: CACHE1_PULL0 -> 1, changes to 0 when done -To set up DMA Cache1 Push: CACHE1_PUsh0 -> 1, changes to 0 when done -Set CACHES to 1 - -GO: Set DMA0 to 1 - -***** Implementation in Driver ****** - -You can dma to "localvidmem:", "sysvidmem:" or "sysmem:", this is represented by a driver - -CAUSE OF FAILURE: -the pfifo is never free because it never processes the submitted objects -which means that the FIFO is never free -which means that the drivers spin forever waiting for the fifo to be free - -DMA_OBJECT STRUCTURE IN RESOURCE MANAGER: -0x328: Valid -0x34c: base address? -0x374: actually do the transfer - -some objects don't actually need to do dma, for example, video patchcord, it just creates a structure in the driver, and rop just allocate ssome memory to put the data for the patchcord/rop thing - -dma start=nv3_mini dc67 -call of mthdCreate for ***DRIVER*** CLASS ID: a7b44, check ptr - - - - - - diff --git a/doc/nvidia_notes/NV_PFB_CONFIG_0.txt b/doc/nvidia_notes/NV_PFB_CONFIG_0.txt deleted file mode 100644 index dee3b646c..000000000 --- a/doc/nvidia_notes/NV_PFB_CONFIG_0.txt +++ /dev/null @@ -1,29 +0,0 @@ -NV_PFB_CONFIG_0 - -Observed Valus: -Drivers 0x1000 -BIOS 0x1114 - -Bits -5:0 Resolution -9:8 Pixel depth -12:12 Tiling -13:13 Tiling Debug -14:14 Tiling Debug Tile Size -17:15 "Tetris" tiling -19:18 "Tetris" tiling shift -22:20 Bank Swap -23 Unused - -NV_PFB_CONFIG_1 -2:0 CAS Latency -3:3 NEC Mode (PC-98?) -7:4 RAS Default / 9 Cycles -10:8 RAS PCHG -14:12 RAS Low -18:16 MRS to RAS -22:20 Write to Read -26:24 RAS to CAS -30:28 Read to Write -31:31 Read to PCFg - diff --git a/doc/nvidia_notes/PFIFO RAMHT RAMRO.txt b/doc/nvidia_notes/PFIFO RAMHT RAMRO.txt deleted file mode 100644 index 9b4c94718..000000000 --- a/doc/nvidia_notes/PFIFO RAMHT RAMRO.txt +++ /dev/null @@ -1,38 +0,0 @@ -PIO VS dma - -driver is DIRECTLY modifying pfifo - -8X8 channel setup - -Names are 32-bit integers >4096 - -RAMFC - DMA Context object 0,0 to 8,8 - -context = channel, render object, object type, offset in instance memory - -for a rectangle (type 0x47), object render = 1, channel 0, at 0x0400 in the ramht memory - -=0x00c70400 as the context - -the ramht hash : - -xor every byte of the hash individually and then xor that with the channel number - -so obj id 01020304 in channel 0 is 1 xor 2 xor 3 xor 4 xor 0 - -Store in RAMHT at + 4*16 = name -Store in RAMHT at + 4*16 + 4 = context - -then the ramin stuff starts at *0xc04000 since c00000 is the start of ramin [PCI BAR1] which is where you put the contents of the class struct - -nv_user - -Consider the 8x8 channels as 64 subchannels. -Now you can do: - -They seem to end at 0x880000 - -(0x880000)/64 = 0x2000 for each channel - -FAILURE -> RAMRO! - diff --git a/doc/nvidia_notes/RIVA fansites.txt b/doc/nvidia_notes/RIVA fansites.txt deleted file mode 100644 index d1cfaeb90..000000000 --- a/doc/nvidia_notes/RIVA fansites.txt +++ /dev/null @@ -1,26 +0,0 @@ -RIVA fansites (?????): -RIVA User's Group http://tiger.tnstate.edu:8080/ -RIVA 128 Homepage http://pages.prodigy.net/babblin5/Main.html -> Riva3D (riva3d.com) -Zone 128 http://www.tc.umn.edu/~reda0003/zone128/ -RIVAZone https://web.archive.org/web/19981212032348/http://www.rivazone.com/ (Launched January 2, 1998) -Dimension 128 http://dimension128.smartcom.net (early domain name that was mostly not archived) -> d128.com (1999-2001) -Riva3D https://web.archive.org/web/20000525110305/http://riva3d.gxnetwork.com/s3.html -nVNews https://web.archive.org/web/20001205171202/http://www.nvnews.net/ (1999-2015) - -BluesNews has stuff https://www.bluesnews.com/archives/ -(July 1996-present!) - -https://www.bluesnews.com/archives/july97-3.html July 21, 1997 ("unreleased nvidia RIVA chipset is indeed faster than 3dfx" (carmack) - -"However, using the nvidia RIVA 128 chip it runs 1 f/s faster" - -https://web.archive.org/web/19980615024744/http://www.ogr.com/columns/techtalk/technology_talk_0611_2.shtml First mention of NV10 (June 1998) - -"RIVA 128 Turbo" early ZX name -"Riva4" early TNT name - -https://web.archive.org/web/20001002193706/http://www.rivazone.com/files/rivalog.txt - -https://web.archive.org/web/20010422044820/http://www.rivazone.com/finger/finger.cgi?nick@finger.nvidia.com nvidia .plan files - -WAVE Report - april 1997 date for tapeout \ No newline at end of file diff --git a/doc/nvidia_notes/Versions.txt b/doc/nvidia_notes/Versions.txt deleted file mode 100644 index 052de854f..000000000 --- a/doc/nvidia_notes/Versions.txt +++ /dev/null @@ -1,8 +0,0 @@ -Name Base Version Notes Date API Support Platform -0.75_nt4 Version 0.75 No Resource Manager (miniport) 1997-08-15 GDI NT4.0 -0.77_win9x Version 0.77 Symbols (COFF/VXD) 1997-09-02 GDI, D3D5 -nv3quake.zip Version 1.21 OpenGL Alpha 1 1997-11-14 GDI, D3D5, OpenGL 1.1 (alpha; Build 151) Win9x -quakea2f.zip Version 1.21 OpenGL Alpha 2 1997-12-02 GDI, D3D5, OpenGL 1.1 (alpha; Build 258) Win9x -win95_131.zip Version 1.31 OpenGL Beta 1 1998-02-04 GDI, D3D5, OpenGL 1.1 (beta; Build 661) Win9x - - diff --git a/doc/nvidia_notes/gpucompanies.txt b/doc/nvidia_notes/gpucompanies.txt deleted file mode 100644 index 08a6c61c5..000000000 --- a/doc/nvidia_notes/gpucompanies.txt +++ /dev/null @@ -1,63 +0,0 @@ -GPU companies (the period they made gpus) - -DEC 19xx-1998 -HP 19xx-2000+ (possibly until present) -IBM 19xx-2002+ (possibly until present) -E&S 1968-2006 -Intergraph 1969-2000 -Apple 1976-present (some break) -Motorola 1977-1994+ (???) -TI 1979-1988+ -Matrox 1979-2014 -Hitachi 1981-1986 -SGI 1981-2009 -Intel 1983-present -Number Nine 1983-2000 -Tseng Labs 1983-1997 -Cirrus Logic 1984-1998 -Video 7 1985-1991 -C&T 1985-1999 -Imagination 1985-present -NCR 1986-1993 -Paradise/WD 1986-1996 -Gemini 1987-1990 -Genoa Systems 1987-1991 -Trident 1987-2003 -Oak Technology 1988-1997 -Realtek 1988-1997 -Compaq 1989-1991 -Sun 1989-2002(+?) -S3 Graphics 1989-2010 -Macronix 1989-1998 -Winbond 1989-1996+ -Tamarack 198x-1991+ -UMC 198x-1993 -Sigma Designs 198x-1996 -Acer 198x-1998 (roughly) -Fujitsu 198x-1998 -AMD 1991-present (really starting in 2006) -HMC 1991-1994 -Avance Logic 1991-1995 -Weitek <1991-1996 -Bitboys 1991-2006 -IIT 1992-1994 -Weitek 1992-1996(?) -Rendition 1993-1998 -ARK Logic 1993-1999 -S-MOS/Stellar 1994-1999 -Nvidia 1993-present -Alliance 1994-1997 -iGS 1994-1999 -3dfx 1994-2000 -3dlabs 1994-2006 -Silicon Motion 1995-200? -SiS 1995-2007 -Dynamic Pictures1996-1997 -NeoMagic 1996-2000 -GigaPixel 1997-2000 -Philips 1997-199x -Tatung 199x-199x -ASPEED 2004-present -Jingjia 2006-present - - diff --git a/doc/nvidia_notes/hardware cursor.txt b/doc/nvidia_notes/hardware cursor.txt deleted file mode 100644 index 0845778f4..000000000 --- a/doc/nvidia_notes/hardware cursor.txt +++ /dev/null @@ -1,21 +0,0 @@ -NV3/NV3T/NV4 hardware cursor - -Unlock extended CRTC registers - -CIO_CRE_HCUR_ADDR0 - Bits [6:0] = Address - -CIO_CRE_HCUR_ADDR1 - Bits [7:3] = Bits [11:7] of address - Bit 1 = Cursor Doubling - Bit 0 = Enable - -PRAMDAC_CU_START_POS (MMIO 0x680300) - Bits [11:0] = X Pos - Bits [27:16] = Y Pos - -CursorAddress >> 16 written to addr0 -(((CursorAddress >> 11) & 0x1F) << 3) | 1 (for enable) written to addr1 - -Lock extended CRTC registers -Enable - write \ No newline at end of file diff --git a/doc/nvidia_notes/multithreading.pdn b/doc/nvidia_notes/multithreading.pdn deleted file mode 100644 index 427d4bab81b03e52de811f80ca4dd777b63c366d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106011 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≠JI|LU&66?u>f>oZQ43 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

  • {w;*^ab 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 Zny0BMO^5#J*nq?bT^z2uOI1S2s@< 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 diff --git a/doc/nvidia_notes/nv3 driver init status.txt b/doc/nvidia_notes/nv3 driver init status.txt deleted file mode 100644 index d0d13bacd..000000000 --- a/doc/nvidia_notes/nv3 driver init status.txt +++ /dev/null @@ -1,112 +0,0 @@ -Driver version: Windows 2000 Version 3.37 (23 March 1999) - SDK: Windows 2000 build 1996.1 -Features: - -Resource Manager Yes -DirectDraw Yes -OpenGL Yes -Direct3D No - - -get started: -MUST USE X86 WINDBG -Does older windbg support coff??? - -sxe ld nv3_mini.sys -bp nv3_mini + 0x409ac - 0x10000 (nv3_mini+0x309ac) - -offset purpose -30a1a RmInitRm call - -6be7 initClientInfo call -6878 initClientInfo -6bec Check for initClientInfo success -6bf4 initGrPatchPool call -6bf9 Check for initGrPatchPool success -6c01 initDmaListElementPool call -6c06 Check for initDmaListElementPool -6c1c initDisplayInfo call - -6c26 rmInitRm End - Success: eax=FFFFFFFFh / -1 -6c26 Fail eax=0 - -30c8b NvFindAdapter -30cb6 NvFindAdapter -> NVIsPresent call -1010 NVIsPresent function -102f NVIsPresent VideoPortGetAccessRanges call -103b NVIsPresent VideoPortGetAccessRanges call success check (only possible way to fail) -127c NVIsPresent end -30cbb NvFindAdapter -> NVIsPresent success check - Success: al=1 - Failure: al=0 -30cca NVIsPresent NVMapMemoryRanges call -e9e NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #1 [PCI Space] -ea4 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #1 success check [PCI Space] -ebd NVIsPresent NVMapMemoryRanges VideoPortFreeDeviceBase [conditional] -ec3 NVIsPresent NVMapMemoryRanges VideoPortFreeDeviceBase [conditional] success check -ed6 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #2 [MMIO] -edc NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #2 success check [MMIO] -f0c NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #3 [LFB/RAMIN?] -f12 NVIsPresent NVMapMemoryRanges VideoPortGetDeviceBase call #3 success check [LFB/RAMIN?] - -30ccf NvFindAdapter NVMapMemoryRanges success check - Success: eax=0 - Failure: eax=87 -30cf1 NvFindAdapter RmInitNvMapping call -6ce6 NvFindAdapter RmInitNvMapping -30cf6 NVIsPresent RmInitNvMapping success check - Success: eax!=0 (in practice 0xFFFFFFFF/-1) - Failure: eax=0 -30d5c NvFindAdapter -30d64 NvFindAdapter RmPostNvDevice call -6d88 RmPostNvDevice function -6d91 NvFindAdapter DevinitInitializeDevice call -6d96 NvFindAdapter DevinitInitializeDevice success check - Success: eax=0 (?) - Failure: eax=1 -e546 DevinitInitializeDevice function - [very complicated] - [several register reads] -e61d DevinitPrepDeviceForInit call -e641 DevinitPrepDeviceForInit function -e627 InitNV call -e67a InitNV function - -30d64 NVIsPresent RmPostNvDevice success check - Success: eax=0 (?) - Failure: eax=1 -30d78 NVIsPresent NVGetNVInfo call -30d7d NVIsPresent NVGetNVInfo success check - -3e9a NvFindAdapter end - Success: eax=0 - Fail eax=55 (RmInitNvMapping or NVIsPresent failed) - Fail eax=87 (NVMapMemoryRanges or NVMapFrameBuffer failed) - -30ea3 NVInitialize -30f02 NVStartIO -2aa6 NVInterrupt -30a2d NVGetChildDescriptor -30a9c NVGetPowerState -30b20 NVSetPowerState - - -Driver Init Status: -DriverEntry Success -rmInitRm -> initClientInfo Success -rmInitRm -> initGrPatchPool Success -rmInitRm -> initDmaListElementPool Success -rmInitRm -> initDisplayInfo Success -rmInitRm overall Success -NvFindAdapter Success 17:32 27/11/2024 -NvFindAdapter -> NvIsPresent Success 16:19 24/11/2024 -NvFindAdapter -> NvMapMemoryRanges Success 19:15 26/11/2024 -NvFindAdapter -> RmInitNvMapping Success 19:18 26/11/2024 -NvFindAdapter -> RmPostNvDevice Success 17:32 27/11/2024 -NvFindAdapter -> NVGetNVInfo Success 17:32 27/11/2024 -NvFindAdapter -> NVMapFrameBuffer Success 17:32 27/11/2024 - -00:00 30/12/2024: stateGr -> i2c_Write -22:31 31/12/2024: fifoService - -fix ptimer issue \ No newline at end of file diff --git a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt b/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt deleted file mode 100644 index b5643a2ea..000000000 --- a/doc/nvidia_notes/nv3 driver init status_2025-02-10.txt +++ /dev/null @@ -1,90 +0,0 @@ -nv3_disp: - -0x10fe -> DrvBitBlt -0x19be -> DrvCopyBits -0x597c -> DrvCreateDeviceBitmap -0x5a6e -> DrvDeleteDeviceBitmap -0x60b8 -> DrvTextOut -0x6248 -> DrvDestroyFont -0x64b8 -> DrvRealizeBrush -0x6a46 -> DrvDitherColor -0x797a -> DrvGetDirectDrawInfo -0x7b14 -> DrvEnableDirectDraw -0x7b70 -> DrvDisableDirectDraw -0x817c -> DrvPaint -0x81c2 -> DrvResetPDEV -0x82dc -> DrvEnableDriver -0x8312 -> DrvEnablePDEV -0x83ee -> DrvDisablePDEV -0x840a -> DrvCompletePDEV -0x8418 -> DrvSynchronise -0x845a -> DrvEnableSurface -0x851a -> DrvDisableSurface -0x8554 -> DrvAssertMode -0x8690 -> DrvGetModes -0xe59a -> DrvEscape -0xf3ee -> DrvFillPath -0xf3f6 -> DrvStrokePath -0xfa08 -> DrvLineTo -0x12fee -> DrvSetPalette -0x132a4 -> DrvMovePointer -0x13d20 -> DrvSetPointerShape -0x13dea -> DrvStretchBlt -0x147f2 -> DrvSetPixelFormat -0x1483c -> DrvDescribePixelFormat -0x1495a -> DrvClipChanged -0x255b8 -> DrvSwapBuffers - -DrvEnableDriver SUCCESS -DrvEnablePDEV SUCCESS 23:28 09/02/2025 - Check for cjCaps >= 0x130 && cjDevInfo >= 0x12C SUCCESS 23:31 09/02/2025 - EngAllocMem call SUCCESS 23:38 09/02/2025 - CreateOglGlobalMemory call SUCCESS 23:40 09/02/2025 - bInitializeModeFields call SUCCESS 23:41 09/02/2025 - bInitializePalette call SUCCESS 23:42 09/02/2025 - EngDeviceIoControl IOCTL 0x232020 (CHECK mini) SUCCESS - EngDeviceIoControl IOCTL 0x232044 (CHECK mini) SUCCESS (eax=0) -DrvCompletePDEV SUCCESS 23:52 09/02/2025 -DrvEnableSurface - bEnableHardware call SUCCESS 22:36 13/02/2025 - EngCreateSemaphore call #1 csCrtc SUCCESS 00:55 10/02/2025 - EngCreateSemaphore call #2 csFifo SUCCESS 00:57 10/02/2025 - EngDeviceIoControl IOCTL 0x230460 SUCCESS 00:57 10/02/2025 - EngDeviceIoControl IOCTL 0x230458 SUCCESS 00:57 10/02/2025 - NvAllocRoot SUCCESS 01:03 10/02/2025 - NvAllocDevice SUCCESS 01:04 10/02/2025 - NV3/NV4 architecture check SUCCESS 01:14 10/02/2025 - bAssertModeHardware call (bEnable=1) SUCCESS Passing starting with build at 02:23 10/02/2025 - EngDeviceIoControl IOCTL 0x23040C] SUCCESS Passing starting with build at 02:23 10/02/2025 - nv3_mini NVStartIO ioctlcode=0x23040C - NVSetMode - NV3SetMode SUCCESS 01:53 10/02/2025 - RmUnloadState SUCCESS 01:48 10/02/2025 - VBESetModeEx SUCCESS 01:51 10/02/2025 - NV_OEMEnableExtensions SUCCESS 01:52 10/02/2025 - UpdateArbitrationSettings SUCCESS 01:52 10/02/2025 - RmLoadState SUCCESS 01:53 10/02/2025 - NV3EnableCursor SUCCESS 01:54 10/02/2025 - NV3WaitUntilFinished SUCCESS 02:23 10/02/2025 - EngDeviceIoControl IOCTL 0x230408 SUCCESS 02:26 10/02/2025 - EngDeviceIoControl IOCTL 0x232024 SUCCESS 02:26 10/02/2025 - NvAllocHardware SUCCESS 02:29 10/02/2025 - bCreateStdPatches(?) SUCCESS (EAX=1!!!) 22:24 13/02/2025 - CHECK - NV4 N/A - vDestroyStdPatches(?) N/A - NV3_WaitForOneVerticalRefresh SUCCESS - EngDeviceIoControl IOCTL 0x230410 SUCCESS - - - SET UP CORRECT FUNCTION POINTERS - Indirect call (call dword [edi]) to NV3_WaitWhileGraphicsEngineBusy HANG 22:14 16/02/2025 - _heap_init call - bEnableOffscreenHeap call - bEnablePointer call - bEnableText call - bEnablePalette call - bEnableDirectDraw call - EngCreateBitmap call - EngAssociateBitmap call - -DrvDisableSurface: ONLY IN THE CASE OF FAILURE diff --git a/doc/nvidia_notes/nv3_object_classes.txt b/doc/nvidia_notes/nv3_object_classes.txt deleted file mode 100644 index 05d1d9bf3..000000000 --- a/doc/nvidia_notes/nv3_object_classes.txt +++ /dev/null @@ -1,37 +0,0 @@ -Object classes as understood by the GPU. - -(May be represented, in RAMFC, as 0x40+ 22:16) - -0x00 = Invalid -0x01 = beta factor -0x02 = ROP5 operation -0x03 = Chroma key -0x04 = Plane mask -0x05 = Clipping rectangle -0x06 = Pattern -0x07 = Rectangle -0x08 = Point -0x09 = Line -0x0A = Lin (line without starting or ending pixel) -0x0B = Triangle -0x0C = Windows 95 GDI text acceleration -0x0D = Memory to memory format -0x0E = Scaled image from memory -0x0F = INVALID -0x10 = Blit -0x11 = Image -0x12 = Bitmap -0x13 = INVALID -0x14 = Transfer to Memory -0x15 = Stretched image from CPU -0x16 = INVALID -0x17 = Direct3D 5.0 accelerated textured triangle w/zeta buffer -0x18 = Point w/zeta buffer -0x19 = INVALID -0x1A = INVALID -0x1B = INVALID -0x1C = Image in memory -0x1D = INVALID -0x1E = INVALID -0x1F = INVALID - \ No newline at end of file diff --git a/doc/nvidia_notes/nv3d3d_t.txt b/doc/nvidia_notes/nv3d3d_t.txt deleted file mode 100644 index c8579fd6c..000000000 --- a/doc/nvidia_notes/nv3d3d_t.txt +++ /dev/null @@ -1,161 +0,0 @@ -12=unk_int12 -16=unk_short16 -22=unk_short22 (seems to determine if nv_sys_ptr is valid) -24=unk_short24 -26=unk_short26 -44=unk_int44 -56=unk_int56 -60=unk_int60 -64=unk_short64 -66=unk_short66 - -82=unk_short82 - -362..822: - big_struct - (weird alignment?) - 477 - 489 - 505 - -1140=nv_sys_ptr - - -1156=unk_int1156 [start of structure] -1160=unk_int1160 - -1168=unk_int1168 [possibly a byte array of up to 16 bytes) - -1184=unk_int1184 - -1208=unk_int1208 -1212=unk_int1212 -1216=unk_int1216 -1220=unk_int1220 -1224=unk_int1224 - -1236=unk_int1236 -1240=unk_int1240 -1244=unk_int1244 -1248=unk_int1248 - -1256=unk_int1256 -1260=fog_table_enable -1264=unk_int1264 -1270=unk_int1270 -1272=unk_byte1272 -1273=unk_byte1273 -1274=unk_byte1274 (?) -1275=unk_byte1275 - -1316=unk_int1316 -1320=unk_int1320 - -1332=unk_int1332 - -1340=unk_int1340 -1344=unk_int1344 - -1356=unk_int1356 -1360=d3d_clear_enabled -1364=texture_enabled -1368=mipmap_size_max -1372=mipmap_levels -1376=user_mipmaps -1380=zoh_mode (bool) -1384=tex_heap -1388=text_size -1392=video_texture -1396=min_video_tex_size -1400=draw_prim -1404=spread_x -1408=spread_y -1412=size_adj -1416=turbo_adj -1420=dma_min_push_count -1424=dma_push_enable - -1436=unk_int1436 -1440=unk_int1440 - -1448=unk_int1448 -1452=unk_int1452 (set to value of unk_int1908) -1456=unk_int1456 (set to value of unk_int1956) -1460=unk_int1460 (set to value of unk_int2020) - -1516=unk_int1516 -1520=unk_int1520 -1524=unk_int1524 -1528=unk_int1528 -1532=unk_int1532 - -1548=unk_int1548 -1552=unk_int1552 -1556=unk_int1556 -1560=unk_int1560 -1564=unk_int1564 - -1600=unk_int1600 - -1612=unk_int1612 -1616=unk_int1616 -1620=unk_int1620 - -1632=unk_int1632 - -1640=unk_int1640 -1644=unk_ptr1644 - -1676=unk_int1676 -1680=unk_int1680 -1684=unk_int1684 -1688=unk_int1688 -1692=unk_int1692 -1696=unk_int1696 -1700=unk_int1700 - -1716=unk_int1716 -1720=unk_int1720 -1724=unk_int1724 -1728=unk_ptr1728 - -1848=unk_int1848 -1852=unk_int1852 -1856=unk_int1856 (unk_int1552 | 0x800) -1864=unk_func_ptr1864 -1868=unk_int1868 - -1884=unk_int1884 -1888=ptr_to_start_of_structure? -1892=hInstDll -1896=unk_int1896 -1900=unk_int1900 - -1908=unk_int1908 -1912=unk_int1912 -1916=unk_func_ptr1916 -1920=unk_func_ptr1920 - -1932=unk_func_ptr1932 -1936=unk_func_ptr1936 -1944=unk_func_ptr1944 - -1956..2020: Set of function pointrs - 1956=unk_int1956 - 1960=unk_int1960 - 1960=unk_func_ptr1960 - 1964=unk_func_ptr1964 - 1968=unk_func_ptr1968 - 1976=unk_func_ptr1976 - 1980=unk_func_ptr1980 - 1988=unk_func_ptr1988 - 1996=unk_func_ptr1996 - 2000=unk_func_ptr2000 - 2004=unk_func_ptr2004 - 2008=unk_func_ptr2008 - - 2020=unk_int2020 - -2024=unk_int2024 -2028=unk_int2028 -2032=unk_int2032 diff --git a/doc/nvidia_notes/old nouveau wiki.txt b/doc/nvidia_notes/old nouveau wiki.txt deleted file mode 100644 index c875954fd..000000000 --- a/doc/nvidia_notes/old nouveau wiki.txt +++ /dev/null @@ -1,9 +0,0 @@ -old nouveau wiki: -real VRAM address = VRAM_size - (ramin_address - (ramin_address % reversal_unit_size)) - reversal_unit_size + (ramin_address % reversal_unit_size) - -nv3=16 bytes - -0x400000 - ((0x100000) - (0x100000 % 16)) - 16 + (0x100000 % 16) = 2ffff0 - - - \ No newline at end of file diff --git a/doc/nvidia_notes/rivatv_riva128.txt b/doc/nvidia_notes/rivatv_riva128.txt deleted file mode 100644 index 780ad5edd..000000000 --- a/doc/nvidia_notes/rivatv_riva128.txt +++ /dev/null @@ -1,2238 +0,0 @@ - - -Riva 128 documentation -====================== - -This document is based on kernel sources, XFree86 sources, open sources -released by nVidia and pure deduction. To make nVidia happy: - - /***************************************************************************\ -|* *| -|* Copyright (c) 1996-1998 NVIDIA, Corp. All rights reserved. *| -|* *| -|* NOTICE TO USER: The source code is copyrighted under U.S. and *| -|* international laws. NVIDIA, Corp. of Sunnyvale, California owns *| -|* the copyright and as design patents pending on the design and *| -|* interface of the NV chips. Users and possessors of this source *| -|* code are hereby granted a nonexclusive, royalty-free copyright *| -|* and design patent license to use this code in individual and *| -|* commercial software. *| -|* *| -|* Any use of this source code must include, in the user documenta- *| -|* tion and internal comments to the code, notices to the end user *| -|* as follows: *| -|* *| -|* Copyright (c) 1996-1998 NVIDIA, Corp. NVIDIA design patents *| -|* pending in the U.S. and foreign countries. *| -|* *| -|* NVIDIA, CORP. MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF *| -|* THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT *| -|* EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORP. DISCLAIMS *| -|* ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, INCLUDING ALL *| -|* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *| -|* PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA, CORP. BE LIABLE *| -|* FOR ANY SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, *| -|* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR *| -|* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER *| -|* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR *| -|* PERFORMANCE OF THIS SOURCE CODE. *| -|* *| - \***************************************************************************/ - -Feel free to send me corrections, updates, suggestions about this document to: -fero@drama.obuda.kando.hu or rivatv-devel@lists.sourceforge.net -Any kind of info, hint or idea may be helpful. -Please visit the rivatv homepage at: -http://fero.koli.kando.hu/rivatv/ - - - -Rendering -========= - -DMA vs PIO ----------- - -Theese two methods are used to program the hardware. DMA method provides -better performance but it is harder to implement. Almost all of the released -sources uses PIO method. The rest of this document describes the PIO method. - - -Objects -------- - -The Riva rendering engine is an object-oriented hardware. (See the nVidia -docs at https://www.nvidia.com/nv/nvarch.nsf/Home?OpenView -> Documents -> -NV Programmer's Reference Manual) Do not take it serious, it's simplified -for the hardware, it is not like C++. -Each object has a name. The name is 32 bits long and should be unique. - - -FIFO Channels -------------- - -There are 8 channels for submitting graphical objects to the rendering engine. -Each channel contains 8 subchannels. The rules how channels and subchannels -are working is not clear for me yet. Channel 0 is usable without extra coding. -Other channels can not be used until the 'context switching' code is done. -FIFO channels are located in the CTRL memory at 0x00800000-0x0087FFFF. Each -channel is 64 kbytes long, each subchannel is 8 kbytes long. Registers of -subchannels are variable: their function depends on the type of the submitted -object. Registers are write only and 32 bits long except the 'Free' register. - -Offset Name(s) Valid object types ------- ----------------------------- ------------------ -0000 Object all -0010 Free (2 bytes, read only!) all -0100 Synchronize t V s3 -0100 NoOperation BKPCLTRbISMr DG -0104 StopAlarm t -0104 Notify BKPCLTRbISMr DG -0104 StopImage V -0108 StopCursor V -010c StopDac V -0180 DmaNotifies tBKPCLTRbISMrVDGs3 -0184 ClipRectangle LTR -0184 ColorKey bIS -0184 DmaImage M V -0184 DmaTexture D -0184 Pattern G -0184 DmaSource s -0184 DmaSurfaces 3 -0188 Pattern LTR SM -0188 ClipRectangle bI D -0188 Rop G -0188 DmaDestin s -018c Rop LTR SM -018c Pattern bI -018c DmaLut V -018c Surfaces3D D -018c Beta1 G -0190 Beta1 LTR SM -0190 Rop bI -0190 Surfaces G -0194 Surfaces LTR SM -0194 Beta1 bI -0194 DmaCursor V -0198 Surfaces I -019c Surfaces b -02fc Operation LTRbIS G -02fc Get V -0300 Time0 t -0300 Beta1d31 B -0300 ColorFormat KP LTR ISM G -0300 Point C -0300 ControlPointIn b -0300 Rop r -0300 Image V -0300 Format s -0300 Pitch 3 -0304 Time1 t -0304 Color K LTR -0304 MonochromeFormat P G -0304 Size C -0304 ControlPointOut b -0304 Point I -0304 SizeIn S -0304 TextureOffset D -0304 Pitch s -0304 OffsetColor 3 -0300 Operation M -0308 AlarmNotify t -0308 MonochromeShape P -0308 Size b -0308 SizeOut I -0308 DxDu S -0308 ClipPoint M -0308 TextureFormat D -0308 OffsetSource s -0308 OffsetZeta 3 -030c SizeIn I -030c DyDv S -030c ClipSize M -030c TextureFilter D -030c OffsetDestin s -0310 MonochromeColor0 P -0310 TrianglePoint0 T -0310 ClipPoint S -0310 ImageOutPoint M -0310 FogColor D -0314 MonochromeColor1 P -0314 TrianglePoint1 T -0314 ClipSize S -0314 ImageOutSize M -0314 Control0 D -0318 MonochromePattern0 P -0318 TrianglePoint2 T -0318 Point12d4 S -0318 DxDu M -0318 Control1 D -031c MonochromePattern1 P -031c DyDv M -0320 Triangle32Point0X T -0324 Triangle32Point0Y T -0328 Triangle32Point1X T -032c Triangle32Point1Y T -0330 Triangle32Point2X T -0334 Triangle32Point2Y T -0340 Cursor V -0358 CursorPointOutA V -0380 Dac V -03a0 PixelClock V -03fc Color1A G -0400 Lin L -0400 Trimesh T -0400 Rectangle R -0400 Color IS -0400 ImageInSize M -0400 UnclippedRectangle G -0404 ImageInFormat M -0408 ImageInOffset M -040c ImageInPoint M -0480 Lin32 L -0480 Trimesh32 T -0500 PolyLin L -0500 ColorTriangle T -0580 PolyLin32 L -0580 ColorTrimesh T -0600 ColorPolyLin L -07f4 ClipPoint0B G -07f8 ClipPoint0B G -07fc Color1B G -0800 ClippedRectangle G -0bec ClipPoint0C G -0bf0 ClipPoint1C G -0bf4 Color1C G -0bf8 SizeC G -0bfc PointC G -0c00 MonochromeColor1C G -0fe8 ClipPoint0D G -0fec ClipPoint1D G -0ff0 Color1D G -0ff4 SizeInD G -0ff8 SizeOutD G -0ffc PointD G -1000 Tlvertex D -1000 MonochromeColor1D G -13e4 ClipPoint0E G -13e8 ClipPoint1E G -13ec Color0E G -13f0 Color1E G -13f4 SizeInE G -13f8 SizeOutE G -13fc PointE G -1400 MonochromeColor01E G - -t=timer -B=beta1 (0x41) -K=color key (0x43) -P=pattern (0x46) -C=clip rectangle (0x45) -L=solid line (0x4a) -T=solid triangle (0x4b) -R=solid rectangle (0x47) -b=image blit (0x50) -I=image from CPU (0x51) -S=stretched image from CPU (0x55) -M=scaled image from memory (0x4e) -r=raster operation (0x42) -V=video lut cursor dac -D=DX3 textured triangle (0x57) -G=GDI rectangle text (0x4c?) -s=Surfaces 2D -3=Surfaces 3D - -Contexts --------- - -Context is a 32 bit record of channel id, object type and pointer to the -object. - -Bits 24-30: channel id -Bit 23: rendering - 0x0 other object (DMA?) - 0x1 renderable object -Bits 16-22: object type - 0x41 beta1 - 0x42 raster operation - 0x43 color key - 0x44 plane - 0x45 clipping rectangle - 0x46 pattern - 0x47 solid rectangle - 0x49 unknown line - 0x4A solid line - 0x4B solid triangle - 0x4C GDI rectangle - 0x4E scaled image from memory - 0x50 blit - 0x51 image from CPU - 0x52 bitmap - 0x55 streched image from CPU - 0x57 DX3 textured triangle - 0x?? Surfaces 2D - 0x?? Surfaces 3D - 0x?? Timer - 0x?? Video lut cursor dac - and many more... -Bits 0-15: offset in instance memory (RAMIN) / 16 - -Each graphic object has a context. Context determines the type (triangle, -raster operation, bitmap, etc) of the object. Don't ask me why this thingy -is called context: I really don't know. - - -Hash table ----------- - -The hash table (HT) is located in the instance memory area (RAMIN). -See RAMHT register in section FIFO registers. -The HT contains 8 byte length records of object names and contexts. Each -record is placed in the table by it's hash key. The key is computed as -follows: each byte of the object's name and the channel id are XOR-ed -together, and this value is multiplied by the hash depth. More explanation -on hash tables can be found in database books. -The purpose of HT is to provide connection between object names and contexts -for the graphic hardware. But it may be useful for your software driver too. - - -FIFO context table ------------------- - -The FIFO context table (FC) is located in the instance memory area (RAMIN). -See RAMFC register in section FIFO registers. -FC contains the context values for each subchannel. E.g. if FC begins -at 0x1C00: -0x1C00 Context of object in channel 0, subchannel 0 -0x1C04 Context of object in channel 0, subchannel 1 -0x1C08 Context of object in channel 0, subchannel 2 -... -0x1C1C Context of object in channel 0, subchannel 7 -0x1C20 Context of object in channel 1, subchannel 0 -0x1C24 Context of object in channel 1, subchannel 1 -... -0x1CFC Context of object in channel 7, subchannel 7 - - -Example: How to render a solid rectangle? ------------------------------------------ - -- Choose a name for your rectangle. E.g.: 0x01020304 -- Select an unused area in the instance memory. Let it be 0x00c04000 -- Calculate the context: - Channel = 0x00 - Render object = 1 - Object type = 0x47 (rectangle) - Offset in intance memory = 0x4000 / 16 = 0x0400 - Context = 0x00c70400 -- Place the name - context pair in the hash table (HT begins at 0x00c00000): - Hash key = 01 xor 02 xor 03 xor 04 xor 00 = 0x04 - (each byte of the name) (channel id) - *(0x00c00000 + 0x04 * 16) = 0x00000001; // name - *(0x00c00000 + 0x04 * 16) + 4 = 0x00c70400; // context -- Fill in the instace ram: - *(0x00c04000) = 0x00100000; // 15bpp (TODO: decode this magic) - *(0x00c04004) = 0x00000000; // not used yet - *(0x00c04008) = 0x00000000; // not used yet - *(0x00c0400c) = 0x00000000; // not used yet -- At this point the FIFO channel is ready for submitting rectangles. -- Submit the first rectangle: - write the object's name to channel 0 (subchannel 0): - *(0x00800000) = 0x00000001; -- At this point subchannel 0 becomes a rectangle. -- Submit the attributes of the rectangle: - write x,y coordinates: - (0x00800400) = 0x01000200; // x=0x100 y=0x200 - size: - (0x00800404) = 0x00100020; // width=0x10 height=0x20 -- After size is submitted, our rectangle appears on the screen. -- Subsequent rectangles may be submitted by re-sending new attributes. - - -Speculation: How to make video in working? ------------------------------------------- - -V4L stuff and hardware rendered rectangles have some similarities: -- both have color keys -- both have clipping rectangles -- both are stretchable -I suppose video in feature is implemented by rendered objects. A chroma-keyed -and clipped rectangle with 'live texture' is blitted on the screen. -Thats all. :) - - - -Memory layout -============= - -Three memory regions are accessible via the PCI (or AGP) interface: -- CTRL region (PCI base 0) -- FB region (PCI base 1) -- BIOS ROM (PCI base 2) - - -CTRL region ------------ - -The CTRL region contains memory mapped IO ports. Most of them are 32-bit -registers. The registers are grouped by functionality: - -- MC: master control (0x00000000-0x00000FFF) -- BUS: (0x00001000-0x00001FFF) -- FIFO: for DMA and PIO stuff (0x00002000-0x00003FFF) -- TIMER: built-in timer (0x00009000-0x00009FFF) -- VIO: 8 bit SVGA (MISC, GRAPH, SEQ) registers (0x000C0000-0x00C00FFF) -- FB: framebuffer properties (0x00100000-0x00100FFF) -- EXTDEV: external devices (0x00101000-0x00101FFF) -- GRAPH: (0x00400000-0x00401FFF) -- PCIO: 8 bit SVGA (CRTC, ATTR) registers (0x00601000-0x00601FFF) -- RAMDAC: RAMDAC timings (0x00680000-0x00680FFF) -- PDIO: 8 bit registers (0x00681000-0x00681FFF) -- FIFO channels: (0x00800000-0x0087FFFF) -(List in not complete.) - - -FB region ---------- - -This region contains the video memory and the instance memory. -Video memory begins at offset 0x00000000. Instance memory (RAMIN) -begins at offset 0x00C00000. (Note: Riva TNT (and higher) cards have -their instance memory in the CTRL region.) - - - -Master control -============== - -The MC registers begin at 0x00000000 in the CTRL region. They control the -master IRQ. - -Interrupt 0 register --------------------- -Name: INTR_0 -Offset: 0x00000100 -4 bytes, read-write - -Bit 0: PAUDIO (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 4: PMEDIA (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 8: PFIFO (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 12: PGRAPH0 (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 13: PGRAPH1 (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 16: PVIDEO (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 20: PTIMER (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 24: PFB (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 28: PBUS (-V, read-only) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - -Bit 31: SOFTWARE (IV, read-write) - NOT_PENDING 0x0 (default, read-write) - PENDING 0x1 (read-write) - -Interrupt 0 enable register ---------------------------- -Name: INTR_EN_0 -Offset: 0x00000140 -4 bytes, read-write - -Bits 0-1: INTA (IV, read-write) - DISABLED 0x0 (default, read-write) - HARDWARE 0x1 (read-write) - SOFTWARE 0x2 (read-write) - -Enable (what?) register ------------------------ -Name: ENABLE -Offset: 0x00000200 -4 bytes, read-write - - - -Bus -=== - -The BUS registers begin at 0x00001000 in the CTRL region. They are -undiscovered. - -Debug 0 register ----------------- -Name: DEBUG_0 -Offset: 0x00001080 -4 bytes, read-write - -Bit 0: MODE (IV, read-write) - MODE_DISABLED 0x0 (default, read-write) - MODE_ENABLED 0x1 (read-write) - -Bit 4: DESKEWER (IV, read-write) - ENABLED 0x0 (default, read-write) - -Bits 8-11: FBIO_SCLK_DELAY (IV, read-write) - 0x0 (default, read-write) - -Bits 12-15: FBIO_FBCLK_DELAY (IV, read-write) - 0x3 (default, read-write) - -Debug 1 register ----------------- -Name: DEBUG_1 -Offset: 0x00001084 -4 bytes, read-write - -Bit 0: PCIM_THROTTLE (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 1: PCIM_CMD (IV, read-write) - SIZE_BASED 0x0 (default, read-write) - MRL_ONLY 0x1 (read-write) - -Bit 2: PCIM_AGP (IV, read-write) - IS_AGP 0x0 (default, read-write) - IS_PCI 0x1 (read-write) - -Bits 3-4: AGPM_CMD (IV, read-write) - HP_ON_1ST 0x0 (read-write) - LP_ONLY 0x1 (default, read-write) - HP_ONLY 0x2 (read-write) - -Bit 5: PCIS_WRITE (IV, read-write) - 0_CYCLE 0x0 (read-write) - 1_CYCLE 0x1 (default, read-write) - -Bit 6: PCIS_2_1 (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 7: PCIS_RETRY (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 8: PCIS_RD_BURST (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 9: PCIS_WR_BURST (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 10: PCIS_EARLY_RTY (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 11: PCIS_RMAIO (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 12: PCIS_CPUQ (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -Bit 13: DPSH_PIPE (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 14: SPARE1 (IV, read-write) - ZERO 0x0 (default, read-write) - ONE 0x1 (read-write) - -Bit 15: SPARE2 (IV, read-write) - ZERO 0x0 (default, read-write) - ONE 0x1 (read-write) - -Bit 16: SPARE3 (IV, read-write) - ZERO 0x0 (default, read-write) - ONE 0x1 (read-write) - - - -FIFO -==== - -The BUS registers begin at 0x00002000 in the CTRL region. - -Interrupt 0 register --------------------- - -Name: INTR_0 -Offset: 0x00002100 -4 bytes, read-write - -Bit 0: CACHE_ERROR (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 4: RUNOUT (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 8: RUNOUT_OVERFLOW (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 12: DMA_PUSHER (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 16: DMA_PTE (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Interrupt 0 enable register ---------------------------- - -Name: INTR_EN_0 -Offset: 0x00002140 -4 bytes, read-write - -Bit 0: CACHE_ERROR (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 4: RUNOUT (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 8: RUNOUT_OVERFLOW (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Config 0 register ------------------ - -Name: CONFIG_0 -Offset: 0x00002200 -4 bytes, read-write - -Hash table register -------------------- - -Hardware hash table offset (in instance memory) and size. - -Name: RAMHT -Offset: 0x00002210 -4 bytes, read-write - -Bits 12-15: BASE_ADDRESS (XV, read-write) - -Bits 16-17: SIZE (XV, read-write) - 4K 0x0 (default, read-write) - 8K 0x1 (read-write) - 16K 0x2 (read-write) - 32K 0x3 (read-write) - -FIFO context table offset -------------------------- - -Name: RAMFC -Offset: 0x00002214 -4 bytes, read-write - -Bits 9-15: BASE_ADDRESS (XV, read-write) - -Runout table register ---------------------- - -FIFO runout table offset and size. - -Name: RAMRO -Offset: 0x00002218 -4 bytes, read-write - -Bits 9-15: BASE_ADDRESS (XV, read-write) - -Bit 16: SIZE (XV, read-write) - 512 0x0 (default, read-write) - 8K 0x1 (read-write) - -Runout status register ----------------------- - -Name: RUNOUT_STATUS -Offset: 0x00002400 -4 bytes, read-only - -Runout put register -------------------- - -Index in runout table. - -Name: RUNOUT_PUT -Offset: 0x00002410 -4 bytes, read-write - -Runout get register -------------------- - -Index in runout table. - -Name: RUNOUT_GET -Offset: 0x00002420 -4 bytes, read-write - -Caches register ---------------- - -Name: CACHES -Offset: 0x00002500 -4 bytes, read-write - -Bit 0: REASSIGN (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Cache 0 push 0 register ------------------------ - -Name: CACHE0_PUSH0 -Offset: 0x00003000 -4 bytes, read-write - -Bit 0: ACCESS (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Cache 0 push 1 register ------------------------ - -Name: CACHE0_PUSH1 -Offset: 0x00003004 -4 bytes, read-write - -Bits 0-6: CHID (XU, read-write) - -Cache 0 pull 0 register ------------------------ - -Name: CACHE0_PULL0 -Offset: 0x00003040 -4 bytes, read-write - -Bit 0: ACCESS (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Cache 1 push 0 register ------------------------ - -Name: CACHE1_PUSH0 -Offset: 0x00003200 -4 bytes, read-write - -Bit 0: ACCESS (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Cache 1 push 1 register ------------------------ - -Name: CACHE1_PUSH1 -Offset: 0x00003204 -4 bytes, read-write - -Bits 0-6: CHID (XU, read-write) - -Cache 1 put register --------------------- - -Name: CACHE1_PUT -Offset: 0x00003210 -4 bytes, read-write - -Bits 2-6: ADDRESS (XU, read-write) - -Cache 1 DMA 0 register ------------------------ - -Name: CACHE1_DMA0 -Offset: 0x00003220 -4 bytes, read-write - -Cache 1 DMA 1 register ------------------------ - -Name: CACHE1_DMA1 -Offset: 0x00003224 -4 bytes, read-write - -Cache 1 DMA 2 register ------------------------ - -Name: CACHE1_DMA2 -Offset: 0x00003228 -4 bytes, read-write - -Cache 1 pull 0 register ------------------------ - -Name: CACHE1_PULL0 -Offset: 0x00003240 -4 bytes, read-write - -Bit 0: ACCESS (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Cache 1 pull 1 register ------------------------ - -Name: CACHE1_PULL1 -Offset: 0x00003250 -4 bytes, read-write - -Bit 4: CTX (XV, read-write) - CLEAN 0x0 (read-write) - DIRTY 0x1 (read-write) - -Cache 1 get register --------------------- - -Name: CACHE1_GET -Offset: 0x00003270 -4 bytes, read-write - -Bits 2-6: ADDRESS (XU, read-write) - -Cache 1 context registers -------------------------- - -8 context registers for each channel (I guess). - -Name: CACHE1_CTX -Offset: 0x00003280+i*16 (0 <= i < 8) -8*16(4?) bytes, read-write - - - -Framebuffer properties -====================== - -The FB registers begin at 0x00100000 in the CTRL region. - -Boot 0 register ---------------- - -Name: BOOT_0 -Offset: 0x00100000 -4 bytes, read-write - -Bits 0-1: RAM_AMOUNT (IV, read-write) - 1MB 0x0 (read-write) - 2MB 0x1 (read-write) - 4MB 0x2 (read-write) - 8MB 0x0 (read-write) - UNDEFINED 0x3 (read-write) - DEFAULT 0x2 (default, read-write) - -Bit 2: RAM_WIDTH_128 (-V, read-write) - OFF 0x0 (read-write) - ON 0x1 (read-write) - -Bit 3: RAM_BANKS (IV, read-write) - 2BANK 0x0 (default, read-write) - 4BANK 0x1 (read-write) - -Bit 4: RAMDATA_TWIDDLE (IV, read-write) - OFF 0x0 (default, read-write) - ON 0x1 (read-write) - -Bit 5: RAM_AMOUNT_EXTENSION (IV, read-write) - OFF 0x0 (default, read-write) - 8MB 0x1 (read-write) - -Delay 1 register ----------------- - -Name: DELAY_1 -Offset: 0x00100044 -4 bytes, read-write - -Bits 0-1: WRITE_ENABLE_RISE (IU, read-write) - WRITE_ENABLE_RISE_0 0x0 (default, read-write) - -Bits 4-5: WRITE_ENABLE_FALL (IU, read-write) - WRITE_ENABLE_FALL_0 0x0 (default, read-write) - -Bits 8-9: CAS_ENABLE_RISE (IU, read-write) - CAS_ENABLE_RISE_0 0x0 (default, read-write) - -Bits 12-13: CAS_ENABLE_FALL (IU, read-write) - CAS_ENABLE_FALL_0 0x0 (default, read-write) - -Bits 16-17: OUTPUT_DATA (IU, read-write) - OUTPUT_DATA_0 0x0 (default, read-write) - -Bits 20-21: RAS_ENABLE (IU, read-write) - RAS_ENABLE_0 0x0 (default, read-write) - -Debug 0 register ----------------- - -Name: DEBUG_0 -Offset: 0x00100080 -4 bytes, read-write - -Bit 4: REFRESH (IV, read-write) - ENABLED 0x0 (default, read-write) - DISABLED 0x1 (read-write) - -Green 0 register ----------------- - -Name: GREEN_0 -Offset: 0x001000C0 -4 bytes, read-write - -Bits 0-1: LEVEL (IV, read-write) - VIDEO_ENABLED 0x0 (read-write) - VIDEO_DISABLED 0x1 (read-write) - TIMING_DISABLED 0x2 (read-write) - MEMORY_DISABLED 0x3 (default, read-write) - -Config 0 register ------------------ - -Name: CONFIG_0 -Offset: 0x00100200 -4 bytes, read-write - -Bits 0-5: RESOLUTION (IV, read-write) - 320_PIXELS 0x0a (read-write) - 400_PIXELS 0x0d (read-write) - 480_PIXELS 0x0f (read-write) - 512_PIXELS 0x10 (read-write) - 640_PIXELS 0x14 (read-write) - 800_PIXELS 0x19 (read-write) - 960_PIXELS 0x1e (read-write) - 1024_PIXELS 0x20 (read-write) - 1152_PIXELS 0x24 (read-write) - 1280_PIXELS 0x28 (read-write) - 1600_PIXELS 0x32 (read-write) - DEFAULT 0x14 (default, read-write) - -Bits 8-9: PIXEL_DEPTH (IV, read-write) - 8_BITS 0x1 (read-write) - 16_BITS 0x2 (read-write) - 32_BITS 0x3 (read-write) - DEFAULT 0x1 (default, read-write) - -Bit 12: TILING (IV, read-write) - ENABLED 0x0 (read-write) - DISABLED 0x1 (default, read-write) - - -Bits 13-23: TILING_DEBUG (IV, read-write) - DISABLED 0x0 (read-write) - -RTL(?) register ---------------- - -Name: RTL -Offset: 0x00100??? -4 bytes, read-write - -Bits 0-1: S (IU, read-write) - DEFAULT 0x2 (default, read-write) - -Bits 4-5: V (IU, read-write) - DEFAULT 0x2 (default, read-write) - -Bits 8-9: M (IU, read-write) - DEFAULT 0x2 (default, read-write) - -Bits 12-13: H (IU, read-write) - DEFAULT 0x1 (default, read-write) - -Bits 16-17: A (IU, read-write) - DEFAULT 0x1 (default, read-write) - -Bits 20-21: G (IU, read-write) - DEFAULT 0x1 (default, read-write) - -Bit 24: ARB_GR_HI_PRIOR (IU, read-write) - DEFAULT 0x0 (default, read-write) - -Bit 28: ARB_MEDIA_HI_PRIOR (IU, read-write) - DEFAULT 0x0 (default, read-write) - - - -External devices -================ - -The EXTDEV registers begin at 0x00101000 in the CTRL region. - -Boot 0 register ---------------- - -Name: BOOT_0 -Offset: 0x00101000 -4 bytes, read-write - -Bit 2: STRAP_RAM_TYPE (XV, read-write) - SGRAM_8MBIT 0x1 (read-write) - SGRAM_16MBIT 0x0 (read-write) - -Bit 11: STRAP_OVERWRITE (IV, read-write) - ENABLED 0x1 (read-write) - -Bit 4: STRAP_RAM_WIDTH (XV, read-write) - 64 0x0 (read-write) - 128 0x1 (read-write) - - - -GRAPH -===== - -The GRAPH registers begin at 0x00400000 in the CTRL region. - -Debug 0 register ----------------- - -Name: DEBUG_0 -Offset: 0x00400080 -4 bytes, read-write - -Bit 0: STATE (-V, clear(check?)-write) - NORMAL 0x0 (clear(check?)-write) - RESET 0x1 (write-only) - -Bit 4: BULK_READS (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 20: WRITE_ONLY_ROPS_2D (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 24: DRAWDIR_AUTO (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Debug 1 register ----------------- - -Name: DEBUG_1 -Offset: 0x00400084 -4 bytes, read-write - -Bit 0: VOLATILE_RESET (IV, read-write) - NOT_LAST 0x0 (default, read-write) - LAST 0x1 (read-write) - -Bit 16: INSTANCE (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 20: CTX (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Debug 2 register ----------------- - -Name: DEBUG_2 -Offset: 0x00400088 -4 bytes, read-write - -Bit 0: AVOID_RMW_BLEND (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 8: DPWR_FIFO (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Bit 28: VOLATILE_RESET (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Debug 3 register ----------------- - -Name: DEBUG_3 -Offset: 0x0040008C -4 bytes, read-write - -Bit 24: HONOR_ALPHA (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Interrupt 0 register --------------------- - -Name: INTR_0 -Offset: 0x00400100 -4 bytes, read-write - -Bit 0: RESERVED (-V, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 4: CONTEXT_SWITCH (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 8: VBLANK (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 12: RANGE (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 16: METHOD_COUNT (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 20: FORMAT (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 24: COMPLEX_CLIP (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 28: NOTIFY (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Interrupt 1 register --------------------- - -Name: INTR_1 -Offset: 0x00400104 -4 bytes, read-write - -Bit 0: METHOD (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 4: DATA (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 12: DOUBLE_NOTIFY (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 16: CTXSW_NOTIFY (IV, read-write) - NOT_PENDING 0x0 (default, read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Interrupt 0 enable register ---------------------------- - -Name: INTR_EN_0 -Offset: 0x00400140 -4 bytes, read-write - -Interrupt 1 enable register ---------------------------- - -Name: INTR_EN_1 -Offset: 0x00400144 -4 bytes, read-write - -Context switch register ------------------------ - -Name: CTX_SWITCH -Offset: 0x00400180 -4 bytes, read-write - -Context control register ------------------------- - -Name: CTX_CONTROL -Offset: 0x00400190 -4 bytes, read-write - -Bits 0-1: MINIMUM_TIME (IV, read-write) - 33US 0x0 (default, read-write) - 262US 0x1 (read-write) - 2MS 0x2 (read-write) - 17MS 0x3 (read-write) - -Bit 8: TIME (IV, read-write) - EXPIRED 0x0 (default, read-write) - NOT_EXPIRED 0x1 (read-write) - -Bit 16: CHID (IV, read-write) - INVALID 0x0 (default, read-write) - VALID 0x1 (read-write) - -Bit 20: SWITCH (-V, read-only) - UNAVAILABLE 0x0 (read-only) - AVAILABLE 0x1 (read-only) - -Bit 24: SWITCHING (IV, read-write) - IDLE 0x0 (default, read-write) - BUSY 0x1 (read-write) - -Bit 28: DEVICE (IV, read-write) - DISABLED 0x0 (default, read-write) - ENABLED 0x1 (read-write) - -Context user register ---------------------- - -Name: CTX_USER -Offset: 0x00400194 -4 bytes, read-write - -Context cache registers ------------------------ - -8 context registers for each channel (I guess). - -Name: CTX_CACHE -Offset: 0x004001a0+i*4 (0 <= i 8) -8*4 bytes, read-write - -Absolute X RAM registers ------------------------- - -Name: ABS_X_RAM -Offset: 0x00400400+i*4 (0 <= i < 32) -32*4 bytes, read-write - -Absolute Y RAM registers ------------------------- - -Name: ABS_Y_RAM -Offset: 0x00400480+i*4 (0 <= i < 32) -32*4 bytes, read-write - -X misc register ---------------- - -Name: X_MISC -Offset: 0x00400500 -4 bytes, read-write - -Y misc register ---------------- - -Name: Y_MISC -Offset: 0x00400504 -4 bytes, read-write - -Exceptions register -------------------- - -Name: EXCEPTIONS -Offset: 0x00400508 -4 bytes, read-write - -Source color register ---------------------- - -Name: SOURCE_COLOR -Offset: 0x0040050C -4 bytes, read-write - -XY logic misc 0 register ------------------------- - -Name: XY_LOGIC_MISC0 -Offset: 0x00400514 -4 bytes, read-write - -XY logic misc 1 register ------------------------- - -Name: XY_LOGIC_MISC1 -Offset: 0x00400518 -4 bytes, read-write - - DVDY_VALUE 0x0 (default, read-write) (???) - -XY logic misc 2 register ------------------------- - -Name: XY_LOGIC_MISC2 -Offset: 0x0040051C -4 bytes, read-write - -XY logic misc 3 register ------------------------- - -Name: XY_LOGIC_MISC3 -Offset: 0x00400520 -4 bytes, read-write - -Clip X 0 register ------------------ - -Name: CLIPX_0 -Offset: 0x00400524 -4 bytes, read-write - -Clip X 1 register ------------------ - -Name: CLIPX_1 -Offset: 0x00400528 -4 bytes, read-write - -Clip Y 0 register ------------------ - -Name: CLIPY_0 -Offset: 0x0040052c -4 bytes, read-write - -Clip Y 1 register ------------------ - -Name: CLIPY_1 -Offset: 0x00400530 -4 bytes, read-write - -Absolute iclip X max. register ------------------------------- - -Name: ABS_ICLIP_XMAX -Offset: 0x00400534 -4 bytes, read-write - -Absolute iclip Y max. register ------------------------------- - -Name: ABS_ICLIP_YMAX -Offset: 0x00400538 -4 bytes, read-write - -Absolute uclip X min. register ------------------------------- - -Name: ABS_UCLIP_XMIN -Offset: 0x0040053C -4 bytes, read-write - -Absolute uclip Y min. register ------------------------------- - -Name: ABS_UCLIP_YMIN -Offset: 0x00400540 -4 bytes, read-write - -Absolute uclip X max. register ------------------------------- - -Name: ABS_UCLIP_XMAX -Offset: 0x00400544 -4 bytes, read-write - -Absolute uclip Y max. register ------------------------------- - -Name: ABS_UCLIP_YMAX -Offset: 0x00400548 -4 bytes, read-write - -Absolute uclipa X min. register ------------------------------- - -Name: ABS_UCLIPA_XMIN -Offset: 0x00400560 -4 bytes, read-write - -Absolute uclipa Y min. register ------------------------------- - -Name: ABS_UCLIPA_YMIN -Offset: 0x00400564 -4 bytes, read-write - -Absolute uclipa X max. register ------------------------------- - -Name: ABS_UCLIPA_XMAX -Offset: 0x00400568 -4 bytes, read-write - -Absolute uclipa Y max. register ------------------------------- - -Name: ABS_UCLIPA_YMAX -Offset: 0x0040056C -4 bytes, read-write - -Source canvas min. register ---------------------------- - -Name: SRC_CANVAS_MIN -Offset: 0x00400550 -4 bytes, read-write - -Source canvas max. register ---------------------------- - -Name: SRC_CANVAS_MAX -Offset: 0x00400554 -4 bytes, read-write - -Destination canvas min. register --------------------------------- - -Name: DST_CANVAS_MIN -Offset: 0x00400558 -4 bytes, read-write - -Destination canvas max. register --------------------------------- - -Name: DST_CANVAS_MAX -Offset: 0x0040055C -4 bytes, read-write - -Pattern color 0 0 register --------------------------- - -Name: PATT_COLOR0_0 -Offset: 0x00400600 -4 bytes, read-write - -Pattern color 0 1 register --------------------------- - -Name: PATT_COLOR0_1 -Offset: 0x00400604 -4 bytes, read-write - -Pattern color 1 0 register --------------------------- - -Name: PATT_COLOR1_0 -Offset: 0x00400608 -4 bytes, read-write - -Pattern color 1 1 register --------------------------- - -Name: PATT_COLOR1_1 -Offset: 0x0040060C -4 bytes, read-write - -Pattern registers ------------------ - -Name: PATTERN -Offset: 0x00400610+i*4 (0 <=i < 2) -2*4 bytes, read-write - -Pattern shape register ----------------------- - -Name: PATTERN_SHAPE -Offset: 0x00400618 -4 bytes, read-write - -Bits 0-1: VALUE (XV, read-write) - 8X8 0x0 (read-write) - 64X1 0x1 (read-write) - 1X64 0x2 (read-write) - -Monochrome color 0 register ---------------------------- - -Name: MONO_COLOR0 -Offset: 0x0040061C -4 bytes, read-write - -Raster operation register -------------------------- - -Name: ROP3 -Offset: 0x00400624 -4 bytes, read-write - -Plane mask register -------------------- - -Name: PLANE_MASK -Offset: 0x00400628 -4 bytes, read-write - -Chroma register ---------------- - -Name: CHROMA -Offset: 0x0040062C -4 bytes, read-write - -B(?) offset 0 register ----------------------- - -Name: BOFFSET0 -Offset: 0x00400630 -4 bytes, read-write - -B(?) offset 1 register ----------------------- - -Name: BOFFSET1 -Offset: 0x00400634 -4 bytes, read-write - -B(?) offset 2 register ----------------------- - -Name: BOFFSET2 -Offset: 0x00400638 -4 bytes, read-write - -B(?) offset 3 register ----------------------- - -Name: BOFFSET3 -Offset: 0x0040063C -4 bytes, read-write - -Beta register -------------- - -Name: BETA -Offset: 0x00400640 -4 bytes, read-write - -Control out register --------------------- - -Name: CONTROL_OUT -Offset: 0x00400644 -4 bytes, read-write - -B(?) pitch 0 register ---------------------- - -Name: BPITCH0 -Offset: 0x00400650 -4 bytes, read-write - -B(?) pitch 1 register ---------------------- - -Name: BPITCH1 -Offset: 0x00400654 -4 bytes, read-write - -B(?) pitch 2 register ---------------------- - -Name: BPITCH2 -Offset: 0x00400658 -4 bytes, read-write - -B(?) pitch 3 register ---------------------- - -Name: BPITCH3 -Offset: 0x0040065C -4 bytes, read-write - -DMA register ------------- - -Name: DMA -Offset: 0x00400680 -4 bytes, read-write - -Notify register ---------------- - -Name: NOTIFY -Offset: 0x00400684 -4 bytes, read-write - -Bits 0-15: INST_MEM_LOC ??? - -Instance register ------------------ - -Name: INSTANCE -Offset: 0x00400688 -4 bytes, read-write - -Memory format register ----------------------- - -Name: MEMFMT -Offset: 0x0040068C -4 bytes, read-write - -Clip 0 min. register --------------------- - -Name: CLIP0_MIN -Offset: 0x00400690 -4 bytes, read-write - -Clip 0 max. register --------------------- - -Name: CLIP0_MAX -Offset: 0x00400694 -4 bytes, read-write - -Clip 1 min. register --------------------- - -Name: CLIP1_MIN -Offset: 0x00400698 -4 bytes, read-write - -Clip 1 max. register --------------------- - -Name: CLIP1_MAX -Offset: 0x0040069C -4 bytes, read-write - -Clip misc register ------------------- - -Name: CLIP_MISC -Offset: 0x004006A0 -4 bytes, read-write - -FIFO register -------------- - -Name: FIFO -Offset: 0x004006A4 -4 bytes, read-write - -Bit 0: ACCESS (IV, read-write) - DISABLED 0x0 (read-write) - ENABLED 0x1 (default, read-write) - -B(?) pixel register -------------------- - -Name: BPIXEL -Offset: 0x004006A8 -4 bytes, read-write - -Bits 0-1: DEPTH0_FMT (XV, read-write) - Y16_BITS 0x0 (read-write) - BITS_8 0x1 (read-write) - BITS_16 0x2 (read-write) - BITS_32 0x3 (read-write) - -Bit 2: DEPTH0 (XV, read-write) - NOT_VALID 0x0 (read-write) - VALID 0x1 (read-write) - -Bits 4-5: DEPTH1_FMT (XV, read-write) - Y16_BITS 0x0 (read-write) - BITS_8 0x1 (read-write) - BITS_16 0x2 (read-write) - BITS_32 0x3 (read-write) - -Bit 6: DEPTH1 (XV, read-write) - NOT_VALID 0x0 (read-write) - VALID 0x1 (read-write) - -Bits 8-9: DEPTH2_FMT (XV, read-write) - Y16_BITS 0x0 (read-write) - BITS_8 0x1 (read-write) - BITS_16 0x2 (read-write) - BITS_32 0x3 (read-write) - -Bit 10: DEPTH2 (XV, read-write) - NOT_VALID 0x0 (read-write) - VALID 0x1 (read-write) - -Bits 12-13: DEPTH3_FMT (XV, read-write) - Y16_BITS 0x0 (read-write) - BITS_8 0x1 (read-write) - BITS_16 0x2 (read-write) - BITS_32 0x3 (read-write) - -Bit 14: DEPTH3 (XV, read-write) - NOT_VALID 0x0 (read-write) - VALID 0x1 (read-write) - -Status register ---------------- - -Name: STATUS -Offset: 0x004006B0 -4 bytes, read-only - -DMA interrupt 0 register ------------------------- - -Name: DMA_INTR_0 -Offset: 0x00401100 -4 bytes, read-write - -Bit 0: INSTANCE (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 4: PRESENT (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 8: PROTECTION (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 12: LINEAR (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -Bit 16: NOTIFY (XV, read-write) - NOT_PENDING 0x0 (read-only) - PENDING 0x1 (read-only) - RESET 0x1 (write-only) - -DMA interrupt 0 enable register -------------------------------- - -Name: DMA_INTR_EN_0 -Offset: 0x00401140 -4 bytes, read-write - -DMA control register --------------------- - -Name: DMA_CONTROL -Offset: 0x00401210 -4 bytes, read-write - - - -RAMDAC -====== - -The RAMDAC registers begin at 0x00680000 in the CTRL region. - -Graphic cursor starting position register ------------------------------------------ - -Name: GRCURSOR_START_POS -Offset: 0x00680300 -4 bytes, read-write - -Bits 0-11: X (XS, read-write) - -Bits 16-27: Y (XS, read-write) - -MPLL coeff register -------------------- - -Name: MPLL_COEFF -Offset: 0x00680504 -4 bytes, read-write - -Bits 0-7: MDIV (IU, read-write) - -Bits 8-15: NDIV (IU, read-write) - -Bits 16-18: PDIV (IV, read-write) - -VPLL coeff register -------------------- - -Name: VPLL_COEFF -Offset: 0x00680508 -4 bytes, read-write - -Bits 0-7: MDIV (IU, read-write) - -Bits 8-15: NDIV (IU, read-write) - -Bits 16-18: PDIV (IV, read-write) - -PLL coeff select register -------------------------- - -Name: PLL_COEFF_SELECT -Offset: 0x0068050C -4 bytes, read-write - -Bit 4: DLL_BYPASS (IV, read-write) - FALSE 0x0 (default, read-write) - TRUE 0x1 (read-write) - -Bit 8: MPLL_SOURCE (IV, read-write) - DEFAULT 0x0 (default, read-write) - PROG 0x1 (read-write) - -Bit 12: MPLL_BYPASS (IV, read-write) - FALSE 0x0 (default, read-write) - TRUE 0x1 (read-write) - -Bit 16: VPLL_SOURCE (IV, read-write) - DEFAULT 0x0 (default, read-write) - PROG 0x1 (read-write) - -Bit 20: VPLL_BYPASS (IV, read-write) - FALSE 0x0 (default, read-write) - TRUE 0x1 (read-write) - -Bits 24-25: PCLK_SOURCE (IV, read-write) - VPLL 0x0 (default, read-write) - VIP 0x1 (read-write) - XTALOSC 0x2 (read-write) - -Bit 28: VCLK_RATIO (IV, read-write) - DB1 0x0 (default, read-write) - DB2 0x1 (read-write) - -General control register ------------------------- - -Various flags for DAC. BPC controls the width of the palette. - -Name: GENERAL_CONTROL -Offset: 0x00680600 -4 bytes, read-write - -Bits 0-1: FF_COEFF (IV, read-write) - DEF 0x0 (default, read-write) - -Bit 4: IDC_MODE (IV, read-write) - GAMMA 0x0 (default, read-write) - INDEX 0x1 (read-write) - -Bit 8: VGA_STATE (IV, read-write) - NOTSEL 0x0 (default, read-write) - SEL 0x1 (read-write) - -Bit 12: 565_MODE (IV, read-write) - NOTSEL 0x0 (default, read-write) - SEL 0x1 (read-write) - -Bit 16: BLK_PEDSTL (IV, read-write) - OFF 0x0 (default, read-write) - ON 0x1 (read-write) - -Bit 17: TERMINATION (IV, read-write) - 37OHM 0x0 (default, read-write) - 75OHM 0x1 (read-write) - -Bit 20: BPC (IV, read-write) - 6BITS 0x0 (default, read-write) - 8BITS 0x1 (read-write) - -Bit 24: DAC_SLEEP (IV, read-write) - DIS 0x0 (default, read-write) - EN 0x1 (read-write) - -Bit 28: PALETTE_CLK (IV, read-write) - EN 0x0 (default, read-write) - DIS 0x1 (read-write) - -VSERR width register --------------------- - -Name: VSERR_WIDTH -Offset: 0x00680700 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -VEQU end register ------------------ - -Name: VEQU_END -Offset: 0x00680704 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Vertical B(?) blank end register --------------------------------- - -Name: VBBLANK_END -Offset: 0x00680708 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Vertical blank end register ---------------------------- - -Name: VBLANK_END -Offset: 0x0068070C -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Vertical blank start register ------------------------------ - -Name: VBLANK_START -Offset: 0x00680710 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Vertical (B?) blank start register ----------------------------------- - -Name: VBBLANK_START -Offset: 0x00680714 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -VEQU start register -------------------- - -Name: VEQU_START -Offset: 0x00680718 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Vertical total register ------------------------ - -Name: VTOTAL -Offset: 0x0068071C -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal sync width register ------------------------------- - -Name: HSYNC_WIDTH -Offset: 0x00680720 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal burst start register -------------------------------- - -Name: HBURST_START -Offset: 0x00680724 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal burst end register ------------------------------ - -Name: HBURST_END -Offset: 0x00680728 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal blank start register -------------------------------- - -Name: HBLANK_START -Offset: 0x0068072C -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal blank end register ------------------------------ - -Name: HBLANK_END -Offset: 0x00680730 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -Horizontal total register -------------------------- - -Name: HTOTAL -Offset: 0x00680734 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -HEQU width register -------------------- - -Name: HEQU_WIDTH -Offset: 0x00680738 -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - -HSERR width register --------------------- - -Name: HSERR_WIDTH -Offset: 0x0068073C -4 bytes, read-write - -Bits 0-10: VAL (IV, read-write) - - - -'Standard' SVGA registers -========================= - -ATTR, CTRC, GRAPH, SEQ and MISC registers are available throught MMIO too. - -Name: ATTR_REG_INDEX -Offset: 0x006013c0 -1 byte, read-write - -Name: ATTR_REG_DATA -Offset: 0x006013c1 -1 byte, read-write - -Name: CRTC_REG_INDEX -Offset: 0x006013d4 -1 byte, read-write - -Name: CRTC_REG_DATA -Offset: 0x006013d5 -1 byte, read-write - -Name: GRA_REG_INDEX -Offset: 0x000C03ce -1 byte, read-write - -Name: GRA_REG_DATA -Offset: 0x000C03cf -1 byte, read-write - -Name: SEQ_REG_INDEX -Offset: 0x000C03c4 -1 byte, read-write - -Name: SEQ_REG_DATA -Offset: 0x000C03c5 -1 byte, read-write - -Name: MISC_REG -Offset: 0x000C03c2 -1 byte, read-write - - - -Extra CRTC registers -==================== - -Repaint 0 register ------------------- - -Extended offset and start address. - -Name: REPAINT0 -Index: 0x19 -1 byte - -Bits 0-4: START_ADDR_20_16 - -Bits 5-7: OFFSET_10_8 - -Repaint 1 register ------------------- - -Various flags. - -Name: REPAINT1 -Index: 0x1a -1 byte - -Bit 1: PALETTE_WIDTH - 8BITS 0x0 - 6BITS 0x1 - -Bit 2: LARGE_SCREEN - DISABLE 0x1 - ENABLE 0x0 ( >= 1280 ) - -Bit 4: COMPATIBLE_TEXT - ENABLE 0x1 - DISABLE 0x0 - -Bit 6: VSYNC - DISABLE 0x1 - ENABLE 0x0 - -Bit 7: HSYNC - DISABLE 0x1 - ENABLE 0x0 - -FIFO control register ---------------------- - -Controls how much data the refresh fifo requests. - -Name: FIFO_CONTROL -Index: 0x1b -1 byte - -Bits 0-2: BURST_LENGTH - BURST_LENGTH_8 0x0 - BURST_LENGTH_32 0x1 - BURST_LENGTH_64 0x2 - BURST_LENGTH_128 0x3 - BURST_LENGTH_256 0x4 - -Bit 7: UNDERFLOW_WARN - -FIFO register -------------- - -When the fifo occupancy falls below *twice* the watermark, -the refresh fifo will start to be refilled. If this value is -too low, you will get junk on the screen. Too high, and performance -will suffer. Watermark in units of 8 bytes. - -Name: FIFO -Index: 0x20 -1 byte - -Bits 0-5: WATERMARK - -Bit 7: RESET - -Extra register --------------- - -Assorted extra bits. - -Name: EXTRA -Index: 0x25 -1 byte - -Bit 0: VERT_TOTAL_10 - -Bit 1: VERT_DISPLAY_END_10 - -Bit 2: VERT_RETRACE_START_10 - -Bit 3: VERT_BLANK_START_10 - -Bit 4: HORIZ_BLANK_END_6 - -Bit 5: OFFSET_11 - -Pixel register --------------- - -Controls what the format of the framebuffer is. - -Name: PIXEL -Index: 0x28 -1 byte - -Bits 0-1: FORMAT - VGA 0x0 - 8BPP 0x1 - 16BPP 0x2 - 32BPP 0x3 - -Bits 3-5: TV_HORIZ_ADJUST - -Bit 6: TV_MODE - NTSC 0x0 - PAL 0x1 - -Bit 7: MODE - TV 0x1 - VGA 0x0 - -Horizontal extra register -------------------------- - -Horizonal extended bits. - -Name: HORIZ_EXTRA -Index: 0x2d -1 byte - -Bit 0: DISPLAY_TOTAL_8 - -Bit 1: DISPLAY_END_8 - -Bit 2: HORIZ_BLANK_START_8 - -Bit 3: HORIZ_RETRACE_START_8 - -Bit 4: INTER_HALF_START_8 - -Graphic cursor 0 register -------------------------- - -Name: GRCURSOR0 -Index: 0x30 -1 byte - -Bits 0-5: START_ADDR_21_16 - -Graphic cursor 1 register -------------------------- - -Name: GRCURSOR1 -Index: 0x31 -1 byte - -Bit 0: CURSOR - DISABLE 0x0 - ENABLE 0x1 - -Bit 1: SCAN_DBL - DISABLE 0x0 - ENABLE 0x1 - -Bits 3-7: START_ADDR_15_11 - -EOF diff --git a/doc/nvidia_notes/status.xlsx b/doc/nvidia_notes/status.xlsx deleted file mode 100644 index bfeeb14528f00e6aa6a6f1d9e1f055ab1399947f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15945 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 - * - * Copyright 2024-2025 Connor Hyde - */ - -#pragma once -#include -#include -#include -#include - -// CLass names for debugging -extern const char* nv3_class_names[]; - -/* Defines valid classes. */ -typedef enum nv3_pgraph_class_e -{ - nv3_pgraph_class01_beta_factor = 0x01, - nv3_pgraph_class02_rop = 0x02, - nv3_pgraph_class03_chroma_key = 0x03, - nv3_pgraph_class04_plane_mask = 0x04, - nv3_pgraph_class05_clipping_rectangle = 0x05, - nv3_pgraph_class06_pattern = 0x06, - nv3_pgraph_class07_rectangle = 0x07, - nv3_pgraph_class08_point = 0x08, - nv3_pgraph_class09_line = 0x09, - nv3_pgraph_class0a_lin = 0x0a, - nv3_pgraph_class0b_triangle = 0x0b, - nv3_pgraph_class0c_w95txt = 0x0c, - nv3_pgraph_class0d_m2mf = 0x0d, - nv3_pgraph_class0e_scaled_image_from_memory = 0x0e, - nv3_pgraph_class10_blit = 0x10, - nv3_pgraph_class11_image = 0x11, - nv3_pgraph_class12_bitmap = 0x12, - nv3_pgraph_class14_transfer2memory = 0x14, - nv3_pgraph_class15_stretched_image_from_cpu = 0x15, - nv3_pgraph_class17_d3d5tri_zeta_buffer = 0x17, - nv3_pgraph_class18_point_zeta_buffer = 0x18, - nv3_pgraph_class1c_image_in_memory = 0x1c, -} nv3_pgraph_class; - -/* - OBJECT METHODS -*/ - -// Global stuff -#define NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE 0x0 // I'm going insane at 00:48 14/02/2025 -#define NV3_SET_NOTIFY_CONTEXT_FOR_DMA 0x0100 // Set object ctx for dma...see nv3_dma_context_t structure -#define NV3_SET_NOTIFY 0x0104 - -// Crap e.g. "OS name", that sometimes gets submitted, for some reason. So we just suppress the warning messages for them -#define NV3_NVCLASS_CRAP_START 0x0310 -#define NV3_NVCLASS_CRAP_END 0x0324 - -// Render OPeration -#define NV3_ROP_SET_ROP 0x0300 // Set GDI standard rop - -// Beta Factor -#define NV3_BETA_FACTOR 0x0300 - -// Chroma Key -// Can't figure out what this is, used in 9x but certainly software, can't find anywhere... -#define NV3_CHROMA_UNKNOWN_0200 0x0200 -#define NV3_CHROMA_KEY 0x0304 - -// Clip -#define NV3_CLIP_POSITION 0x0300 // S16:S16, 0=topleft -#define NV3_CLIP_SIZE 0x0304 // U16:U16 - -// Blit Pattern -#define NV3_PATTERN_FORMAT 0x0304 -#define NV3_PATTERN_SHAPE 0x0308 -#define NV3_PATTERN_SHAPE_8X8 0 -#define NV3_PATTERN_SHAPE_64X1 1 -#define NV3_PATTERN_SHAPE_1X64 2 -#define NV3_PATTERN_SHAPE_LAST_VALID NV3_PATTERN_SHAPE_1X64 - -#define NV3_PATTERN_UNUSED_DRIVER_BUG 0x030C -#define NV3_PATTERN_COLOR0 0x0310 -#define NV3_PATTERN_COLOR1 0x0314 -#define NV3_PATTERN_BITMAP_HIGH 0x0318 -#define NV3_PATTERN_BITMAP_LOW 0x031C - -// Rect - -#define NV3_RECTANGLE_COLOR 0x0304 - -// 16 possible rectangles. 8 byte structure, first 4 bytes = position, second 2 = size. -#define NV3_RECTANGLE_START 0x0400 - -#define NV3_RECTANGLE_MAX 16 -#define NV3_RECTANGLE_END 0x0480 - -// M2MF -#define NV3_M2MF_IN_CTXDMA_OFFSET 0x030C -#define NV3_M2MF_OUT_CTXDMA_OFFSET 0x0310 -#define NV3_M2MF_IN_PITCH 0x0314 -#define NV3_M2MF_OUT_PITCH 0x0318 -#define NV3_M2MF_SCANLINE_LENGTH_IN_BYTES 0x031C -#define NV3_M2MF_NUM_SCANLINES 0x0320 -#define NV3_M2MF_FORMAT 0x0324 - -// M2MF formats (IN and OUT ORed together) -#define NV3_M2MF_FORMAT_INPUT 0 -#define NV3_M2MF_FORMAT_OUTPUT 8 - -#define NV3_M2MF_NOTIFY 0x0328 - -// blit -#define NV3_BLIT_POSITION_IN 0x0300 -#define NV3_BLIT_POSITION_OUT 0x0304 -#define NV3_BLIT_SIZE 0x0308 - -// image_from_cpu -#define NV3_IMAGE_START_POSITION 0x0304 // starting position of image from cpu -#define NV3_IMAGE_SIZE 0x0308 -#define NV3_IMAGE_SIZE_IN 0x030C -#define NV3_IMAGE_COLOR_START 0x0400 -#define NV3_IMAGE_COLOR_MAX 32 -#define NV3_IMAGE_COLOR_END 0x0480 - -#define NV3_IMAGE_IN_MEMORY_COLOR_FORMAT 0x0300 -#define NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE 0x0304 -#define NV3_IMAGE_IN_MEMORY_PITCH 0x0308 -#define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET 0x030C -#define NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET_END 22 - -/* GDI */ - -/* Type A: Unclipped Rectangle */ -#define NV3_W95TXT_A_COLOR 0x03FC // It's the colour of the text. This is used to submit a dummy object so the notifier can be used to sync in Win2000 DDraw6 drivers. -#define NV3_W95TXT_A_RECT_START 0x0400 -#define NV3_W95TXT_A_RECT_SIZE 64 // Number of rects -#define NV3_W95TXT_A_RECT_END 0x05FF - -/* Type B: Clipped Rectangle */ -#define NV3_W95TXT_B_CLIP_TOPLEFT 0x07F4 -#define NV3_W95TXT_B_CLIP_BOTTOMRIGHT 0x07F8 -#define NV3_W95TXT_B_COLOR 0x07FC -#define NV3_W95TXT_B_CLIP_CLIPRECT_START 0x0800 -#define NV3_W95TXT_B_CLIP_CLIPRECT_SIZE 128 // Number of rects -#define NV3_W95TXT_B_CLIP_CLIPRECT_END 0x09FF - -/* Type C: Unscaled Single-Colour Text (filled in from mono bitmap) */ -#define NV3_W95TXT_C_CLIP_TOPLEFT 0x0BEC -#define NV3_W95TXT_C_CLIP_BOTTOMRIGHT 0x0BF0 -#define NV3_W95TXT_C_CLIP_COLOR 0x0BF4 -#define NV3_W95TXT_C_CLIP_SIZE 0x0BF8 -#define NV3_W95TXT_C_CLIP_POSITION 0x0BFC /* TOP LEFT */ -#define NV3_W95TXT_C_CLIP_CLIPRECT_START 0x0C00 -#define NV3_W95TXT_C_CLIP_CLIPRECT_SIZE 64 // Number of rects -#define NV3_W95TXT_C_CLIP_CLIPRECT_END 0x0DFF - -/* Type D: Scaled Single-Colour Text (filled in from mono bitmap) */ -#define NV3_W95TXT_D_CLIP_TOPLEFT 0x0FE8 -#define NV3_W95TXT_D_CLIP_BOTTOMRIGHT 0x0FEC -#define NV3_W95TXT_D_CLIP_COLOR 0x0FF0 -#define NV3_W95TXT_D_CLIP_SIZE_IN 0x0FF4 -#define NV3_W95TXT_D_CLIP_SIZE_OUT 0x0FF8 -#define NV3_W95TXT_D_CLIP_POSITION 0x0FFC /* TOP LEFT */ -#define NV3_W95TXT_D_CLIP_CLIPRECT_START 0x1000 -#define NV3_W95TXT_D_CLIP_CLIPRECT_SIZE 128 // Number of rects -#define NV3_W95TXT_D_CLIP_CLIPRECT_END 0x11FF - -/* Type E: Scaled Pattern-type Bitmap Text (filled) */ -#define NV3_W95TXT_E_CLIP_TOPLEFT 0x13E4 -#define NV3_W95TXT_E_CLIP_BOTTOMRIGHT 0x13E8 -#define NV3_W95TXT_E_CLIP_COLOR_0 0x13EC -#define NV3_W95TXT_E_CLIP_COLOR_1 0x13F0 -#define NV3_W95TXT_E_CLIP_SIZE_IN 0x13F4 -#define NV3_W95TXT_E_CLIP_SIZE_OUT 0x13F8 -#define NV3_W95TXT_E_CLIP_POSITION 0x13FC /* TOP LEFT */ -#define NV3_W95TXT_E_CLIP_CLIPRECT_START 0x1400 -#define NV3_W95TXT_E_CLIP_CLIPRECT_SIZE 128 // Number of rects -#define NV3_W95TXT_E_CLIP_CLIPRECT_END 0x15FF - - -/* Class context switch method */ -typedef struct nv3_class_ctx_switch_method_s -{ - union - { - uint32_t data; - - uint16_t instance; - uint8_t channel : 6; - uint16_t reserved : 9; - bool reset_if_volatile; // ???? - } set_notify_ctx_dma; // Set notifier context for DMA (context switch) - -} nv3_class_ctx_switch_method_t; - -/* -enumerates color formats -there are some other colour formats that are only used in certain areas which are defined below -*/ -typedef enum nv3_pgraph_pixel_format_e -{ - nv3_pgraph_pixel_format_r5g5b5 = 0, - nv3_pgraph_pixel_format_r8g8b8 = 1, - nv3_pgraph_pixel_format_r10g10b10 = 2, - nv3_pgraph_pixel_format_y8 = 3, - nv3_pgraph_pixel_format_y16 = 4, - nv3_pgraph_pixel_format_v8y8u8y18 = 5, // "18"? NV wtf? - nv3_pgraph_pixel_format_y18v8y8u8 = 6, // "18"? NV wtf? - nv3_pgraph_pixel_format_y420 = 7, // YUV 420 -} nv3_pgraph_pixel_format; - -/* Main color format */ -typedef struct nv3_color_expanded_s -{ - uint8_t a; - - /* WARNING: The internal format is 10-bit RGB! */ - uint16_t r; - uint16_t g; - uint16_t b; - - // YUV stuff - float y; - float u; - float v; - - // Indexed colour - union - { - uint16_t i16; - - uint8_t i16_high; - uint8_t i8; - }; - - // the pixel format - nv3_pgraph_pixel_format pixel_format; -} nv3_color_expanded_t; - -/* A simple ARGB format colour */ -typedef struct nv3_color_argb_s -{ - uint8_t a; - uint8_t r; - uint8_t g; - uint8_t b; -} nv3_color_argb_t; - -/* Generic 16-bit coordinate*/ -typedef struct nv3_coord_16_s -{ - uint16_t x; - uint16_t y; -} nv3_coord_16_t; - -/* A big position format with 30:16 = y, 15:11 = nothing, 10:0 = x */ -typedef struct nv3_coord_16_bigy_s -{ - // WHOSE IDEA WAS THIS? - uint16_t x : 11; - uint8_t reserved : 5; - uint16_t y : 15; - bool reserved2 : 1; -} nv3_coord_16_bigy_t; - -/* Generic 32-bit colour + 16-bit position */ -typedef struct nv3_color_and_coord_16_s -{ - nv3_color_expanded_t color; - nv3_coord_16_t points; -} nv3_color_and_coord_16_t; - -/* "UTRI" type triangle */ -typedef struct nv3_utri_s -{ - uint32_t color; // use nv3_color_expanded_t but changed for alignment reasons - nv3_coord_16_t point0; - nv3_coord_16_t point1; - nv3_coord_16_t point2; -} nv3_utri_t; - -/* Generic 16-bit clip region */ -typedef struct nv3_clip_16_s -{ - // The bounds of the clipping area. - uint16_t left; - uint16_t top; - uint16_t right; - uint16_t bottom; -} nv3_clip_16_t; - -/* In case your positions weren't HIGH PRECISION enough */ -typedef struct nv3_coord_32_s -{ - uint32_t x; - uint32_t y; -} nv3_coord_32_t; - -// COLOUR FORMATS - -/* - Object Class 0x01 (real hardware, also 0x41) - 0x12 (drivers) - Beta factor -*/ -typedef struct nv3_object_class_001 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; - uint32_t set_beta_factor_1d31; // 31:31 (?) value, 30:21 fraction - // Put the rest of it here -} nv3_beta_factor_t; - -/* - Object class 0x02 (real hardware) - 0x14/0x43 (drivers) - Also 0x42 in context IDs - Render operation used for things like blending. Appears to be 8-bit i.e. a ROP3 with 256 possible operations. -*/ -typedef struct nv3_object_class_002 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint8_t rop; // ROP3 (ID = ????????) -} nv3_render_operation_t; - -/* - Object class 0x03 (real hardware) - 0x15 (drivers) - Also 0x43 in context IDs - A chroma/color key, like in video editing -*/ -typedef struct nv3_object_class_003 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t color; // ROP3 (ID = ????????) -} nv3_chroma_key_t; - -/* - Object class 0x04 (real hardware) - 0x15 (drivers) - Also 0x44 in context IDs - Plane mask -*/ -typedef struct nv3_object_class_004 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t color; // Plane mask -} nv3_plane_mask_t; - -/* - Object class 0x05 (real hardware) - 0x19/0x1E/0x47 (drivers) - Also 0x45 in context IDs - Clipping rectangle used for various blitting operations -*/ -typedef struct nv3_object_class_005 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - - /* 16-bit precision */ - nv3_coord_16_t position; - nv3_coord_16_t size; - -} nv3_clipping_rectangle_t; - -/* - Object Class 0x06 (real hardware) - 0x?? (drivers) - Also 0x46 in context IDs - A pattern used for blits. Wahey! -*/ -typedef struct nv3_object_class_006 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t shape; // 0 = 8x8, 1 = 64x1, 2 = 1x64 - uint32_t color0; // Some 32-bit format (argb?) - uint32_t color1; // bit0=color0, bit1=color1 - uint32_t pattern[2]; // bit0=color0, bit1=color1 -} nv3_pattern_t; - -/* - Object Class 0x07 (real hardware) - 0x1E (drivers) - Also 0x47 in context IDs - A rectangle. Wahey! -*/ -typedef struct nv3_object_class_007 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t color; // The colour of the object. - nv3_coord_16_t position[16]; // The positions of up to 16 rectangles. - nv3_coord_16_t size[16]; // The sizes of up to 16 rectangles -} nv3_rectangle_t; - - -/* In case your points weren't colourful enough */ -typedef struct nv3_object_class_008_cpoint_s -{ - nv3_color_expanded_t color; // argb-format 32-bit color - nv3_coord_16_t position; // position -} nv3_object_class_008_cpoint_t; - -/* - Object Class 0x08 (real hardware) - 0x1A (drivers) - Also 0x48 in context IDs - A point: the revolutionary 3d graphics technique... -*/ -typedef struct nv3_object_class_008 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - nv3_color_expanded_t color; // argb? - nv3_coord_16_t point[16]; // Boring points - nv3_coord_32_t point32[16]; // Allows you to have points with full 32-bit precision - nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points! -} nv3_point_t; - -/* Normal line... */ -typedef struct nv3_object_class_009_line_s -{ - nv3_coord_16_t start; // presumably unless it's in reverse order...TODO: check the order - nv3_coord_16_t end; - -} nv3_object_class_009_line_t; - -/* THIRTY TWO BIT PRECISION line */ -typedef struct nv3_object_class_009_line32_s -{ - uint32_t x0; - uint32_t x1; - uint32_t y0; - uint32_t y1; -} nv3_object_class_009_line32_t; - -/* nv3_object_class_009_polyline_t not implemented because it's just a duplicate of nv3_object_class_009_line */ -/* nv3_object_class_009_polyline32_t not implemented because it's just a duplicate of nv3_object_class_009_line32 */ - - -/* - Object Class 0x09 (real hardware) - 0x1B (drivers) - Also 0x49 in context IDs - It's a line, but also a polygon... -*/ -typedef struct nv3_object_class_009 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - nv3_color_expanded_t color; // argb? - nv3_object_class_009_line_t line[16]; // List of line points (...) - nv3_object_class_009_line32_t line32[8]; - nv3_object_class_009_line_t polyline[32]; - nv3_object_class_009_line32_t polyline32[16]; - nv3_color_and_coord_16_t cpolyline[16]; // List of line points and colours. -} nv3_line_t; - -/* - Object Class 0x0A (real hardware) - 0x1c (drivers) - Also 0x4a in context IDs - - This one is where nvidia reinvents the line, but without the starting or ending pixel. - Seriously. -*/ -typedef struct nv3_object_class_00A -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - - uint32_t set_notify; // Set notifier - - nv3_color_expanded_t color; // argb? - nv3_object_class_009_line_t line[16]; // List of line points (...) - nv3_object_class_009_line32_t line32[8]; - nv3_object_class_009_line_t polyline[32]; - nv3_object_class_009_line32_t polyline32[16]; - nv3_color_and_coord_16_t cpolyline[16]; // List of line points and colours. - -} nv3_lin_t; - -/* - Object Class 0x0B (real hardware) - 0x?? (drivers) - Also 0x4b in context IDs. - - This is a triangle but seems to be obsolete. It's replaced with UD3D0Z / D3D5 Accelerated Triangle with Zeta Buffer. Does it even exist? -*/ -typedef struct nv3_object_class_00B -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - nv3_color_expanded_t color; // argb? - // The points of the triangle. - nv3_coord_16_t points[3]; - - // Another way of filling out the points of the triangle - uint32_t x0; - uint32_t y0; - uint32_t x1; - uint32_t y1; - uint32_t x2; - uint32_t y2; - - nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions? - nv3_coord_32_t mesh32[16]; // Mesh with 32-bit format - nv3_utri_t ctriangle[8]; // Triangles with colour - nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours - -} nv3_triangle_t; - -/* - Object Class 0x0C (real hardware) - 0x0C (drivers) - Also 0x4C in context IDs. - - GDI text acceleration for Windows 95. -*/ -typedef struct nv3_object_class_00C -{ - /* Unclipped rect (basically class 0x07 )*/ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t color_a; // Color for Clip A - nv3_coord_16_t rect_a_position[64]; - nv3_coord_16_t rect_a_size[64]; - /* Clipped rect */ - nv3_clip_16_t clip_b; - uint32_t color_b; // Color for Clip B - nv3_clip_16_t clipped_rect[64]; - /* Unclipped transparent bitmap */ - nv3_clip_16_t clip_c; - uint32_t color1_c; - nv3_coord_16_t size_c; - nv3_coord_16_t point_c; - uint32_t bitmap_c[128]; - /* Clipped transparent bitmap */ - nv3_clip_16_t clip_d; - uint32_t color1_d; - nv3_coord_16_t size_in_d; - nv3_coord_16_t size_out_d; - nv3_coord_16_t point_d; - uint32_t bitmap_d[128]; - /* Clipped 1bpp bitmap */ - nv3_clip_16_t clip_e; - uint32_t color0_e; - uint32_t color1_e; - nv3_coord_16_t size_in_e; - nv3_coord_16_t size_out_e; - nv3_coord_16_t point_e; - uint32_t bitmap_e[128]; -} nv3_win95_text_t; - - -/* - Object Class 0x0D (real hardware) - 0x?? (drivers) - Also 0x4D in context IDs. - - Represents reformatting of an image in memory. -*/ -typedef struct nv3_object_class_00D -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t offset_in; - uint32_t offset_out; - uint32_t pitch_in; - uint32_t pitch_out; - uint32_t scanline_length; // Stride? - uint32_t num_scanlines; - uint8_t format; // input increment 1 2 or 4, output increment 1 2 or 4 (represented by << 8) - uint32_t buffer_notify; // Notify the Buffedr -} nv3_memory_to_memory_format_t; - -/* - Object Class 0x0E (real hardware) - 0x?? (drivers) - Also 0x4E in context IDs. - - Represents a scaled image coming from memory. -*/ -typedef struct nv3_object_class_00E -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_coord_16_t clip_0; - nv3_coord_16_t clip_1; - nv3_coord_16_t rectangle_out_0; - nv3_coord_16_t rectangle_out_1; - // Calculus in a graphics card - uint32_t delta_du_dx; - uint32_t delta_dv_dy; - nv3_coord_16_t size; // can be size_y if YUV420 - uint32_t pitch; - uint32_t offset; - uint32_t point; - // YUV420 stuff - uint32_t pitch_yuv420; - uint32_t offset_y; - uint32_t offset_u; - uint32_t offset_v; - uint32_t point_yuv420; -} nv3_scaled_image_from_memory_t; - -// (0x0F does not exist) - -/* - Object Class 0x10 (real hardware) - 0x?? (drivers) - Also 0x50 in context IDs. - - Represents a screen to screen blit. I'm still figuring it out. -*/ - -typedef struct nv3_object_class_010 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_coord_16_t point_in; - nv3_coord_16_t point_out; - nv3_coord_16_t size; -} nv3_blit_t; - -/* - Object Class 0x11 (real hardware) - 0x?? (drivers) - Also 0x51 in context IDs. - - Represents an image from the cpu (i guess from system memory) -*/ -typedef struct nv3_object_class_011 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_coord_16_t point; - nv3_coord_16_t size; - nv3_coord_16_t size_in; - nv3_color_expanded_t color[32]; // The colour to use -} nv3_image_t; - -/* - Object Class 0x12 (real hardware) - 0x?? (drivers) - Also 0x52 in context IDs. - - Bitmap from CPU. - It seems the difference is that an image is colour but a -*/ -typedef struct nv3_object_class_012 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_color_expanded_t color_0; - nv3_color_expanded_t color_1; - nv3_coord_16_t point; // Top left(?) of the bitmap - nv3_coord_16_t size; - nv3_coord_16_t size_in; - uint32_t monochrome_bitmap[32]; -} nv3_bitmap_t; - -// 0x13 doesn't exist - -/* - Object Class 0x14 (real hardware) - 0x?? (drivers) - Also 0x54 in context IDs. - - Image Transfer to Memory - It seems the difference is that an image is colour but a bitmap is b&w -*/ -typedef struct nv3_object_class_014 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_coord_16_t point; - nv3_coord_16_t size; - uint32_t image_pitch; // bytes per row - uint32_t image_start; -} nv3_image_to_memory_t; - -/* - Object Class 0x15 (real hardware) - 0x?? (drivers) - Also 0x55 in context IDs. - - Stretched Image from CPU - Seems to be, by the very large color array, the main class used in 2d acceleration. -*/ -typedef struct nv3_object_class_015 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_coord_16_t size_in; - uint32_t delta_dx_du; - uint32_t delta_dy_dv; - nv3_coord_16_t clip_0; - nv3_coord_16_t clip_1; - uint32_t point12d4; /* todo: fraction struct */ - // no reserve needed -} nv3_stretched_image_from_cpu_t; - -// 0x16 invalid - -/* - Object Class 0x17 (real hardware) - 0x?? (drivers) - Also 0x57 in context IDs. - - Direct3D 5.0 accelerated triangle with zeta buffer (combined z buffer and stencil buffer) - This is the final boss of this GPU. True horror stands below. -*/ - -// -// NV3 D3D5: TEXTURING & PIXEL FORMATS -// - -typedef enum nv3_d3d5_texture_pixel_format_e -{ - /* - 16-Bit Pixel Format - 5 bits red, 5 bits green, 5 bits alpha, "boolean" alpha - */ - nv3_d3d5_pixel_format_le_a1r5g5b5 = 0, - - /* - 15-Bit Pixel Format (represented as 16) - 1 bit irrelevant, 5 bits red, 5 bits green, 5 bits alpha - */ - nv3_d3d5_pixel_format_le_x1r5g5b5 = 1, - - /* - 16-Bit Pixel Format - 4 bits alpha, 4 bits red, 4 bits green, 4 bits blue - */ - nv3_d3d5_pixel_format_le_a4r4g4b4 = 2, - - /* - 16-Bit Pixel Format - 5 bits red, 6 bits green, 5 bits blue - (Required nv3tweak...) - */ - nv3_d3d5_pixel_format_le_r5g6b5 = 3, - -} nv3_d3d5_texture_pixel_format; - -/* Output format - - The output pixel format...I -*/ -typedef enum nv3_d3d5_output_pixel_format_e -{ - /* - 32-Bit Pixel Format - 8 bits irrelevant, 8 bits red, 8 bits green, 8 bits blue - */ - nv3_d3d5_output_pixel_format_le_x8r8g8b8 = 0, - - /* - 32-Bit Pixel Format - 8 bits irrelevant, 8 bits red, 8 bits green, 8 bits blue - Is this even used?? The riva can't even do 32 bit colour in 3d... - */ - nv3_d3d5_output_pixel_format_le_a8r8g8b8 = 1, - - -} nv3_d3d5_output_pixel_format; - - -/* Texture size - - NOTE: ONLY 256X256 OR LOWER ARE SUPPORTED! 2048X2048X16 TAKES UP ENTIRE VRAM OF RIVA 128 ZX... - I ASSUME THESE ARE INTERNALLY SCALED DOWN -*/ -typedef enum nv3_d3d5_texture_size_e -{ - nv3_d3d5_texture_size_1x1 = 0, - - nv3_d3d5_texture_size_2x2 = 1, - - nv3_d3d5_texture_size_4x4 = 2, - - nv3_d3d5_texture_size_8x8 = 3, - - nv3_d3d5_texture_size_16x16 = 4, - - nv3_d3d5_texture_size_32x32 = 5, - - nv3_d3d5_texture_size_64x64 = 6, - - nv3_d3d5_texture_size_128x128 = 7, - - nv3_d3d5_texture_size_256x256 = 8, - - nv3_d3d5_texture_size_512x512 = 9, - - nv3_d3d5_texture_size_1024x1024 = 10, - - // Kind of infeasible considering hardware VRAM size limitations - nv3_d3d5_texture_size_2048x2048 = 11, - - -} nv3_d3d5_texture_size; - -/* Texture Wrapping Mode for U/V Coordinate Overflow - -*/ -typedef enum nv3_d3d5_texture_wrap_mode_e -{ - // Map textures in a cylindrical fashion. - nv3_d3d5_texture_wrap_mode_cylindrical = 0, - - // Simply wrap back to the start. - nv3_d3d5_texture_wrap_mode_wrap = 1, - - // Mirror the texture. - nv3_d3d5_texture_wrap_mode_mirror = 2, - - // Clamp to the last border pixel. - nv3_d3d5_texture_wrap_mode_clamp = 3, -} nv3_d3d5_texture_wrap_mode; - - -/* This is blending but isn't really considered to be it in the GPU for some reason - What do we do with out input texel BEFORE processing it? - */ -typedef enum nv3_d3d5_dest_color_interpretation_e -{ - // Do nothing - nv3_d3d5_source_color_normal = 0, - - // Invert Colour - nv3_d3d5_source_color_inverse = 1, - - // Invert Alpha before Processing - nv3_d3d5_source_color_alpha_inverse = 2, - - // Take Alpha as 1 - nv3_d3d5_source_color_alpha_one = 3, - -} nv3_d3d5_dest_color_interpretation; - -// The full texture format structure -typedef struct nv3_d3d5_texture_format_s -{ - uint16_t color_key_color_mask; - bool color_key_enabled : 1; - nv3_d3d5_texture_pixel_format color_format : 2; - nv3_d3d5_texture_size size_min : 4; - nv3_d3d5_texture_size size_max : 4; -} nv3_d3d5_texture_format_t; - -// -// NV3 D3D5: INTERPOLATION -// - -/* - ?????? - Interpolating between mip levels? (or for texture blending) -*/ -typedef enum nv3_d3d5_interpolator_algorithm_e -{ - // Zero-order hold interpolation? - nv3_d3d5_interpolator_zoh = 0, - - // Zero-order hold (microsoft variant)? - nv3_d3d5_interpolator_zoh_ms = 1, - - // Full-order hold interpolation? - nv3_d3d5_interpolator_foh = 2, - -} nv3_d3d5_interpolator_algorithm; - -// -// NV3 D3D5: Z-BUFFER -// - -/* Probably the sorting algorithm */ -typedef enum nv3_d3d5_zbuffer_type_e -{ - // Sort based on linear distance - nv3_d3d5_zbuffer_linear = 0, - - // Sort based on distance from view frustum - nv3_d3d5_zbuffer_screen = 1, - -} nv3_d3d5_zbuffer_type; - -// NV3 D3D5: WRITE CONTROL (SHARED BETWEEN ZETA BUFFER AND ALPHA) -typedef enum nv3_d3d5_buffer_write_control_e -{ - // Never write. - nv3_d3d5_buffer_write_control_never = 0, - - // Only write the alpha. - nv3_d3d5_buffer_write_control_alpha = 1, - - // Write both alpha and the zeta-buffer. - nv3_d3d5_buffer_write_control_alpha_zeta = 2, - - // Write only the zeta-buffer - nv3_d3d5_buffer_write_control_zeta = 3, - - // Write everything (alpha+z+zeta?) - nv3_d3d5_buffer_write_control_always = 4, - - -} nv3_d3d5_buffer_write_control; - -// -// NV3 D3D5: BUFFER COMPARISON (SHARED BETWEEN ZETA BUFFER AND ALPHA CONTROL) -// - -// Todo: Which direction? (i.e. is less than p1 < p2 or p2 < p1!) -typedef enum nv3_d3d5_buffer_comparison_type_e -{ - // !!!ILLEGAL COMPARISON TYPE!!!! - nv3_d3d5_buffer_comparison_illegal = 0, - - // The (depth?) test always fails. - nv3_d3d5_buffer_comparison_always_false = 1, - - // True if less than fail. - nv3_d3d5_buffer_comparison_less_than = 2, - - // The test succeeds if equal. - nv3_d3d5_buffer_comparison_equal = 3, - - // The test succeeds if less or equal. - nv3_d3d5_buffer_comparison_less_or_equal = 4, - - // The test succeeds if greater. - nv3_d3d5_buffer_comparison_greater = 5, - - // The test succeeds if the two elements are equal. - nv3_d3d5_buffer_comparison_not_equal = 6, - - // The test succeeds if greater or equal - nv3_d3d5_buffer_comparison_greater_or_equal = 7, - - // The test always succeeds. - nv3_d3d5_buffer_comparison_always_true = 8, - -} nv3_d3d5_buffer_comparison_type; - -// -// NV3 D3D5: BLENDING -// - -/* Render Operation for Blending */ -typedef enum nv3_d3d5_blend_render_operation_e -{ - nv3_d3d5_blend_render_operation_and = 0, - - nv3_d3d5_blend_add_with_saturation = 1, - -} nv3_d3d5_blend_render_operation; - -typedef enum nv3_d3d5_blend_beta_factor_e -{ - nv3_d3d5_blend_beta_factor_srcalpha = 0, - - nv3_d3d5_blend_beta_factor_zero = 1, - -} nv3_d3d5_blend_beta_factor; - -typedef enum nv3_d3d5_blend_input0_e -{ - nv3_d3d5_blend_input0_srcalpha = 0, - - nv3_d3d5_blend_input0_zero = 1, - -} nv3_d3d5_blend_input0; - -typedef enum nv3_d3d5_blend_input1_e -{ - nv3_d3d5_blend_input1_destalpha = 0, - - nv3_d3d5_blend_input1_zero = 1, - -} nv3_d3d5_blend_input1; - -// -// NV3 D3D5: CULLING -// - -typedef enum nv3_d3d5_culling_algorithm_e -{ - // Don't cull - nv3_d3d5_culling_algorithm_none = 0, - - // Cull Clockwise around view frustum? - nv3_d3d5_culling_algorithm_clockwise = 1, - - // Cull counterclockwise around view frustum? - nv3_d3d5_culling_algorithm_counterclockwise = 2, - -} nv3_d3d5_culling_algorithm; - -/* Specular reflection parameters */ -typedef struct nv3_d3d5_specular_s -{ - uint8_t i0 : 4; - uint8_t i1 : 4; - uint8_t i2 : 4; - uint8_t i3 : 4; - uint8_t i4 : 4; - uint8_t i5 : 4; - uint8_t fog; //table fog emulation? -} nv3_d3d5_specular_t; - -// -// NV3 D3D5: MISC -// -typedef struct nv3_d3d5_texture_filter_s -{ - uint8_t spread_x; - uint8_t spread_y; - uint8_t mipmap; - uint8_t turbo; -} nv3_d3d5_texture_filter_t; - -// -// NV3 D3D5: OUTPUT CONTROL STRUCTURE -// - -/* Output Control for D3D5 Triangles */ -typedef struct nv3_d3d5_control_out_s -{ - nv3_d3d5_interpolator_algorithm ctrl_out_interpolator : 2; - uint8_t reserved : 2; - nv3_d3d5_texture_wrap_mode wrap_u : 2; // Controls wrapping mode of U texture coordinate - nv3_d3d5_texture_wrap_mode wrap_v : 2; // Controls wrapping move of V texture coordinate - nv3_d3d5_output_pixel_format output_pixel_format : 1; - bool reserved2 : 1; - nv3_d3d5_dest_color_interpretation dest_color_interpretation : 2; - nv3_d3d5_culling_algorithm culling_algorithm : 2; - bool reserved3 : 1; - nv3_d3d5_zbuffer_type zbuffer_type : 1; - nv3_d3d5_buffer_comparison_type zeta_buffer_compare : 4; - nv3_d3d5_buffer_write_control zeta_write : 3; - bool reserved4 : 1; - nv3_d3d5_buffer_write_control color_write : 3; - bool reserved5 : 1; - nv3_d3d5_blend_render_operation blend_rop : 1; - nv3_d3d5_blend_input0 blend_input0 : 1; - nv3_d3d5_blend_input1 blend_input1 : 1; -} nv3_d3d5_control_out_t; - -typedef struct nv3_d3d5_alpha_control_s -{ - uint8_t alpha_key; - nv3_d3d5_buffer_comparison_type zeta_buffer_compare : 4; - uint32_t reserved : 20; -} nv3_d3d5_alpha_control_t; - -// -// NV3 D3D5: Triangle Coordinates -// -typedef struct nv3_d3d5_coordinate_s -{ - nv3_d3d5_specular_t specular_reflection_parameters; - nv3_color_expanded_t color; // YOU HAVE TO FLIP THE ENDIANNESS. NVIDIA??? WHAT??? - - // Seems more plausible for these specifically to be floats. - // Also makes my life easier... - float x; // X coordinate in 3d space of the triangle - float y; // Y coordinate in 3d space of the triangle - float z; // Z coordinate in 3d space of the triangle - float m; // 1/W for projection - float u; // U coordinate within texture for the (top left?) of the triangle where sampling starts. - float v; // V coordinate within texture for the (top left?) of the triangle where sampling starts. -} nv3_d3d5_coordinate_t; - -typedef struct nv3_object_class_017 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - uint32_t texture_offset; - nv3_d3d5_texture_format_t texture_format; - nv3_d3d5_texture_filter_t texture_filter; - nv3_color_expanded_t fog_color; // Alpha is ignored here! - nv3_d3d5_control_out_t control_out; - nv3_d3d5_alpha_control_t alpha_control; - - nv3_d3d5_coordinate_t coordinate_points[128]; // The points we are rendering. - /* No placeholder needed, it really is that long. */ -} nv3_d3d5_accelerated_triangle_with_zeta_buffer_t; - - -// Color and Zeta Buffer algorithm -typedef struct nv3_zeta_buffer_s -{ - nv3_color_expanded_t color; - uint32_t zeta; // 16 bits z, 8 bits stenciul -} nv3_zeta_buffer_t; - -typedef struct nv3_object_class_018 -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; - uint32_t set_notify; - nv3_d3d5_control_out_t control_out; - nv3_d3d5_alpha_control_t alpha_control; - nv3_coord_16_t point; - nv3_zeta_buffer_t zeta[8]; -} nv3_point_with_zeta_buffer_t; - -/* 0x19, 0x1A, 0x1B don't exist */ - -/* WHY IS THE FORMAT DIFFERENT TO THE REST OF THE GPU? - They are making it look like a bitfield but it's hex? - - THEY ARE ALL LITTLE ENDIAN -*/ -typedef enum nv3_object_class_01C_pixel_format_e -{ - // Y8P4 - // 12-bits (Y8 - Planar YUV 8 bits (Y value only), 4 bits of indexed colour too? - nv3_image_in_memory_pixel_format_le_y8_p4 = 0x1010000, - - // Y16P2 - // 16-bits (Y16) - Planar YUV 16 bits (Y value only), 2 bits of indexed colour too? - nv3_image_in_memory_pixel_format_le_y16_p2 = 0x1010101, - - /* 1 unused bit, 555 15-bit format, p2(?) */ - nv3_image_in_memory_pixel_format_x1r5g5b5_p2 = 0x1000000, - - // X8G8B8R8, 24-bit colour (or 24-bit colour with alpha) - nv3_image_in_memory_pixel_format_x8g8b8r8 = 0x1, -} nv3_object_class_01C_pixel_format; - -typedef struct nv3_object_class_01C -{ - nv3_class_ctx_switch_method_t set_notify_ctx_dma; // Set notifier context for DMA (context switch) - uint32_t set_notify; // Set notifier - nv3_object_class_01C_pixel_format format; // Completely different from everything else - uint32_t pitch; // 16-bit - uint32_t linear_address; // 22-bit: Linear address in vram. -} nv3_image_in_memory_t; - -// See envytools. This is where we finally end up after this mess, it allows parameters to be passed to the methods. -typedef struct nv3_grobj_s -{ - uint32_t grobj_0; - uint32_t grobj_1; - uint32_t grobj_2; - uint32_t grobj_3; -} nv3_grobj_t; -// TODO: PATCHCORDS!!!! TO LINK ALL OF THIS TOGETHER!!! - -// PIO Subchannel info -#define NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE 0x0010 -#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START 0x0012 -#define NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END 0x0017 \ No newline at end of file diff --git a/src/include/86box/nv/render/vid_nv3_render.h b/src/include/86box/nv/render/vid_nv3_render.h deleted file mode 100644 index cca3761be..000000000 --- a/src/include/86box/nv/render/vid_nv3_render.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 headers for rendering - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#pragma once - -/* Core */ -void nv3_render_current_bpp(); -void nv3_render_current_bpp_dfb_8(uint32_t address); -void nv3_render_current_bpp_dfb_16(uint32_t address); -void nv3_render_current_bpp_dfb_32(uint32_t address); - -/* Pixel */ -void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj); -uint8_t nv3_render_read_pixel_8(nv3_coord_16_t position, nv3_grobj_t grobj); -uint16_t nv3_render_read_pixel_16(nv3_coord_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj); - -/* Address */ -uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj); -uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer); - -/* Colour Conversion */ -uint32_t nv3_render_get_palette_index(uint8_t index); // Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) -uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded); // Convert a colour to A1R10G10B10 for chroma key purposes. -nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj); // Convert a colour to full RGB10 format from the current working format. -uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color); // Convert a colour from the current working format to RGB10 format. - -/* ROP */ -uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop); - -/* Pattern */ -void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1); - -/* Primitives */ -void nv3_render_rect(nv3_coord_16_t position, nv3_coord_16_t size, uint32_t color, nv3_grobj_t grobj); // Render an A (unclipped) GDI rect -void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj); // Render a B (clipped) GDI rect. - -/* Chroma */ -bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj); - -/* Blit */ -void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj); -void nv3_render_blit_screen2screen(nv3_grobj_t grobj); - -/* GDI */ -void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj); -void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj); /* GDI Type-E: Clipped 1bpp colour-expanded bitmap */ - -/* DMA */ -void nv3_perform_dma_m2mf(nv3_grobj_t grobj); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv.h b/src/include/86box/nv/vid_nv.h deleted file mode 100644 index 83f94c167..000000000 --- a/src/include/86box/nv/vid_nv.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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 real Nvidia driver. - * Implements components shared by all variants of the Nvidia GPUs (hopefully to be) supported by 86Box. - * - * This driver draws on collaborative work by many people over many years. - * - * Credit to: - * - * - Marcelina Kościelnicka (envytools) https://envytools.readthedocs.io/en/latest/ - * - fuel (PCBox developer) https://github.com/PCBox/PCBox - * - nouveau developers https://nouveau.freedesktop.org/ - * - Utah GLX developers https://utah-glx.sourceforge.net/ - * - XFree86 developers https://www.xfree86.org/ - * - xemu developers https://github.com/xemu-project/xemu - * - RivaTV developers https://rivatv.sourceforge.net (esp. https://rivatv.sourceforge.net/stuff/riva128.txt) - * - Nvidia for leaking their driver symbols numerous times ;^) https://nvidia.com - * - Vort (Bochs GeForce fork) https://github.com/Vort/Bochs/tree/geforce - * - People who prevented me from giving up (various) - * - * Authors: Connor Hyde / starfrost - * - * Copyright 2024-2025 Connor Hyde - */ -#ifdef EMU_DEVICE_H // what - -//TODO: split this all into nv1, nv3, nv4... -#include <86box/log.h> -#include <86box/i2c.h> -#include <86box/vid_ddc.h> -#include <86box/timer.h> -#include <86box/vid_svga.h> -#include <86box/vid_svga_render.h> -#include <86box/nv/vid_nv_rivatimer.h> - -void nv_log_set_device(void* device); -void nv_log(const char *fmt, ...); - -// Verbose logging level. -void nv_log_verbose_only(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 NV_PCI_DEVICE_NV1 0x0008 // Nvidia NV1 -#define NV_PCI_DEVICE_NV1_VGA 0x0009 // Nvidia NV1 VGA core -#define NV_PCI_DEVICE_NV2 0x0010 // Nvidia NV2 / Mutara V08 (cancelled) -#define NV_PCI_DEVICE_NV3 0x0018 // Nvidia NV3 (Riva 128) -#define NV_PCI_DEVICE_NV3T 0x0019 // Nvidia NV3T (Riva 128 ZX) -#define NV_PCI_DEVICE_NV4 0x0020 // Nvidia NV4 (RIVA TNT) - -#define NV_CHIP_REVISION_NV1_A0 0x0000 // 1994 -#define NV_CHIP_REVISION_NV1_B0 0x0010 // 1995 -#define NV_CHIP_REVISION_NV1_C0 0x0020 // 1995-96? - -#define NV_CHIP_REVISION_NV3_A0 0x0000 // January 1997 -#define NV_CHIP_REVISION_NV3_B0 0x0010 // October 1997 -#define NV_CHIP_REVISION_NV3_C0 0x0020 // 1998 - -// Architecture IDs -#define NV_ARCHITECTURE_NV1 1 // NV1/STG2000 -#define NV_ARCHITECTURE_NV2 2 // Nvidia 'Mutara V08' -#define NV_ARCHITECTURE_NV3 3 // Riva 128 -#define NV_ARCHITECTURE_NV4 4 // Riva TNT and later - -#define NV_MAX_BUF_SIZE_X 1920 // Maximum buffer size, X -#define NV_MAX_BUF_SIZE_Y 1200 // Maximum buffer size, Y - -typedef enum nv_bus_generation_e -{ - // NV1 - Prototype version - nv_bus_vlb = 0, - - // NV1 - // NV3 - nv_bus_pci = 1, - - // NV3 - nv_bus_agp_1x = 2, - - // NV3T - // NV4 - nv_bus_agp_2x = 3, - -} nv_bus_generation; - -// PCI configuration -typedef struct nv_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; -} nv_pci_config_t; - -// NV Base -typedef struct nv_base_s -{ - rom_t vbios; // NVIDIA/OEm VBIOS - nv_pci_config_t pci_config; // PCI configuration - // move to nv3_cio_t? - svga_t svga; // SVGA core (separate to nv3) - Weitek licensed - uint32_t vram_amount; // The amount of VRAM - void* log; // new logging engine - // 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) - NV3 ONLY - 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) - uint32_t gpu_revision; // GPU Stepping - double pixel_clock_frequency; // Frequency used for pixel clock# - double refresh_time; // Rough estimation of refresh rate, for when we can present the screen - double refresh_clock; // Time since the last refresh - rivatimer_t* pixel_clock_timer; // Timer for measuring pixel clock - bool pixel_clock_enabled; // Pixel Clock Enabled - stupid crap used to prevent us enabling the timer multiple times - double memory_clock_frequency; // Source Frequency for PTIMER - rivatimer_t* memory_clock_timer; // Timer for measuring memory/gpu clock - - // VCLK / NVCLK do not have timers set here. - pc_timer_t* nv4_vclk_timer; // NV4+ MCLK (Video RAM) timer - - bool memory_clock_enabled; // Memory Clock Enabled - stupid crap used to prevent us eanbling the timer multiple times - void* i2c; // I2C for monitor EDID - void* ddc; // Display Data Channel for EDID - bool agp_enabled; // AGP Enabled (for debugging) - bool agp_sba_enabled; // AGP Sideband Addressing enabled - - // - // DEBUG UI STUFF - // - bool debug_dba_enabled; // Debug DBA override - uint32_t debug_dba; // Debug DBA -} 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)(void); // 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); - - -#endif \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv1.h b/src/include/86box/nv/vid_nv1.h deleted file mode 100644 index be6895c8b..000000000 --- a/src/include/86box/nv/vid_nv1.h +++ /dev/null @@ -1,167 +0,0 @@ -/* - * 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. - * - * Quadratic Heaven - * - * Authors: Connor Hyde - * - * Copyright 2024-2025 Connor Hyde - */ - -#pragma once - -#include -#include - -extern const device_config_t nv1_config[]; // Config for RIVA 128 (revision A/B) - -// -// PCI Bus Information -// - -#define NV1_PCI_BAR0_SIZE 0xFFFFFF - -// -// VRAM -// -#define NV1_VRAM_SIZE_1MB 0x100000 -#define NV1_VRAM_SIZE_2MB 0x200000 -#define NV1_VRAM_SIZE_4MB 0x400000 - -// Video BIOS -#define NV1_VBIOS_E3D_2X00 "roms/video/nvidia/nv1/Diamond_Edge_3D_2x00.BIN" -#define NV1_VBIOS_E3D_3X00 "roms/video/nvidia/nv1/Diamond_Edge_3D_3400_BIOS_M27C256.BIN" - -// -// PMC -// -#define NV1_PMC_BOOT_0 0x0 -#define NV1_PMC_BOOT_0_REVISION 0 - -#define NV1_PMC_BOOT_0_REVISION_A 0x0 // Prototype (1994) -#define NV1_PMC_BOOT_0_REVISION_B1 0x0 // Prototype (early 1995) -#define NV1_PMC_BOOT_0_REVISION_B2 0x0 // Prototype (mid 1995) -#define NV1_PMC_BOOT_0_REVISION_B3 0x0 // Final? -#define NV1_PMC_BOOT_0_REVISION_C 0x0 // Final? - -#define NV1_PMC_BOOT_0_IMPLEMENTATION 8 -#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV0 0x1 // Nvidia Hardware Simulator (1993-1994) -#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV1_D32 0x2 // NV1 + DRAM + SGS-Thomson STG-1732/1764 DAC -#define NV1_PMC_BOOT_0_IMPLEMENTATION_NV1_V32 0x3 // NV1 + VRAM + SGS-Thomson STG-1732/1764 DAC -#define NV1_PMC_BOOT_0_IMPLEMENTATION_PICASSO 0x4 // NV1 + VRAM + NV 128-bit DAC - - -// Defines the NV architecture version (NV1/NV2/...) -#define NV1_PMC_BOOT_0_ARCHITECTURE 16 -#define NV1_PMC_BOOT_0_ARCHITECTURE_NV0 0x0 // Nvidia Hardware Simulator (1993-1994) -#define NV1_PMC_BOOT_0_ARCHITECTURE_NV1 0x1 // NV1 (1995) -#define NV1_PMC_BOOT_0_ARCHITECTURE_NV2 0x2 // Mutara (1996, cancelled) - -#define NV1_PMC_DEBUG_0 0x80 - -#define NV1_PMC_INTR_0 0x100 -#define NV1_PMC_INTR_EN_0 0x140 - -#define NV1_PMC_INTR_EN_0_INTA 0 -#define NV1_PMC_INTR_EN_0_INTB 4 -#define NV1_PMC_INTR_EN_0_INTC 8 -#define NV1_PMC_INTR_EN_0_INTD 12 - -#define NV1_PMC_INTR_EN_0_DISABLED 0x0 -#define NV1_PMC_INTR_EN_0_HARDWARE 0x1 -#define NV1_PMC_INTR_EN_0_SOFTWARE 0x2 -#define NV1_PMC_INTR_EN_0_ALL 0x3 // (HARDWARE | SOFTWARE) - -#define NV1_PMC_INTR_READ 0x160 - -//TODO: DEFINE bits -#define NV1_PMC_ENABLE 0x200 - -// -// PRAMIN -// - -#define NV1_RAMIN_START 0x100000 - -// -// PAUTH -// Scary nvidia mode -// - -// Read only -#define NV1_PAUTH_DEBUG_0 0x605080 -#define NV1_PAUTH_DEBUG_0_BREACH_DETECTED 0 -#define NV1_PAUTH_DEBUG_0_EEPROM_INVALID 4 - -#define NV1_PAUTH_CHIP_TOKEN_0 0x605400 -#define NV1_PAUTH_CHIP_TOKEN_1 0x605404 -#define NV1_PAUTH_PASSWORD_0(i) 0x605800+(i*16) -#define NV1_PAUTH_PASSWORD_1(i) 0x605804+(i*16) -#define NV1_PAUTH_PASSWORD_2(i) 0x605808+(i*16) -#define NV1_PAUTH_PASSWORD_3(i) 0x60580C+(i*16) - -#define NV1_PAUTH_PASSWORD_SIZE 128 - -// -// PFB -// - -#define NV1_PFB_BOOT_0 0x600000 - -#define NV1_PFB_BOOT_0_RAM_AMOUNT 0 -#define NV1_PFB_BOOT_0_RAM_AMOUNT_1MB 0x0 -#define NV1_PFB_BOOT_0_RAM_AMOUNT_2MB 0x1 -#define NV1_PFB_BOOT_0_RAM_AMOUNT_4MB 0x2 - -// -// PEXTDEV -// - -#define NV1_STRAPS 0x608000 -#define NV1_STRAPS_STRAP_VENDOR 0 - -// -// PRAM+RAMIN -// - -#define NV1_PRAM_CONFIG 0x602200 -#define NV1_PRAM_CONFIG_SIZE 0 -#define NV1_PRAM_CONFIG_12KB 0 -#define NV1_PRAM_CONFIG_20KB 1 -#define NV1_PRAM_CONFIG_36KB 2 -#define NV1_PRAM_CONFIG_68KB 3 - -// Position of RAMPW in RAMIN -#define NV1_RAMPW_POSITION_CONFIG0 0x2c00 -#define NV1_RAMPW_POSITION_CONFIG1 0x4c00 -#define NV1_RAMPW_POSITION_CONFIG2 0x8c00 -#define NV1_RAMPW_POSITION_CONFIG3 0x10c00 - -// Static RAMPW mirror -#define NV1_PRAMPW 0x606000 -#define NV1_RAMPW_SIZE 0x400 - -// -// PROM -// -#define NV1_PROM 0x601000 -#define NV1_PROM_SIZE 32768 - -// Structures -typedef struct nv1_s -{ - nv_base_t nvbase; // Base Nvidia structure -} nv1_t; - -// Device Core -void nv1_init(); -void nv1_close(void* priv); -void nv1_speed_changed(void *priv); -void nv1_draw_cursor(svga_t* svga, int32_t drawline); -void nv1_recalc_timings(svga_t* svga); -void nv1_force_redraw(void* priv); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv3.h b/src/include/86box/nv/vid_nv3.h deleted file mode 100644 index 3726ecc03..000000000 --- a/src/include/86box/nv/vid_nv3.h +++ /dev/null @@ -1,1764 +0,0 @@ -/* - * 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. - * - * Welcome to what happens when a single person demands that their overly complicated "vision" of a design be implemented - * with absolutely no compromise. This is true lunacy. - * - * Explanation of what is being done here, and how this all works, hopefully posted on the 86Box blog. - * Notes specific to a subsystem in the header or c file for that subsystem - * Also check the doc folder for some more notres - * - * vid_nv3.h: NV3 Architecture Hardware Reference (open-source) - * Last updated: 12 April 2025 (STILL WORKING ON IT!!!) - * - * Authors: Connor Hyde - * - * Copyright 2024-2025 Connor Hyde - */ - -#pragma once -#include <86box/nv/classes/vid_nv3_classes.h> -#include <86box/nv/render/vid_nv3_render.h> - -// The GPU base structure -extern const device_config_t nv3_config[]; // Config for RIVA 128 (revision A/B) -extern const device_config_t nv3t_config[]; // Config for RIVA 128 ZX (revision C) - -#define NV3_MMIO_SIZE 0x1000000 // Max MMIO size - -#define NV3_LFB_RAMIN_MIRROR_START 0x400000 // Mirror of ramin (VERIFY ON HARDWARE) -#define NV3_LFB_MIRROR_START 0x800000 // The second half of LFB(?) -#define NV3_LFB_RAMIN_START 0xC00000 // RAMIN mapping start -#define NV3_LFB_MAPPING_SIZE 0x400000 // Size of RAMIN - -// THere are 64 DMA channels grouped into 8 "channels" with 8 "subchannels" each. You can only use one channel at a time. An arbitrary number of 8 objects can be submitted. -// Channel 0 is always taken up by NV drivers. - -#define NV3_DMA_CHANNELS 8 -#define NV3_DMA_SUBCHANNELS_PER_CHANNEL 8 - -#define NV3_DMA_CHANNELS_TOTAL 0x7F // This is also used somewhere despite there only being 8*8 = 64 channels - -#define NV3_LAST_VALID_GRAPHICS_OBJECT_ID 0x1F - -// The class ids are represented with 5 bits in PGRAPH, but 7 bits in PFIFO! -// What... -#define NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID 0x40 -#define NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID 0x5F - -// Default value for the boot information register. -// Depends on the chip -#define NV3_BOOT_REG_REV_A00 0x00030100 // todo: format is wrong(?) for nv3a, fix it later -#define NV3_BOOT_REG_REV_B00 0x00030110 -#define NV3_BOOT_REG_REV_C00 0x00030120 - -// various vbioses for testing -// Coming soon: MIROmagic Premium BIOS (when I get mine dumped) -//todo: move to hash system - -// Oldest one of these - August 8, 1997 - -// This particular VBIOS is very early and has certain bugs -#define NV3_VBIOS_STB_V128_V160 "roms/video/nvidia/nv3/stb-velocity128-1.6.bin" // STB Velocity 128 3D (Riva 128) Ver.1.60 -#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 -#define NV3T_VBIOS_DIAMOND_V330_V182B "roms/video/nvidia/nv3/nv3t182b.rom" // Diamond Multimedia Viper V330 8M BIOS - Version 1.82B -#define NV3T_VBIOS_ASUS_V170 "roms/video/nvidia/nv3/A170D03T.rom" // ASUS AGP-V3000 ZXTV BIOS - V1.70D.03 (C) 1996-98 Nvidia Corporation -#define NV3T_VBIOS_REFERENCE_CEK_V171 "roms/video/nvidia/nv3/BIOS_49_Riva 128" // Reference BIOS: RIVA 128 ZX BIOS - V1.71B-N (C) 1996-98 NVidia Corporation -#define NV3T_VBIOS_REFERENCE_CEK_V172 "roms/video/nvidia/nv3/vgasgram.rom" // Reference(?) BIOS: RIVA 128 ZX BIOS - V1.72B (C) 1996-98 NVidia Corporation - -// The default VBIOS to use -#define NV3_VBIOS_DEFAULT NV3_VBIOS_ERAZOR_V15403 - -// Temporary, will be loaded from settings -#define NV3_VRAM_SIZE_2MB 0x200000 // 2MB -#define NV3_VRAM_SIZE_4MB 0x400000 // 4MB -#define NV3_VRAM_SIZE_8MB 0x800000 // NV3T only -// There is also 1mb supported by the card but it was never used - -// 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_AGP_CAPABILITIES_POINTER 0x34 - -#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 - -// -// AGP configuration -// - -#define NV3_AGP_START 0x44 - -// stupid stupid pci system -#define NV3_AGP_CAPABILITIES_START 0x44 -#define NV3_AGP_CAPABILITIES_CAP_ID 0x44 -#define NV3_AGP_CAPABILITIES_CAP_ID_AGP 0x02 // "AGP" -#define NV3_AGP_CAPABILITIES_NEXT_PTR 0x45 -#define NV3_AGP_CAPABILITIES_AGP_VERSION 0x46 -#define NV3_AGP_CAPABILITIES_AGP_VERSION_MINOR 0 -#define NV3_AGP_CAPABILITIES_AGP_VERSION_MAJOR 4 -#define NV3_AGP_CAPABILITIES_H 0x47 - -#define NV3_AGP_STATUS_RATE 0x48 -#define NV3_AGP_STATUS_RATE_1X_SUPPORTED 0x1 -#define NV3_AGP_STATUS_RATE_2X_SUPPORTED 0x2 - -#define NV3_AGP_STATUS_BYTE1 0x49 -#define NV3_AGP_STATUS_BYTE1_SBA 1 -#define NV3_AGP_STATUS_SBA_SUPPORTED 0x0 -#define NV3_AGP_STATUS_SBA_UNSUPPORTED 0x1 -#define NV3_AGP_STATUS_MAX_REQUESTS 0x4B -#define NV3_AGP_STATUS_MAX_REQUESTS_AMOUNT 4 - -#define NV3_AGP_COMMAND 0x4C -#define NV3_AGP_COMMAND_DATA_RATE 0 -#define NV3_AGP_COMMAND_DATA_RATE_1X 0x1 -#define NV3_AGP_COMMAND_DATA_RATE_2X 0x2 -#define NV3_AGP_COMMAND_BYTE1 0x4D -#define NV3_AGP_COMMAND_BYTE1_ENABLE 0 -#define NV3_AGP_COMMAND_BYTE1_ENABLE_DISABLED 0x0 -#define NV3_AGP_COMMAND_BYTE1_ENABLE_ENABLED 0x1 -#define NV3_AGP_COMMAND_SBA_ENABLE 1 -#define NV3_AGP_COMMAND_SBA_ENABLE_DISABLED 0x0 -#define NV3_AGP_COMMAND_SBA_ENABLE_ENABLED 0x1 -#define NV3_AGP_COMMAND_REQUEST_DEPTH 0x4F - -#define NV3_AGP_END 0x4F - -// -// ACPI (NV3T only) -// TODO: IMPLEMENT THIS!!!!!! -// -#define NV3_POWER_CAP_ID 0x60 -#define NV3_POWER_NEXT_PTR 0x61 -#define NV3_POWER_VERSION 0x62 - -// "The RIVA128ZX does not physically change its power consumption when -// POWER_STATE is modified." -#define NV3_POWER_STATE 0x64 - -#define NV3_POWER_STATE_D0 0x0 -#define NV3_POWER_STATE_D3HOT 0x3 - -// 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_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 // Determines if PGRAPH is enabled. -#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 - -#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_DEBUG_0 0x1084 // Bus Control Debug -#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_MINIMUM_GUARANTEED_DEPTH 0x7C - -#define NV3_PFIFO_DELAY_0 0x2040 // PFIFO Config Register -#define NV3_PFIFO_DEBUG_0 0x2080 // PFIFO Debug Register -#define NV3_PFIFO_CACHE0_ERROR_PENDING 0 -#define NV3_PFIFO_CACHE1_ERROR_PENDING 4 - -#define NV3_PFIFO_INTR 0x2100 // FIFO - Interrupt Status -#define NV3_PFIFO_INTR_EN 0x2140 // FIFO - Interrupt Enable - -// PFIFO interrupts -#define NV3_PFIFO_INTR_CACHE_ERROR 0 -#define NV3_PFIFO_INTR_RUNOUT 4 -#define NV3_PFIFO_INTR_RUNOUT_OVERFLOW 8 -#define NV3_PFIFO_INTR_DMA_PUSHER 12 -#define NV3_PFIFO_INTR_DMA_PTE 16 - -#define NV3_PFIFO_CONFIG_0 0x2200 -#define NV3_PFIFO_CONFIG_0_DMA_FETCH 8 - -#define NV3_PFIFO_CONFIG_RAMHT 0x2210 // Hashtable for graphics objects config -#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS 12 // 15:12 -#define NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS_DEFAULT 0x0 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE 16 // 17:16 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE_4K 0x0 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE_8K 0x1 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE_16K 0x2 -#define NV3_PFIFO_CONFIG_RAMHT_SIZE_32K 0x3 - -#define NV3_PFIFO_CONFIG_RAMFC 0x2214 -#define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS 9 -#define NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS_DEFAULT 0x1C00 // Hardcoded in silicon? -#define NV3_PFIFO_CONFIG_RAMRO 0x2218 -#define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS 9 -#define NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS_DEFAULT 0x1E00 // Hardcoded in silicon? -#define NV3_PFIFO_CONFIG_RAMRO_SIZE 16 -#define NV3_PFIFO_CONFIG_RAMRO_SIZE_512B 0x0 -#define NV3_PFIFO_CONFIG_RAMRO_SIZE_8K 0x1 - -#define NV3_PFIFO_RUNOUT_STATUS 0x2400 -#define NV3_PFIFO_RUNOUT_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_RUNOUT_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_RUNOUT_STATUS_FULL 8 -#define NV3_PFIFO_RUNOUT_PUT 0x2410 -#define NV3_PFIFO_RUNOUT_PUT_ADDRESS 3 // 9:3 if small ramfc(?) otherwise 12:3 -#define NV3_PFIFO_RUNOUT_GET 0x2420 -#define NV3_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 - -#define NV3_PFIFO_RUNOUT_RAMIN_ERR 28 // bit to or with - -#define NV3_PFIFO_CACHE0_SIZE 1 // This is for software-injected notified only! -#define NV3_PFIFO_CACHE1_SIZE_REV_AB 32 -#define NV3_PFIFO_CACHE1_SIZE_REV_C 64 -#define NV3_PFIFO_CACHE1_SIZE_MAX NV3_PFIFO_CACHE1_SIZE_REV_C -#define NV3_PFIFO_CACHE_REASSIGNMENT 0x2500 - -#define NV3_PFIFO_CACHE0_PUSH_ENABLED 0x3000 -#define NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID 0x3004 -#define NV3_PFIFO_CACHE0_PUT 0x3010 -#define NV3_PFIFO_CACHE0_STATUS 0x3014 -#define NV3_PFIFO_CACHE0_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE0_STATUS_FULL 8 -#define NV3_PFIFO_CACHE0_PUT_ADDRESS 2 // 1 bit -#define NV3_PFIFO_CACHE0_PULL0 0x3040 -#define NV3_PFIFO_CACHE0_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD 8 -#define NV3_PFIFO_CACHE0_PULLER_CTX_STATE 0x3050 -#define NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY 4 // 1=dirty 0=clean -#define NV3_PFIFO_CACHE0_GET 0x3070 -#define NV3_PFIFO_CACHE0_GET_ADDRESS 2 // 1 bit -// Current channel context - cache1 -#define NV3_PFIFO_CACHE0_CTX 0x3080 - -#define NV3_PFIFO_CACHE0_METHOD_START 0x3100 -#define NV3_PFIFO_CACHE0_METHOD_END 0x3200 -#define NV3_PFIFO_CACHE0_METHOD_ADDRESS 2 // 12:2 -#define NV3_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 // 15:13 -#define NV3_PFIFO_CACHE1_PUSH_ENABLED 0x3200 -#define NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID 0x3204 -#define NV3_PFIFO_CACHE1_PUT 0x3210 -#define NV3_PFIFO_CACHE1_PUT_ADDRESS 2 // 6:2 -#define NV3_PFIFO_CACHE1_STATUS 0x3214 -#define NV3_PFIFO_CACHE1_STATUS_RANOUT 0 // 1 if we fucked up -#define NV3_PFIFO_CACHE1_STATUS_EMPTY 4 // 1 if ramro is empty -#define NV3_PFIFO_CACHE1_STATUS_FULL 8 -#define NV3_PFIFO_CACHE1_DMA_STATUS 0x3218 -#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE 0 -#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE_IDLE 0x00 -#define NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING 0x01 -#define NV3_PFIFO_CACHE1_DMA_CONFIG_0 0x3220 -#define NV3_PFIFO_CACHE1_DMA_CONFIG_1 0x3224 -#define NV3_PFIFO_CACHE1_DMA_CONFIG_2 0x3228 -#define NV3_PFIFO_CACHE1_DMA_CONFIG_3 0x322C -#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE 0 // The type of bus we are sending over -#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_PCI 0x02 // The type of bus we are sending over -#define NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_AGP 0x03 // The type of bus we are sending over - -// Why does a gpu need its own translation lookaside buffer and pagetable format. Are they crazy -// Seems to be the same format as the notifier engine -#define NV3_PFIFO_CACHE1_DMA_TLB_TAG 0x3230 -#define NV3_PFIFO_CACHE1_DMA_TLB_PTE 0x3234 // pagetable entry for dma -#define NV3_PFIFO_CACHE1_DMA_TLB_PTE_IS_PRESENT 1 -#define NV3_PFIFO_CACHE1_DMA_TLB_FRAME_ADDRESS 12 // 31:12 -#define NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE 0x3238 // Base of pagetable for DMA -#define NV3_PFIFO_CACHE1_PULL0 0x3240 -//todo: merge stuff -#define NV3_PFIFO_CACHE1_PULL0_ENABLED 0 -#define NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE 4 -#define NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD 8 // 0=software, 1=hardware -#define NV3_PFIFO_CACHE1_PULLER_CTX_STATE 0x3250 -#define NV3_PFIFO_CACHE1_PULLER_CTX_STATE_DIRTY 4 -#define NV3_PFIFO_CACHE1_GET 0x3270 -#define NV3_PFIFO_CACHE1_GET_ADDRESS 2 // 6:2 - -// Current channel context - cache1 -#define NV3_PFIFO_CACHE1_CTX_START 0x3280 -#define NV3_PFIFO_CACHE1_CTX_END 0x32F0 - -#define NV3_PFIFO_CACHE1_METHOD_START 0x3300 -#define NV3_PFIFO_CACHE1_METHOD_END 0x3400 -#define NV3_PFIFO_CACHE1_METHOD_ADDRESS 2 // 12:2 -#define NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 // 15:13 - - -#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_ALARM 0 // Alarm interrupt -#define NV3_PTIMER_INTR_EN 0x9140 -#define NV3_PTIMER_NUMERATOR 0x9200 -#define NV3_PTIMER_DENOMINATOR 0x9210 -#define NV3_PTIMER_TIME_0_NSEC 0x9400 // nanoseconds [31:5] -#define NV3_PTIMER_TIME_1_NSEC 0x9410 // nanoseconds [28:0] -#define NV3_PTIMER_ALARM_NSEC 0x9420 // nanoseconds [31:5] -#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 // VGA stuff written from main GPU -#define NV3_PRMVIO_END 0xC0400 -#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_DELAY 0x100044 -#define NV3_PFB_DEBUG_0 0x100080 // Debug register for pfb -#define NV3_PFB_GREEN_0 0x1000C0 - -#define NV3_PFB_CONFIG_0 0x100200 // Framebuffer interface config register 0 - -// What is this lol -// ??? Part of the memory timings -#define NV3_PFB_RTL 0x100300 -#define NV3_PFB_CONFIG_0_RESOLUTION 0 -// 1=40 horiz. resolution -// i assume it can be divided by some kind of divisor to produce the vertical resolution (e.g. 3/2 or multiply by 2/3) to get the final -// horiz is 32*value -// theoretically it should support resolutions from 40-2560 horiz - -// WHAT ARE THE TIMINGS: ARE THEY IN THE VBIOS? -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_320 0xA -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_400 0xD -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_480 0xF -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_512 0x10 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_640 0x14 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_800 0x19 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_960 0x1E -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1024 0x20 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1152 0x24 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1280 0x28 -#define NV3_PFB_CONFIG_0_HORIZ_RESOLUTION_1600 0x32 - -#define NV3_PFB_CONFIG_0_PIXEL_DEPTH 8 -#define NV3_PFB_CONFIG_0_DEPTH_8BPP 0x1 -#define NV3_PFB_CONFIG_0_DEPTH_16BPP 0x2 -#define NV3_PFB_CONFIG_0_DEPTH_32BPP 0x3 - -#define NV3_PFB_CONFIG_1 0x100204 // Framebuffer interface config register 1 -#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 0x11FFFF -#define NV3_PALT_START 0x120000 // ??? but it exists -#define NV3_PALT_END 0x12FFFF -#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 -// PGRAPH Core - -#define NV3_PGRAPH_MAX_BUFFERS 4 - -// For these debug registers, 0=Disabled, 1=Enabled - -// Debug 0: General -#define NV3_PGRAPH_DEBUG_0 0x400080 -#define NV3_PGRAPH_DEBUG_0_STATE_IN_RESET 0 -#define NV3_PGRAPH_DEBUG_0_AP_PIPE_IN_RESET 1 -#define NV3_PGRAPH_DEBUG_0_CACHE_IN_RESET 2 -#define NV3_PGRAPH_DEBUG_0_3D_PIPE_IN_RESET 3 -#define NV3_PGRAPH_DEBUG_0_BULK_READS 4 -#define NV3_PGRAPH_DEBUG_0_TILING 16 -#define NV3_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D 20 -#define NV3_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D 21 -#define NV3_PGRAPH_DEBUG_0_DRAWDIR_AUT 24 -#define NV3_PGRAPH_DEBUG_0_DRAWDIR_Y 25 -#define NV3_PGRAPH_DEBUG_0_ALPHA_ABORT 28 - -// Debug 1: Registers -#define NV3_PGRAPH_DEBUG_1 0x400084 -#define NV3_PGRAPH_DEBUG_1_VOLATILE_RESET_LAST 0 -#define NV3_PGRAPH_DEBUG_1_DMA_ACTIVITY_CANCEL 4 -#define NV3_PGRAPH_DEBUG_1_TURBO3D_2X 8 -#define NV3_PGRAPH_DEBUG_1_TURBO3D_4X 9 -#define NV3_PGRAPH_DEBUG_1_TRIANGLE_OPS 12 -#define NV3_PGRAPH_DEBUG_1_TRIANGLE_CLIP_OPS 13 -#define NV3_PGRAPH_DEBUG_1_INSTANCE 16 -#define NV3_PGRAPH_DEBUG_1_CONTEXT 20 -#define NV3_PGRAPH_DEBUG_1_CACHE_FLUSH 24 -#define NV3_PGRAPH_DEBUG_1_ZCLAMP 28 - -// Debug 2: 3D Pipeline -#define NV3_PGRAPH_DEBUG_2 0x400088 -#define NV3_PGRAPH_DEBUG_2_AVOID_READMODIFYWRITE_BLEND 0 -#define NV3_PGRAPH_DEBUG_2_DPWR_FIFO 8 -#define NV3_PGRAPH_DEBUG_2_BILINEAR_FILTERING_3D 12 -#define NV3_PGRAPH_DEBUG_2_ANISOTROPIC_FILTERING_3D 13 -#define NV3_PGRAPH_DEBUG_2_FOG 14 -#define NV3_PGRAPH_DEBUG_2_LIGHTING 15 // Not sure what this does, maybe hardware t&l was planned -#define NV3_PGRAPH_DEBUG_2_BILINEAR_FILTERING_2D 16 -#define NV3_PGRAPH_DEBUG_2_ANISOTROPIC_FILTERING_2D 17 -#define NV3_PGRAPH_DEBUG_2_D3D_COALESCE 20 // coalesce reads/writes for d3d class 0x17 -#define NV3_PGRAPH_DEBUG_2_D3D_COALESCE_POINT_ZETA 22 // class 0x18 coalesce -#define NV3_PGRAPH_DEBUG_2_PREFETCH 24 -#define NV3_PGRAPH_DEBUG_2_VOLATILE_RESET 28 - -// Debug 3: Zeta & Alpha Buffer -#define NV3_PGRAPH_DEBUG_3 0x40008C -#define NV3_PGRAPH_DEBUG_3_CULLING 0 -#define NV3_PGRAPH_DEBUG_3_FAST_DATA_D3D 4 -#define NV3_PGRAPH_DEBUG_3_FAST_DATA_STRETCH 5 -#define NV3_PGRAPH_DEBUG_3_ZFLUSH 7 -#define NV3_PGRAPH_DEBUG_3_AUTOZFLUSH_POINT_ZETA 8 -#define NV3_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D 9 -#define NV3_PGRAPH_DEBUG_3_SLOT_CONFLICT_POINT_ZETA 10 // Slot conflict handling for POINT_ZETA (class 0x18) -#define NV3_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D 11 // Slot conflict handling for D3D5_TRI (class 0x17) -#define NV3_PGRAPH_DEBUG_3_EARLY_ZABORT 12 -#define NV3_PGRAPH_DEBUG_3_TRIANGLE_END_FLUSH 13 -#define NV3_PGRAPH_DEBUG_3_ZFIFO_NOOP 14 // ??? -#define NV3_PGRAPH_DEBUG_3_DITHER 15 -#define NV3_PGRAPH_DEBUG_3_FORCE_COLOR_BUFFER_READ 16 -#define NV3_PGRAPH_DEBUG_3_FORCE_ZETA_BUFFER_READ 17 -#define NV3_PGRAPH_DEBUG_3_DATA_CHECK 20 -#define NV3_PGRAPH_DEBUG_3_DATA_CHECK_FAIL 21 -#define NV3_PGRAPH_DEBUG_3_FORMAT_CHECK 22 -#define NV3_PGRAPH_DEBUG_3_ALPHA_CHECK 24 - -// Interrupt stuff -#define NV3_PGRAPH_INTR_0 0x400100 -#define NV3_PGRAPH_INTR_0_VBLANK 8 // Fired every frame -#define NV3_PGRAPH_INTR_0_VBLANK_ENABLED 0x1 // Is the vblank interrupt enabled? -#define NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY 28 // Fired on software notification - -#define NV3_PGRAPH_INTR_1 0x400104 -#define NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING 0 // Software or invalid method -#define NV3_PGRAPH_INTR_1_INVALID_DATA 4 // Invalid data. Not sure when this would be triggered. -#define NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY 12 // Tried to notify while a notify was pending. -#define NV3_PGRAPH_INTR_1_CTXSW_NOTIFY 16 // Notify fired for software context - -#define NV3_PGRAPH_INTR_EN_0 0x400140 // Interrupt Control for PGRAPH #1 -//todo: add what this does -#define NV3_PGRAPH_INTR_EN_1 0x400144 // Interrupt Control for PGRAPH #2 (it can receive two at onc) -#define NV3_PGRAPH_CTX_SWITCH 0x400180 // Holds the current PGRAPH context, switched by context switching - -/* Contextual information for pgraph */ -#define NV3_PGRAPH_CTX_SWITCH_COLOR_FORMAT 0 // Holds the current color format used for drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_ALPHA 3 // Holds a boolean indicating in if alpha transparency is currently enabled in drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_MONO_FORMAT 8 // Holds the current color format used for monochome drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_DAC_BYPASS 9 // Holds if PRAMDAC should be bypassed, and an external DAC drawn. -#define NV3_PGRAPH_CTX_SWITCH_Z_WRITE 12 // Holds if we should write back to the zbuffer. -#define NV3_PGRAPH_CTX_SWITCH_CHROMA_KEY 13 // Holds the current chroma mask used for drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_PLANE_MASK 14 // Holds the current plane mask used for drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_USER_CLIP 15 // Holds the user-specified clipping information used for drawing operations. -#define NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER 16 // Holds the buffer ID used for drawing operation (i.e. which bpixel/bpitch/boffset index to use) -#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER0_ENABLED 20 // Holds a boolean indicating if buffer 0 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER1_ENABLED 21 // Holds a boolean indicating if buffer 1 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER2_ENABLED 22 // Holds a boolean indicating if buffer 2 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CTX_SWITCH_DST_BUFFER3_ENABLED 23 // Holds a boolean indicating if buffer 3 can be used as the destination for a drawing operation. -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG 24 // ROP type - -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0 0x0 // Reserved -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_DST_SRC 0x1 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_DST 0x2 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_SRC 0x3 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_DST 0x4 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_SRC 0x5 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_DST 0x6 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_SRC0 0x7 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_SRC1 0x8 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_SRC_PAT 0x9 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_SRC 0xA -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_PAT 0xB -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_SRC 0xC -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_PAT 0xD -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_PAT_SRC 0xE -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD1 0xF -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST 0x10 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_DST_SRC 0x11 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_DST 0x12 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_PAT 0x13 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_PAT_SRC 0x14 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_PAT 0x15 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD2 0x16 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS 0x17 // Ignore -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD0 0x18 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_SRC_DST 0x19 -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_DST_SRC 0x1A -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD1 0x1B -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD2 0x1C -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_SRC 0x1D -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD3 0x1E -#define NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD4 0x1F - -#define NV3_PGRAPH_CTX_SWITCH_VOLATILE 31 // HUH - -#define NV3_PGRAPH_CONTEXT_CONTROL 0x400190 // DMA context control -#define NV3_PGRAPH_CONTEXT_USER 0x400194 // Current DMA context state, may rename -#define NV3_PGRAPH_CONTEXT_CACHE(i) 0x4001A0+(i*4) // Context Cache -#define NV3_PGRAPH_CONTEXT_CACHE_SIZE 8 -// TODO: CLIP0/CLIP1 (8 clips min/max in 32bits) -#define NV3_PGRAPH_ABS_UCLIP_XMIN 0x40053C // Clip X minimum -#define NV3_PGRAPH_ABS_UCLIP_XMAX 0x400540 // Clip X maximum -#define NV3_PGRAPH_ABS_UCLIP_YMIN 0x400544 // Clip Y minimum -#define NV3_PGRAPH_ABS_UCLIP_YMAX 0x400548 // Clip Y maximum -#define NV3_PGRAPH_SRC_CANVAS_MIN 0x400550 // Minimum Source Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_SRC_CANVAS_MAX 0x400554 // Maximum Source Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_DST_CANVAS_MIN 0x400558 // Minimum Destination Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_DST_CANVAS_MAX 0x40055C // Maximum Destination Canvas for Blit, Y=30:16, X=10:0 -#define NV3_PGRAPH_PATTERN_COLOR_0_RGB 0x400600 -#define NV3_PGRAPH_PATTERN_COLOR_0_ALPHA 0x400604 -#define NV3_PGRAPH_PATTERN_COLOR_1_RGB 0x400608 -#define NV3_PGRAPH_PATTERN_COLOR_1_ALPHA 0x40060C // pattern color -#define NV3_PGRAPH_PATTERN_BITMAP_HIGH 0x400610 // pattern bitmap [31:0] -#define NV3_PGRAPH_PATTERN_BITMAP_LOW 0x400614 // pattern bitmap [63:32] -#define NV3_PGRAPH_PATTERN_SHAPE 0x400618 -#define NV3_PGRAPH_ROP3 0x400624 // ROP3 -#define NV3_PGRAPH_PLANE_MASK 0x400628 -#define NV3_PGRAPH_CHROMA_KEY 0x40062C -#define NV3_PGRAPH_BETA 0x400640 // Beta factor (30:23 fractional, 22:0 before fraction) -#define NV3_PGRAPH_DMA 0x400680 -#define NV3_PGRAPH_INSTANCE 0x400688 // Current instance (?) - -// Current notification object for pgraph -#define NV3_PGRAPH_NOTIFY 0x400684 // Notifier for PGRAPH -#define NV3_PGRAPH_NOTIFY_INSTANCE 0 -#define NV3_PGRAPH_NOTIFY_REQUEST_PENDING 16 -#define NV3_PGRAPH_NOTIFY_REQUEST_TYPE 20 -#define NV3_PGRAPH_NOTIFY_REQUEST_TYPE_HARDWARE 0x0 // anything else is software - -#define NV3_PGRAPH_CLIP0_MIN 0x400690 // Clip for Blitting 0 Min -#define NV3_PGRAPH_CLIP0_MAX 0x400694 // Clip for Blitting 0 Max -#define NV3_PGRAPH_CLIP1_MIN 0x400698 // Clip for Blitting 1 Min -#define NV3_PGRAPH_CLIP1_MAX 0x40069C // Clip for Blitting 1 Max -#define NV3_PGRAPH_CLIP_MISC 0x4006A0 // Regions/Render/Complex mode -#define NV3_PGRAPH_FIFO_ACCESS 0x4006A4 // Is PGRAPH enabled? -#define NV3_PGRAPH_FIFO_ACCESS_DISABLED 0x0 -#define NV3_PGRAPH_FIFO_ACCESS_ENABLED 0x1 -#define NV3_PGRAPH_CLIP_MISC 0x4006A0 // Miscellaneous clipping information -#define NV3_PGRAPH_STATUS 0x4006B0 // Current PGRAPH status -#define NV3_PGRAPH_TRAPPED_ADDRESS 0x4006B4 -#define NV3_PGRAPH_TRAPPED_DATA 0x4006B8 -#define NV3_PGRAPH_TRAPPED_INSTANCE 0x4006BC - -#define NV3_PGRAPH_DMA_INTR_0 0x401100 // PGRAPH DMA Interrupt Status -#define NV3_PGRAPH_DMA_INTR_INSTANCE 0 -#define NV3_PGRAPH_DMA_INTR_PRESENT 4 -#define NV3_PGRAPH_DMA_INTR_PROTECTION 8 -#define NV3_PGRAPH_DMA_INTR_LINEAR 12 -#define NV3_PGRAPH_DMA_INTR_NOTIFY 16 -#define NV3_PGRAPH_DMA_INTR_EN_0 0x401140 // PGRAPH DMA Interrupt Enable 0 - -#define NV3_PGRAPH_DPRAM_SIZE 12288 // Size of the internal texture cache - -// 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_CLASS01_BETA_START 0x410000 // Beta blending factor -#define NV3_PGRAPH_CLASS01_BETA_END 0x411FFF -#define NV3_PGRAPH_CLASS02_ROP_START 0x420000 // Blending render operation used at final pixel/fragment generation stage -#define NV3_PGRAPH_CLASS02_ROP_END 0x421FFF -#define NV3_PGRAPH_CLASS03_COLORKEY_START 0x430000 // Color key for image -#define NV3_PGRAPH_CLASS03_COLORKEY_END 0x431FFF -#define NV3_PGRAPH_CLASS04_PLANEMASK_START 0x440000 // Plane mask (for clipping?) -#define NV3_PGRAPH_CLASS04_PLANEMASK_END 0x441FFF -#define NV3_PGRAPH_CLASS05_CLIP_START 0x450000 // clipping, probably class 23 -#define NV3_PGRAPH_CLASS05_CLIP_END 0x451FFF -#define NV3_PGRAPH_CLASS06_PATTERN_START 0x460000 // presumably a blend pattern -#define NV3_PGRAPH_CLASS06_PATTERN_END 0x461FFF -#define NV3_PGRAPH_CLASS07_RECTANGLE_START 0x470000 // also class 25 - that's black [NV1] -#define NV3_PGRAPH_CLASS07_RECTANGLE_END 0x471FFF // also class 25 - that's black [NV1] -#define NV3_PGRAPH_CLASS08_POINT_START 0x480000 // A single point -#define NV3_PGRAPH_CLASS08_POINT_END 0x481FFF -#define NV3_PGRAPH_CLASS09_LINE_START 0x490000 // A line -#define NV3_PGRAPH_CLASS09_LINE_END 0x491FFF -#define NV3_PGRAPH_CLASS0A_LIN_START 0x4A0000 // A lin - a line without its starting or ending pixels -#define NV3_PGRAPH_CLASS0A_LIN_END 0x4A1FFF -#define NV3_PGRAPH_CLASS0B_TRIANGLE_START 0x4B0000 // A triangle [NV1 variant] - in NV1 this was converted to a quad patch -#define NV3_PGRAPH_CLASS0B_TRIANGLE_END 0x4B1FFF -#define NV3_PGRAPH_CLASS0C_GDITEXT_START 0x4C0000 // Windows 95/NT GDI text acceleration -#define NV3_PGRAPH_CLASS0C_GDITEXT_END 0x4C1FFF - -#define NV3_PGRAPH_CLASS0D_MEM2MEM_XFER_START 0x4D0000 // memory to memory transfer (not sure about which class this is) -#define NV3_PGRAPH_CLASS0D_MEM2MEM_XFER_END 0x4D1FFF -#define NV3_PGRAPH_CLASS0E_IMAGE2MEM_XFER_SCALED_START 0x4E0000 // class 55, 56 -#define NV3_PGRAPH_CLASS0F_IMAGE2MEM_XFER_SCALED_END 0x4E1FFF - -#define NV3_PGRAPH_CLASS10_BLIT_START 0x500000 // Blit 2d image from memory -#define NV3_PGRAPH_CLASS10_BLIT_END 0x501FFF - -#define NV3_PGRAPH_CLASS11_CPU2MEM_IMAGE_START 0x510000 // Used for class 33, 34, 54 -#define NV3_PGRAPH_CLASS11_CPU2MEM_IMAGE_END 0x511FFF -#define NV3_PGRAPH_CLASS12_CPU2MEM_BITMAP_START 0x520000 // not sure, might depend on format -#define NV3_PGRAPH_CLASS12_CPU2MEM_BITMAP_END 0x521FFF - -#define NV3_PGRAPH_CLASS14_IMAGE2MEM_XFER_START 0x540000 // send image to vram, not sure what class -#define NV3_PGRAPH_CLASS14_IMAGE2MEM_XFER_END 0x541FFF -#define NV3_PGRAPH_CLASS15_CPU2MEM_STRETCHED_START 0x550000 // stretched cpu->vram transfer, 54 -#define NV3_PGRAPH_CLASS15_CPU2MEM_STRETCHED_END 0x551FFF - -#define NV3_PGRAPH_CLASS17_D3D5TRI_ZETA_START 0x570000 // [NV3] Copy a direct3d 5.0 accelerated triangle to the zeta buffer -#define NV3_PGRAPH_CLASS17_D3D5TRI_ZETA_END 0x571FFF -#define NV3_PGRAPH_CLASS18_POINTZETA_START 0x580000 // possibly class 69 -#define NV3_PGRAPH_CLASS18_POINTZETA_END 0x581FFF - -#define NV3_PGRAPH_CLASS1C_MEM2IMAGE_START 0x5C0000 // class 55, 56, 62, 63? -#define NV3_PGRAPH_CLASS1C_MEM2IMAGE_END 0x5C1FFF - - -#define NV3_PGRAPH_REGISTER_END 0x401FFF // end of pgraph registers -#define NV3_PGRAPH_REAL_END 0x5C1FFF - -// PRMCIO is redirected to SVGA subsystem -#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_FIFO_THRESHOLD 0x680238 -#define NV3_PVIDEO_FIFO_BURST_LENGTH 0x68023C -#define NV3_PVIDEO_OVERLAY 0x680244 -#define NV3_PVIDEO_OVERLAY_VIDEO_IS_ON 0 -#define NV3_PVIDEO_OVERLAY_KEY_ENABLED 4 -#define NV3_PVIDEO_OVERLAY_FORMAT 8 // 0 = CCIR, 1 = YUY2 - -#define NV3_PVIDEO_END 0x6802FF -#define NV3_PRAMDAC_START 0x680300 - -#define NV3_PRAMDAC_CURSOR_START 0x680300 - -#define NV3_PRAMDAC_CURSOR_SIZE_X 32 -#define NV3_PRAMDAC_CURSOR_SIZE_Y 32 - -#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 -#define NV3_PRAMDAC_GENERAL_CONTROL_565_MODE 12 - -// 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_DAC_START 0x681200 -#define NV3_USER_DAC_PALETTE_START 0x6813C6 -#define NV3_USER_DAC_PIXEL_MASK 0x6813C6 -#define NV3_USER_DAC_READ_MODE_ADDRESS 0x6813C7 //bit0=read/write mode? -#define NV3_USER_DAC_WRITE_MODE_ADDRESS 0x6813C8 -#define NV3_USER_DAC_PALETTE_DATA 0x6813C9 -#define NV3_USER_DAC_PALETTE_SIZE 768 -#define NV3_USER_DAC_PALETTE_END 0x6813C9 -#define NV3_USER_DAC_END 0x681FFF - -#define NV3_USER_START 0x800000 // Mapping for the area where objects are submitted into the FIFO (up to 0x880000?) -#define NV3_USER_END 0xFFFFFF - -// easier name -#define NV3_OBJECT_SUBMIT_START NV3_USER_START -#define NV3_OBJECT_SUBMIT_SUBCHANNEL 13 -#define NV3_OBJECT_SUBMIT_CHANNEL 16 -#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_RAMIN_START 0x1C00000 - -#define NV3_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects -#define NV3_RAMIN_RAMHT_END 0x1C00FFF -#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF -#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF -#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF -#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF - -/* OBSOLETE AREA for AUDIO probably. DO NOT USE! */ -#define NV3_RAMIN_RAMAU_START 0x1C01000 -#define NV3_RAMIN_RAMAU_END 0x1C01BFF -#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels -#define NV3_RAMIN_RAMFC_END 0x1C01DFF -#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF -#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF -#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions -#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF -#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF -#define NV3_RAMIN_RAMRO_END 0x1C01FFF -#define NV3_RAMIN_RAMRM_START 0x1C02000 -#define NV3_RAMIN_RAMRM_END 0x1C02FFF - -// I'm not sure if this can be moved. -// 4K of "PRAM" is listed at 0x6000. -#define NV3_RAMIN_OFFSET_CURSOR 0x6000 - -#define NV3_RAMIN_END 0x1FFFFFF - -// not done - -// CRTC/CIO (0x3b0-0x3df) - -#define NV3_CRTC_REGISTER_INDEX_MONO 0x3B4 -#define NV3_CRTC_REGISTER_MONO 0x3B5 // Currently Selected CRTC Register - Monochrome - -#define NV3_CRTC_DATA_OUT 0x3C0 -#define NV3_CRTC_MISCOUT 0x3C2 - -#define NV3_RMA_REGISTER_START 0x3D0 -#define NV3_RMA_REGISTER_END 0x3D3 - -#define NV3_CRTC_REGISTER_INDEX 0x3D4 -#define NV3_CRTC_REGISTER_CURRENT 0x3D5 - -#define NV3_CRTC_REGISTER_WTF 0x3D8 - -// 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_START_DISABLED 5 -#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, licensed from weitek (25-63) -#define NV3_CRTC_REGISTER_RPC0 0x19 // 7:5 - [10:8] of CRTC. 4:0 - [20:16] of 21-bit display buffer address -#define NV3_CRTC_REGISTER_RPC1 0x1A // bit7=hsync enabled, bit6=vsync enabled, bit4="compatible text", bit2=large screen, bit1=6bit palette width (>1280) -#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_CURSOR_ADDR0 0x30 // Cursor high 21:16 -#define NV3_CRTC_REGISTER_CURSOR_ADDR1 0x31 // Cursor low (1:0 = enable) 15:11 - -#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_RL0 0x34 -#define NV3_CRTC_REGISTER_RL1 0x35 -#define NV3_CRTC_REGISTER_RMA 0x38 // REAL MODE ACCESS! -#define NV3_CRTC_REGISTER_I2C 0x3E -#define NV3_CRTC_REGISTER_I2C_GPIO 0x3F - -#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_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 - -/* - STRUCTURES FOR THE GPU START HERE - OBJECT CLASS & RENDERING RELATED STUFF IS IN VID_NV3_CLASSES.H -*/ - -//todo: pixel format - -// Master Control -typedef struct nv3_pmc_s -{ - /* - Holds chip manufacturing information at bootup. - Current specification (may change later): pre-packed for convenience - - Revision A: - 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# - Revision B/c: - write this later - */ - 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; - -// 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; - uint32_t debug_0; // debug stuff - uint32_t config_0; // Framebuffer width, etc. - uint32_t config_1; - uint32_t green; - uint32_t delay; - uint32_t rtl; // Part of the memory timings -} nv3_pfb_t; - -// -// DMA & Notifier Engine -// All singing all dancing all happy happy bullshit to send dma transfers literally anywhere in your computer -// - -// Not a notification status, because it's a 16-bit enum -// C23 fixes this -#define NV3_NOTIFICATION_STATUS_DONE_OK 0x0 -#define NV3_NOTIFICATION_STATUS_IN_PROGRESS 0xFF -#define NV3_NOTIFICATION_STATUS_ERROR 0x100 - -#define NV3_NOTIFICATION_INFO_ADJUST 0 // Wut -#define NV3_NOTIFICATION_PT_PRESENT 16 // Determines if the pagetable exists. -#define NV3_NOTIFICATION_TARGET 24 // Determines where this notification goes -#define NV3_DMA_TARGET_NODE_VRAM 0 // VRAM target for DMA -#define NV3_DMA_TARGET_NODE_CART 1 // "Cartridge" target for dma, only mentioned in a few places, !!! NV2 LEFTOVER !!! -#define NV3_DMA_TARGET_NODE_PCI 2 // Send the data to the host system over PCI -#define NV3_DMA_TARGET_NODE_AGP 3 // Send the data to the host system over AGP - -#define NV3_NOTIFICATION_PAGE_IS_PRESENT 0 // Determines if the page really exists -#define NV3_NOTIFICATION_PAGE_ACCESS 1 // Determines the page access type -#define NV3_NOTIFICATION_PAGE_ACCESS_READ 0x0 // If this is set, the page is read only -#define NV3_NOTIFICATION_PAGE_ACCESS_READ_WRITE 0x1 // If this is set, the page is read/write -#define NV3_NOTIFICATION_PAGE_FRAME_ADDRESS 12 // The pageframe to use - - - -// Core notification structure -typedef struct nv3_notification_s -{ - uint64_t nanoseconds; - uint32_t info32; - uint16_t info16; - uint16_t status; -} nv3_notification_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 debug_0; - uint32_t interrupt_status; // Interrupt status - uint32_t interrupt_enable; // Interrupt enable - nv3_pbus_rma_t rma; -} nv3_pbus_t; - -typedef struct nv3_pfifo_cache_s -{ - bool push0; // Can we even access this cache? - uint8_t put_address; // Trigger a DMA into the value you put here. - uint8_t get_address; // Trigger a DMA from the value you put here into where you were going. - uint8_t channel; // The DMA channel ID of this cache. - uint32_t status; - uint32_t pull0; - uint32_t context[NV3_DMA_SUBCHANNELS_PER_CHANNEL]; // Only one of these exists for cache0 - - /* cache1 only - do we even need to emulate this? - */ - uint32_t dma_status; // 0x3218 - bool dma_enabled; // 0x3220 bit0 - bool dma_is_busy; // 0x3220 bit4 - - uint32_t dma_state; // Corresponds to PFIFO_CACHE1_DMA0 - uint32_t dma_length; // Corresponds to PFIFO_CACHE1_DMA1 - uint32_t dma_address; // Corresponds to PFIFO_CACHE1_DMA2 - uint8_t dma_target_node; // Corresponds to PFIFO_CACHE1_DMA3 depends on card bus - uint8_t dma_tlb_tag; - uint8_t dma_tlb_pte; // DMA Engine - Translation Lookaside Buffer - uint8_t dma_tlb_pt_base; // DMA Engine - TLB Pagetable Base Addres - uint16_t method_address; // address of the method (i.e. what method it is) - uint16_t method_subchannel; // subchannel - bool context_is_dirty; - - /* TODO */ -} nv3_pfifo_cache_t; - -typedef struct nv3_pfifo_cache_entry_s -{ - uint16_t method; // method id depending on class (offset from entry channel start in ramin) - uint8_t subchannel; - uint32_t data; // is this the context - -} nv3_pfifo_cache_entry_t; - -// Command submission to PGRAPH -typedef struct nv3_pfifo_s -{ - uint32_t interrupt_status; // Interrupt status - uint32_t interrupt_enable; // Interrupt enable - uint32_t dma_delay_retry; // DMA Delay/Retry - uint32_t debug_0; // Cache Debug register - uint32_t config_0; - uint32_t ramht_config; // RAMHT config - uint32_t ramfc_config; // RAMFC config - uint32_t ramro_config; // RAMRO config - // Runout stuff - uint32_t runout_put; // 8:3 if RAMRO=512b, otherwise 12:3 - uint32_t runout_get; // 8:3 if RAMRO=512b, otherwise 12:3 - - // Cache stuff - uint32_t cache_reassignment; // Allow context switching - nv3_pfifo_cache_t cache0_settings; - nv3_pfifo_cache_t cache1_settings; - - nv3_pfifo_cache_entry_t cache0_entry; // It only has 1 entry - nv3_pfifo_cache_entry_t cache1_entries[NV3_PFIFO_CACHE1_SIZE_MAX]; // ONLY 32 USED ON REVISION A/B CARDS -} 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 - - uint8_t user_pixel_mask; // pixel mask for DAC lookup - uint32_t user_read_mode_address; // user read mode address - uint32_t user_write_mode_address; // user write mode address - uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entries, 1 byte for r,g,b = 768 bytes - - uint32_t cursor_address; // cursor address start - nv3_coord_16_t cursor_start; -} nv3_pramdac_t; - -/* Holds DMA channel context information */ -typedef struct NV3_PGRAPH_CTX_SWITCH_s -{ - /* TODO */ -} NV3_PGRAPH_CTX_SWITCH_t; - -typedef struct nv3_pgraph_context_control_s -{ - /* TODO */ -} nv3_pgraph_context_control_t; - -/* DMA object context info - Context uploaded from CACHE0/CACHE1 by DMA Puller -*/ -typedef struct nv3_pgraph_context_user_s -{ - union - { - uint32_t value; - - struct - { - bool reserved3 : 1; - uint8_t channel : 7; - uint8_t reserved2 : 3; - uint8_t class_id : 5; - uint8_t subchannel : 3; - uint16_t reserved : 13; - }; - }; -} nv3_pgraph_context_user_t; - -typedef struct nv3_pgraph_dma_settings_s -{ - /* TODO */ -} nv3_pgraph_dma_settings_t; - -typedef struct nv3_pgraph_clip_misc_settings_s -{ - /* TODO */ -} nv3_pgraph_clip_misc_settings_t; - -typedef struct nv3_pgraph_status_s -{ - bool overall_busy : 1; // Is anything busy? - uint8_t reserved : 3; - bool xy_logic_busy : 1; // Determines if the line drawing/xy/vector stuff is busy. - uint8_t reserved2 : 3; - bool port_notify_busy : 1; // Mediaport?/PIO? notifier engine busy - uint8_t reserved3 : 3; - bool port_register_busy : 1; - uint8_t reserved4 : 3; - bool port_dma_busy : 1; // Mediaport?/PIO? DMA engine busy - bool dma_engine_busy : 1; // DMA engine busy - uint8_t reserved5 : 2; - bool dma_notify_busy : 1; // Are the notifiers busy? - uint8_t reserved6 : 3; - bool engine_3d_busy : 1; // 3d engine busy? - bool engine_cache_busy : 1; // PFIFO CACHE0/CACHE1 busy? - bool engine_zfifo_busy : 1; // ZFIFO (zeta buffer? z buffer?) busy? - bool port_user_busy : 1; // User context switch? - uint8_t reserved7 : 3; - -} nv3_pgraph_status_t; - -/* All of this B* stuff is registers at 400630..40065c and 4006a8 in reality, easier to implement it like this - BPixel = Buffer Pixel Format -*/ -#define NV3_BPIXEL_FORMAT 0 -#define NV3_BPIXEL_FORMAT_IS_VALID 2 -#define NV3_BPIXEL_DEPTH 4 - -typedef enum nv3_pgraph_bpixel_format_e -{ - // Y16 - bpixel_fmt_y16 = 0, - // 8-bit colour - bpixel_fmt_8bit = 1, - // 16-bit colour - bpixel_fmt_16bit = 2, - // 32-bit colour (ARGB) - bpixel_fmt_32bit = 3, -} nv3_pgraph_bpixel_format; - -typedef enum nv3_pgraph_destination_buffer_e -{ - pgraph_dest_buffer0 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER0_ENABLED), - pgraph_dest_buffer1 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER1_ENABLED), - pgraph_dest_buffer2 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER2_ENABLED), - pgraph_dest_buffer3 = (1 << NV3_PGRAPH_CTX_SWITCH_DST_BUFFER3_ENABLED), -} nv3_pgraph_destination_buffer; - -// Graphics Subsystem -typedef struct nv3_pgraph_s -{ - uint32_t debug_0; - uint32_t debug_1; - uint32_t debug_2; - uint32_t debug_3; - 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 - uint32_t interrupt_status_dma; // Interrupt status for DMA - uint32_t interrupt_enable_dma; // Interrupt enable for DMA - - uint32_t context_switch; // TODO: Make this a struct, it's just going to be enormous lol. - nv3_pgraph_context_control_t context_control; - NV3_PGRAPH_CTX_SWITCH_t context_user_submit; - nv3_pgraph_context_user_t context_user; - uint32_t context_cache[NV3_PGRAPH_CONTEXT_CACHE_SIZE]; // DMA context cache (nv3_pgraph_context_user_t array?) - - // UCLIP stuff - uint32_t abs_uclip_xmin; - uint32_t abs_uclip_xmax; - uint32_t abs_uclip_ymin; - uint32_t abs_uclip_ymax; - // Canvas stuff - nv3_coord_16_bigy_t src_canvas_min; - nv3_coord_16_bigy_t src_canvas_max; - nv3_coord_16_bigy_t dst_canvas_min; - nv3_coord_16_bigy_t dst_canvas_max; - // Pattern stuff - nv3_color_expanded_t pattern_color_0_rgb; // ignore alpha - uint32_t pattern_color_0_alpha; // only 7:0 relevant - nv3_color_expanded_t pattern_color_1_rgb; // ignore alpha - uint32_t pattern_color_1_alpha; // only 7:0 relevant - uint64_t pattern_bitmap; // pattern bitmap for blit. it's alwaus 64 bits to simplify pixel rendering code - uint32_t pattern_shape; // may need to be an enum - 0=8x8, 1=64x1, 2=1x64 - uint32_t plane_mask; // only 7:0 relevant - uint32_t chroma_key; // color key - uint32_t beta_factor; - nv3_pgraph_dma_settings_t dma_settings; - uint8_t rop; // Current GDI Ternary Render Operation - // SURFACE STUFF - PGRAPH CAN OPERATE ON 4 SURFACES/BUFFERS AT A TIME - uint32_t boffset[NV3_PGRAPH_MAX_BUFFERS]; // 22-bit linear VRAM offset for the start of a buffer. - uint16_t bpitch[NV3_PGRAPH_MAX_BUFFERS]; // 12-bit linear VRAM offset for the pitch of a buffer - uint32_t bpixel[NV3_PGRAPH_MAX_BUFFERS]; // Pixel format for each possible surfaces. - // CLIP - nv3_pgraph_clip_misc_settings_t clip_misc_settings; - uint32_t notifier; - bool notify_pending; // Determines if a notification is pending. - /* Are these even used */ - nv3_coord_16_bigy_t clip0_min; - nv3_coord_16_bigy_t clip0_max; - nv3_coord_16_bigy_t clip1_min; - nv3_coord_16_bigy_t clip1_max; - /* idk */ - nv3_coord_16_t clip_start; // Start of the clipping region - nv3_coord_16_t clip_size; // Size of the clipping region. - bool fifo_access; // Determines if PGRAPH can access PFIFO. - nv3_pgraph_status_t status; // Current status of the 3D engine. - uint32_t trapped_address; - uint32_t trapped_data; - uint32_t instance; // no idea what this is but possibly an object context - uint32_t trapped_instance; - uint8_t dpram[NV3_PGRAPH_DPRAM_SIZE]; // Internal vertex/texturea cache. - - /* This area is used for holding universal representations of the U* registers, which are actually mapped into MMIO */ - struct nv3_object_class_001 beta_factor_class; - struct nv3_object_class_002 rop_class; - struct nv3_object_class_003 chroma_key_class; - struct nv3_object_class_004 plane_mask_class; - struct nv3_object_class_005 clipping_rectangle; - struct nv3_object_class_006 pattern; - struct nv3_object_class_007 rectangle; - struct nv3_object_class_008 point; - struct nv3_object_class_009 line; - struct nv3_object_class_00A lin; - struct nv3_object_class_00B triangle; - struct nv3_object_class_00C win95_gdi_text; - /* These are here so we can hold the current state of the image draw */ - uint32_t win95_gdi_text_bit_count; - nv3_coord_16_t win95_gdi_text_current_position; - struct nv3_object_class_00D m2mf; - struct nv3_object_class_00E scaled_image_from_memory; - struct nv3_object_class_010 blit; - struct nv3_object_class_011 image; - nv3_coord_16_t image_current_position; /* This is here so we can hold the current state of the image */ - struct nv3_object_class_012 bitmap; - struct nv3_object_class_014 transfer2memory; - struct nv3_object_class_015 stretched_image_from_cpu; - struct nv3_object_class_017 d3d5_tri; - struct nv3_object_class_018 point_zeta_buffer; - struct nv3_object_class_01C image_in_memory; -} 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; // PTIMER Interrupt status - uint32_t interrupt_enable; // PTIMER Interrupt enable - uint32_t clock_numerator; // PTIMER (tick?) numerator - uint32_t clock_denominator; // PTIMER (tick?) denominator - uint64_t time; // time - uint32_t alarm; // The value of time when there should be an alarm -} nv3_ptimer_t; - -// Object name is just a uint32_t identifier it doesn't need a struct -// This is how the context is represented in ramin -// IN PGRAPH IT IS DIFFERENT! ONLY 5 BITS FOR THE CLASS ID! WHY? -typedef struct nv3_ramin_context_s -{ - union - { - uint32_t context; - - struct - { - uint16_t ramin_offset; - uint8_t class_id : 7; - bool is_rendering : 1; - uint8_t channel : 7; - bool reserved : 1; - }; - }; -} nv3_ramin_context_t; - -// Graphics object hashtable for specific DMA [channel, subchannel] pair -typedef struct nv3_ramin_ramht_subchannel_s -{ - uint32_t name; // must be >4096 - - // Contextual information. - // See the above union. - nv3_ramin_context_t context; -} nv3_ramin_ramht_subchannel_t; - -// Graphics object hashtable -typedef struct nv3_ramin_ramht_s -{ - nv3_ramin_ramht_subchannel_t subchannels[NV3_DMA_CHANNELS][NV3_DMA_SUBCHANNELS_PER_CHANNEL]; -} nv3_ramin_ramht_t; - - -typedef enum nv3_ramin_ramro_reason_e -{ - nv3_runout_reason_illegal_access = 0, - - // PFIFO CACHE0/CACHE1 were turned off, so the graphics object could not be processed. - nv3_runout_reason_no_cache_available = 1, - - // Ran out of CACHE0 & CACHE1 space. - nv3_runout_reason_cache_ran_out = 2, - - nv3_runout_reason_free_count_overrun = 3, - - nv3_runout_reason_caught_lying = 4, - - // Access reserved by pagetable - nv3_runout_reason_reserved_access = 5, - -} nv3_ramin_ramro_reason; - -// context for unused channels -typedef struct nv3_ramin_ramfc_s -{ - -} nv3_ramin_ramfc_t; - -// RAM for AUDIO - RevisionA ONLY -typedef struct nv_ramin_ramau_s -{ - -} nv3_ramin_ramau_t; - -typedef struct nv3_ramin_s -{ - -} nv3_ramin_t; - - -typedef struct nv3_pvideo_s -{ - uint32_t interrupt_status; // Interrupt status - uint32_t interrupt_enable; // Interrupt enable - uint32_t fifo_threshold; // FIFO threshold - uint32_t fifo_burst_size; // FIFO burst size - uint32_t overlay_settings; // Overlay settings -} 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; // OEM Configuration - - // 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 submission - - 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_ramin_ramht_t ramht; // hashtable for PGRAPH objects - // (ramro does not need a struct) - nv3_ramin_ramfc_t ramfc; // context for unused channels - nv3_ramin_ramau_t ramau; // auxillary weirdnes - nv3_ramin_t pramin; // INstance memory for graphics 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 object -extern nv3_t* nv3; - -/* - *FUNCTIONS* for the GPU core start here - Functions for PGRAPH objects are in vid_nv3_classes.h -*/ - -// Device Core -void* nv3_init(const device_t *info); -void nv3_close(void* priv); -void nv3_speed_changed(void *priv); -void nv3_draw_cursor(svga_t* svga, int32_t drawline); -void nv3_recalc_timings(svga_t* svga); -void nv3_force_redraw(void* priv); - -/* BAR0 GPU MMIO read */ -void nv3_update_mappings(void); // Update memory 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 - -/* BAR1 Dumb Framebuffer Read */ -uint8_t nv3_dfb_read8(uint32_t addr, void* priv); // Write 8-bit DFB -uint16_t nv3_dfb_read16(uint32_t addr, void* priv); // Write 16-bit DFB -uint32_t nv3_dfb_read32(uint32_t addr, void* priv); // Write 32-bit DFB -void nv3_dfb_write8(uint32_t addr, uint8_t val, void* priv); // Write 8-bit DFB -void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv); // Write 16-bit DFB -void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv); // Write 32-bit DFB - -uint8_t nv3_svga_read(uint16_t addr, void* priv); // Read SVGA compatibility registers -void nv3_svga_write(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 - -bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value); // Read arbitration so we can read/write to the structures in the first 64k of ramin -bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value); // Write arbitration so we can read/write to the structures in the first 64k of ramin - -// RAMIN functions -uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel); -bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel_id, uint8_t subchannel_id); -#ifndef RELEASE_BUILD -void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); -#endif - -uint32_t nv3_ramfc_read(uint32_t address); -void nv3_ramfc_write(uint32_t address, uint32_t value); -uint32_t nv3_ramro_read(uint32_t address); -void nv3_ramro_write(uint32_t address, uint32_t value); -uint32_t nv3_ramht_read(uint32_t address); -void nv3_ramht_write(uint32_t address, uint32_t value); - -// 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_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) - -// Reads from vbios are 8bit -uint8_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); - -// 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_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 -// TODO: RAMHT, RAMFC...or maybe handle it inside of nv3_ramin_* - -// GPU subsystems - -// NV3 PMC -void nv3_pmc_init(void); -void nv3_pmc_clear_interrupts(void); -uint32_t nv3_pmc_handle_interrupts(bool send_now); - -// NV3 PGRAPH -void nv3_pgraph_init(void); -uint32_t nv3_pgraph_read(uint32_t address); -void nv3_pgraph_write(uint32_t address, uint32_t value); -void nv3_pgraph_vblank_start(svga_t* svga); -void nv3_pgraph_interrupt_valid(uint32_t num); -void nv3_pgraph_interrupt_invalid(uint32_t num); -void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context); - -// PGRAPH class methods -// this should be in "vid_nv3_classes.h", but before that can happen, some things need to be rejigged -void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); -void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj); - -// Notification Engine -void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context,nv3_grobj_t grobj); - -// NV3 PFIFO -void nv3_pfifo_init(void); -uint32_t nv3_pfifo_read(uint32_t address); -void nv3_pfifo_write(uint32_t address, uint32_t value); -void nv3_pfifo_interrupt(uint32_t id, bool fire_now); - -// NV3 PFIFO - Caches -//cache0_push not a thing -void nv3_pfifo_cache0_pull(void); -void nv3_pfifo_cache1_push(uint32_t addr, uint32_t val); -void nv3_pfifo_cache1_pull(void); -uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val); -uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val); -uint32_t nv3_pfifo_cache1_num_free_spaces(void); - -// NV3 PFB -void nv3_pfb_init(void); - -// NV3 PEXTDEV/PSTRAPS -void nv3_pextdev_init(void); - -// NV3 PBUS -void nv3_pbus_init(void); - -// 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 (Final presentation) -void nv3_pramdac_init(void); -void nv3_pramdac_set_vram_clock(void); -void nv3_pramdac_set_pixel_clock(void); -void nv3_pramdac_pixel_clock_poll(double real_time); -void nv3_pramdac_memory_clock_poll(double real_time); - -// NV3 PTIMER -void nv3_ptimer_init(void); -void nv3_ptimer_tick(double real_time); - -// NV3 PVIDEO -void nv3_pvideo_init(void); - -// NV3 PME (Mediaport) -void nv3_pme_init(void); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv4.h b/src/include/86box/nv/vid_nv4.h deleted file mode 100644 index 0f2aad285..000000000 --- a/src/include/86box/nv/vid_nv4.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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. - * - * Riva TNT hardware defines - * - * Authors: Connor Hyde - * - * Copyright 2024-2025 Connor Hyde - */ - - -#pragma once - -#include -#include -#include <86Box/nv/vid_nv4_defines.h> - -// -// Structures -// - - -// PBUS -// RMA: Access the GPU from real-mode -typedef struct nv4_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[NV4_RMA_NUM_REGS]; // The rma registers (saved) -} nv4_pbus_rma_t; - -// Bus Configuration -typedef struct nv4_pbus_s -{ - uint32_t debug_0; - uint32_t interrupt_status; // Interrupt status - uint32_t interrupt_enable; // Interrupt enable - nv4_pbus_rma_t rma; -} nv4_pbus_t; - - -// PTIMER -typedef struct nv4_ptimer_s -{ - uint32_t interrupt_status; // PTIMER Interrupt status - uint32_t interrupt_enable; // PTIMER Interrupt enable - uint32_t clock_numerator; // PTIMER (tick?) numerator - uint32_t clock_denominator; // PTIMER (tick?) denominator - uint64_t time; // time - uint32_t alarm; // The value of time when there should be an alarm -} nv4_ptimer_t; - -// PRAMDAC -typedef struct nv4_pramdac_s -{ - uint32_t mclk; - uint32_t vclk; - uint32_t nvclk; - uint32_t clk_coeff_select; // Clock coefficient selection - uint32_t cursor_address; -} nv4_pramdac_t; - -// Device Core -typedef struct nv4_s -{ - nv_base_t nvbase; // Base Nvidia structure - uint32_t straps; // Straps. See defines - nv4_pbus_t pbus; - nv4_ptimer_t ptimer; - nv4_pramdac_t pramdac; -} nv4_t; - -// -// Globals -// - -extern const device_config_t nv4_config[]; - -extern nv4_t* nv4; // Allocated at device startup - -#ifdef NV_LOG - -// Debug register list -extern nv_register_t nv4_registers[]; - -#endif - -// -// Functions -// - -// Device Core -bool nv4_init(); - -void* nv4_init_stb4400(const device_t* info); - -void nv4_close(void* priv); -void nv4_speed_changed(void *priv); -void nv4_draw_cursor(svga_t* svga, int32_t drawline); -void nv4_recalc_timings(svga_t* svga); -void nv4_force_redraw(void* priv); - -// I/O -uint8_t nv4_mmio_read8(uint32_t addr, void* priv); -uint16_t nv4_mmio_read16(uint32_t addr, void* priv); -uint32_t nv4_mmio_read32(uint32_t addr, void* priv); -void nv4_mmio_write8(uint32_t addr, uint8_t val, void* priv); -void nv4_mmio_write16(uint32_t addr, uint16_t val, void* priv); -void nv4_mmio_write32(uint32_t addr, uint32_t val, void* priv); -uint8_t nv4_dfb_read8(uint32_t addr, void* priv); -uint16_t nv4_dfb_read16(uint32_t addr, void* priv); -uint32_t nv4_dfb_read32(uint32_t addr, void* priv); -void nv4_dfb_write8(uint32_t addr, uint8_t val, void* priv); -void nv4_dfb_write16(uint32_t addr, uint16_t val, void* priv); -void nv4_dfb_write32(uint32_t addr, uint32_t val, void* priv); -uint8_t nv4_ramin_read8(uint32_t addr, void* priv); -uint16_t nv4_ramin_read16(uint32_t addr, void* priv); -uint32_t nv4_ramin_read32(uint32_t addr, void* priv); -void nv4_ramin_write8(uint32_t addr, uint8_t val, void* priv); -void nv4_ramin_write16(uint32_t addr, uint16_t val, void* priv); -void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv); -uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv); -void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv); - -// SVGA -uint8_t nv4_svga_read(uint16_t addr, void* priv); -void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); - -// Memory -void nv4_update_mappings(); - -// PRAMDAC -uint32_t nv4_pramdac_read(uint32_t address); -void nv4_pramdac_write(uint32_t address, uint32_t data); - -// We don't implement NVCLK/VCLK because they are too fast -void nv4_pramdac_set_vclk(); - -void nv4_vclk_tick(); - -// PTIMER -uint32_t nv4_ptimer_read(uint32_t address); -void nv4_ptimer_write(uint32_t address, uint32_t data); \ No newline at end of file diff --git a/src/include/86box/nv/vid_nv4_defines.h b/src/include/86box/nv/vid_nv4_defines.h deleted file mode 100644 index 2a959670c..000000000 --- a/src/include/86box/nv/vid_nv4_defines.h +++ /dev/null @@ -1,4291 +0,0 @@ -#pragma once - -#include -#include - -// -// General -// -#define NV4_VRAM_SIZE_2MB 0x200000 // 2MB (never used; NV4 only) -#define NV4_VRAM_SIZE_4MB 0x400000 // 4MB (never used) -#define NV4_VRAM_SIZE_8MB 0x800000 // 8MB -#define NV4_VRAM_SIZE_16MB 0x1000000 // 16MB -#define NV5_VRAM_SIZE_32MB 0x2000000 // NV5 only - -#define NV4_MMIO_SIZE 0x1000000 // not sure. May be larger!!!! - -// -// VBIOS -// -#define NV4_VBIOS_STB_REVA "roms/video/nvidia/nv4/NV4_STB_velocity.rom" - -#define NV4_PRMIO_START 0x7000 -#define NV4_PRMIO_END 0x7FFF - -// NV4 Legacy I/O space -#define NV4_RMA_REGISTER_START 0x3D0 -#define NV4_RMA_REGISTER_END 0x3D3 -#define NV4_RMA_NUM_REGS 4 - -#define NV4_PRMIO_RMA_ID 0x7100 -#define NV4_PRMIO_RMA_ID_CODE 0 -#define NV4_PRMIO_RMA_ID_CODE_VALID 0x2B16D065 -#define NV4_PRMIO_RMA_PTR 0x7104 -#define NV4_PRMIO_RMA_PTR_ADDRESS 2 -#define NV4_PRMIO_RMA_DATA 0x7108 -#define NV4_PRMIO_RMA_DATA_PORT 0 -#define NV4_PRMIO_RMA_DATA32 0x710C -#define NV4_PRMIO_RMA_DATA32_BYTE2 16 -#define NV4_PRMIO_RMA_DATA32_BYTE1 8 -#define NV4_PRMIO_RMA_DATA32_BYTE0 0 -#define NV4_PRMIO_RMA_MODE_MAX 0x0F - -#define NV4_PRAMDAC_START 0x680300 -#define NV4_PRAMDAC_END 0x680FFF - -#define NV4_PRAMDAC_CU_START_POS 0x680300 -#define NV4_PRAMDAC_CU_START_POS_X 0 -#define NV4_PRAMDAC_CU_START_POS_Y 16 -#define NV4_PRAMDAC_NVPLL_COEFF 0x680500 -#define NV4_PRAMDAC_NVPLL_COEFF_MDIV 0 -#define NV4_PRAMDAC_NVPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_NVPLL_COEFF_PDIV 16 // 18:16 -#define NV4_PRAMDAC_MPLL_COEFF 0x680504 -#define NV4_PRAMDAC_MPLL_COEFF_MDIV 0 -#define NV4_PRAMDAC_MPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_MPLL_COEFF_PDIV 16 // 18:16 -#define NV4_PRAMDAC_VPLL_COEFF 0x680508 -#define NV4_PRAMDAC_VPLL_COEFF_MDIV 0 -#define NV4_PRAMDAC_VPLL_COEFF_NDIV 8 -#define NV4_PRAMDAC_VPLL_COEFF_PDIV 16 // 18:16 -#define NV4_PRAMDAC_PLL_COEFF_SELECT 0x68050C -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE 0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE_XTAL 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VPLL_SOURCE_VIP 0x1 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE 8 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_DEFAULT 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL 0x1 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL 0x2 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL 0x4 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_ALL 0x7 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV 16 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_NONE 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_VSCLK 0x1 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_PCLK 0x2 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VS_PCLK_TV_BOTH 0x3 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE 20 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE_EXT 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_SOURCE_VIP 0x1 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO 24 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO_DB1 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_TVCLK_RATIO_DB2 0x1 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO 28 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB1 0x0 -#define NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 0x1 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL 0x680510 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_VALUE 0 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_VAL 0x44E -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN 12 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_ON 0x0 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_MPLL 0x1 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_VPLL 0x2 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_NVPLL 0x4 -#define NV4_PRAMDAC_PLL_SETUP_CONTROL_PWRDWN_OFF 0x7 -#define NV4_PRAMDAC_PLL_TEST_COUNTER 0x680514 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_NOOFIPCLKS 0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_VALUE 0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE 16 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE_DEASSERTED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_ENABLE_ASSERTED 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET 20 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET_DEASSERTED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_RESET_ASSERTED 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE 24 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_MCLK 0x2 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_VCLK 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_SOURCE_NVCLK 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIV_RST 28 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIVRST_DEASSERTED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_PDIVRST_ASSERTED 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK 29 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_NOTLOCKED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCKED 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK 30 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_NOTLOCKED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCKED 0x1 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK 31 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_NOTLOCKED 0x0 -#define NV4_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCKED 0x1 -#define NV4_PRAMDAC_PALETTE_TEST 0x680518 -#define NV4_PRAMDAC_PALETTE_TEST_BLUE_DATA 0 -#define NV4_PRAMDAC_PALETTE_TEST_GREEN_DATA 8 -#define NV4_PRAMDAC_PALETTE_TEST_RED_DATA 16 -#define NV4_PRAMDAC_PALETTE_TEST_MODE 24 -#define NV4_PRAMDAC_PALETTE_TEST_MODE_8BIT 0x0 -#define NV4_PRAMDAC_PALETTE_TEST_MODE_24BIT 0x1 -#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC 28 -#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC_READWRITE 0x0 -#define NV4_PRAMDAC_PALETTE_TEST_ADDRINC_WRITEONLY 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL 0x680600 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT 0 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT_24 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX32_BIT_31 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX 4 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_OFF 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_POS 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_NEG 0x2 -#define NV4_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON 0x3 -#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE 8 -#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE_NOTSEL 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE 12 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_NOTSEL 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_15 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_16 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_24 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_ALT_MODE_30 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL 16 -#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL_OFF 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_BLK_PEDSTL_ON 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION 17 -#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION_37OHM 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_BPC 20 -#define NV4_PRAMDAC_GENERAL_CONTROL_BPC_6BITS 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_BPC_8BITS 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP 24 -#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP_DIS 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_DAC_SLEEP_EN 0x1 -#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK 28 -#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK_EN 0x0 -#define NV4_PRAMDAC_GENERAL_CONTROL_PALETTE_CLK_DIS 0x1 -#define NV4_PRAMDAC_PALETTE_RECOVERY 0x680604 -#define NV4_PRAMDAC_PALETTE_RECOVERY_ACTIVE_ADDRESS 0 -#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER 8 -#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_RED 0x1 -#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_GREEN 0x2 -#define NV4_PRAMDAC_PALETTE_RECOVERY_RGB_POINTER_BLUE 0x4 -#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE 12 -#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE_WRITE 0x0 -#define NV4_PRAMDAC_PALETTE_RECOVERY_DAC_STATE_READ 0x3 -#define NV4_PRAMDAC_PALETTE_RECOVERY_RED_DATA 16 -#define NV4_PRAMDAC_PALETTE_RECOVERY_GREEN_DATA 24 -#define NV4_PRAMDAC_TEST_CONTROL 0x680608 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET 0 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET_DEASSERTED 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_RESET_ASSERTED 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE 4 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE_DEASSERTED 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_ENABLE_ASSERTED 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL 8 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_BLUE 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_GREEN 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_CRC_CHANNEL_RED 0x2 -#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN 12 -#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN_DEASSERTED 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC 16 -#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_ON 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_DACTM 20 -#define NV4_PRAMDAC_TEST_CONTROL_DACTM_NORMAL 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_DACTM_TEST 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH1 24 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH1_CLEAR 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH1_SET 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH31 25 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH31_CLEAR 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_TPATH31_SET 0x1 -#define NV4_PRAMDAC_TEST_CONTROL_SENSEB 28 -#define NV4_PRAMDAC_TEST_CONTROL_SENSEB_SOMELO 0x0 -#define NV4_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI 0x1 -#define NV4_PRAMDAC_CHECKSUM 0x68060C -#define NV4_PRAMDAC_CHECKSUM_VALUE 0 -#define NV4_PRAMDAC_TESTPOINT_DATA 0x680610 -#define NV4_PRAMDAC_TESTPOINT_DATA_RED 0 -#define NV4_PRAMDAC_TESTPOINT_DATA_GREEN 10 -#define NV4_PRAMDAC_TESTPOINT_DATA_BLUE 20 -#define NV4_PRAMDAC_TESTPOINT_DATA_BLACK 30 -#define NV4_PRAMDAC_TESTPOINT_DATA_NOTBLANK 31 -#define NV4_PRAMDAC_TV_SETUP 0x680700 -#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE 0 -#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE_SLAVE 0x0 -#define NV4_PRAMDAC_TV_SETUP_DEV_TYPE_MASTER 0x1 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT 4 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_555 0x0 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_565 0x1 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_888 0x2 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_101010 0x3 -#define NV4_PRAMDAC_TV_SETUP_VS_PIXFMT_YUV 0x4 -#define NV4_PRAMDAC_TV_SETUP_DATA_SRC 8 -#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_COMP 0x0 -#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_SCALER 0x1 -#define NV4_PRAMDAC_TV_SETUP_DATA_SRC_VIP 0x2 -#define NV4_PRAMDAC_TV_SETUP_COMP_SRC 12 -#define NV4_PRAMDAC_TV_SETUP_COMP_SRC_SCALER 0x0 -#define NV4_PRAMDAC_TV_SETUP_COMP_SRC_NO_SCALER 0x1 -#define NV4_PRAMDAC_TV_SETUP_SYNC_POL 16 -#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_NONE 0x0 -#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_HSYNC 0x1 -#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_VSYNC 0x2 -#define NV4_PRAMDAC_TV_SETUP_SYNC_POL_NEG_BOTH 0x3 -#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC 20 -#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC_LEAD 0x0 -#define NV4_PRAMDAC_TV_SETUP_VIP_VSYNC_TRAIL 0x1 -#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS 24 -#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS_7_0 0x0 -#define NV4_PRAMDAC_TV_SETUP_VIP_DATAPOS_11_4 0x1 -#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD 28 -#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD_0 0x0 -#define NV4_PRAMDAC_TV_SETUP_VIP_FIELD_1 0x1 -#define NV4_PRAMDAC_TV_VBLANK_START 0x680704 -#define NV4_PRAMDAC_TV_VBLANK_START_VAL 0 -#define NV4_PRAMDAC_TV_VBLANK_END 0x680708 -#define NV4_PRAMDAC_TV_VBLANK_END_VAL 0 -#define NV4_PRAMDAC_TV_HBLANK_START 0x68070C -#define NV4_PRAMDAC_TV_HBLANK_START_VAL 0 -#define NV4_PRAMDAC_TV_HBLANK_END 0x680710 -#define NV4_PRAMDAC_TV_HBLANK_END_VAL 0 -#define NV4_PRAMDAC_BLANK_COLOR 0x680714 -#define NV4_PRAMDAC_BLANK_COLOR_VAL 0 -#define NV4_PRAMDAC_TV_CHECKSUM 0x680718 -#define NV4_PRAMDAC_TV_CHECKSUM_VAL 0 -#define NV4_PRAMDAC_TV_VSYNC 28 -#define NV4_PRAMDAC_TV_VSYNC_ACTIVE 0x0 -#define NV4_PRAMDAC_TV_VSYNC_INACTIVE 0x1 -#define NV4_PRAMDAC_TV_TEST_CONTROL 0x68071c -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET 0 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET_DEASSERTED 0x0 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_RESET_ASSERTED 0x1 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE 4 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE_DEASSERTED 0x0 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_ENABLE_ASSERTED 0x1 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL 8 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_7_0 0x0 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_15_8 0x1 -#define NV4_PRAMDAC_TV_TEST_CONTROL_CRC_CHANNEL_23_16 0x2 - -#define NV4_USER_DAC_START 0x681200 -#define NV4_USER_DAC_END 0x681FFF -#define NV4_USER_DAC_PALETTE_START 0x6813C6 -#define NV4_USER_DAC_PALETTE_END 0x6813C9 - -#define NV4_USER_DAC_PIXEL_MASK 0x6813C6 -#define NV4_USER_DAC_PIXEL_MASK_VALUE 0 -#define NV4_USER_DAC_PIXEL_MASK_MASK 0xFF -#define NV4_USER_DAC_READ_MODE_ADDRESS 0x6813C7 -#define NV4_USER_DAC_READ_MODE_ADDRESS_VALUE 0 -#define NV4_USER_DAC_READ_MODE_ADDRESS_WO_VALUE 0 -#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE 0 -#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE_WRITE 0x0 -#define NV4_USER_DAC_READ_MODE_ADDRESS_RW_STATE_READ 0x3 -#define NV4_USER_DAC_WRITE_MODE_ADDRESS 0x6813C8 -#define NV4_USER_DAC_WRITE_MODE_ADDRESS_VALUE 0 -#define NV4_USER_DAC_PALETTE_DATA 0x6813C9 -#define NV4_USER_DAC_PALETTE_DATA_VALUE 0 - -#define NV4_PRMDIO_START 0x681000 -#define NV4_PRMDIO_END 0x681FFF - -#define NV4_IO_MPU_401_DATA 0x330 -#define NV4_IO_MPU_401_DATA_ALIAS_1 0x300 -#define NV4_IO_MPU_401_DATA_ALIAS_2 0x230 -#define NV4_IO_MPU_401_DATA_VALUE 0 -#define NV4_IO_MPU_401_DATA_ACK 0xFE -#define NV4_IO_MPU_401_STATUS 0x331 -#define NV4_IO_MPU_401_STATUS_ALIAS_1 0x301 -#define NV4_IO_MPU_401_STATUS_ALIAS_2 0x231 -#define NV4_IO_MPU_401_STATUS_DATA 0 -#define NV4_IO_MPU_401_STATUS_WRITE 6 -#define NV4_IO_MPU_401_STATUS_WRITE_EMPTY 0x0 -#define NV4_IO_MPU_401_STATUS_WRITE_FULL 0x1 -#define NV4_IO_MPU_401_STATUS_READ 7 -#define NV4_IO_MPU_401_STATUS_READ_FULL 0x0 -#define NV4_IO_MPU_401_STATUS_READ_EMPTY 0x1 -#define NV4_IO_MPU_401_COM 0x331 -#define NV4_IO_MPU_401_COM_ALIAS_1 0x301 -#define NV4_IO_MPU_401_COM_ALIAS_2 0x231 -#define NV4_IO_MPU_401_COM_UART_MODE 0 -#define NV4_IO_MPU_401_COM_UART_MODE_COMPLEX 0xff -#define NV4_IO_MPU_401_COM_UART_MODE_SIMPLE 0x3f - -#define NV4_PMC_START 0x0 -#define NV4_PMC_END 0xFFF - -#define NV4_PMC_BOOT_0 0x0 -#define NV4_PMC_BOOT_0_MINOR_REVISION 0 -#define NV4_PMC_BOOT_0_MINOR_REVISION_0 0x0 -#define NV4_PMC_BOOT_0_MAJOR_REVISION 4 -#define NV4_PMC_BOOT_0_MAJOR_REVISION_A 0x0 -#define NV4_PMC_BOOT_0_MAJOR_REVISION_B 0x1 -#define NV4_PMC_BOOT_0_IMPLEMENTATION 8 -#define NV4_PMC_BOOT_0_IMPLEMENTATION_NV4_0 0x0 -#define NV4_PMC_BOOT_0_ARCHITECTURE 12 -#define NV4_PMC_BOOT_0_ARCHITECTURE_NV0 0x0 -#define NV4_PMC_BOOT_0_ARCHITECTURE_NV1 0x1 -#define NV4_PMC_BOOT_0_ARCHITECTURE_NV2 0x2 -#define NV4_PMC_BOOT_0_ARCHITECTURE_NV3 0x3 -#define NV4_PMC_BOOT_0_ARCHITECTURE_NV4 0x4 -#define NV4_PMC_BOOT_0_FIB_REVISION 16 -#define NV4_PMC_BOOT_0_FIB_REVISION_0 0x0 -#define NV4_PMC_BOOT_0_MASK_REVISION 20 -#define NV4_PMC_BOOT_0_MASK_REVISION_A 0x0 -#define NV4_PMC_BOOT_0_MASK_REVISION_B 0x1 -#define NV4_PMC_BOOT_0_MASK_REVISION_C 0x2 -#define NV4_PMC_BOOT_0_MANUFACTURER 24 -#define NV4_PMC_BOOT_0_MANUFACTURER_NVIDIA 0x0 -#define NV4_PMC_BOOT_0_FOUNDRY 28 -#define NV4_PMC_BOOT_0_FOUNDRY_SGS 0x0 -#define NV4_PMC_BOOT_0_FOUNDRY_HELIOS 0x1 -#define NV4_PMC_BOOT_0_FOUNDRY_TSMC 0x2 -#define NV4_PMC_INTR_0 0x100 -#define NV4_PMC_INTR_0_PMEDIA 4 -#define NV4_PMC_INTR_0_PMEDIA_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PMEDIA_PENDING 0x1 -#define NV4_PMC_INTR_0_PFIFO 8 -#define NV4_PMC_INTR_0_PFIFO_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PFIFO_PENDING 0x1 -#define NV4_PMC_INTR_0_PGRAPH 12 -#define NV4_PMC_INTR_0_PGRAPH_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PGRAPH_PENDING 0x1 -#define NV4_PMC_INTR_0_PVIDEO 16 -#define NV4_PMC_INTR_0_PVIDEO_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PVIDEO_PENDING 0x1 -#define NV4_PMC_INTR_0_PTIMER 20 -#define NV4_PMC_INTR_0_PTIMER_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PTIMER_PENDING 0x1 -#define NV4_PMC_INTR_0_PCRTC 24 -#define NV4_PMC_INTR_0_PCRTC_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PCRTC_PENDING 0x1 -#define NV4_PMC_INTR_0_PBUS 28 -#define NV4_PMC_INTR_0_PBUS_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_PBUS_PENDING 0x1 -#define NV4_PMC_INTR_0_SOFTWARE 31 -#define NV4_PMC_INTR_0_SOFTWARE_NOT_PENDING 0x0 -#define NV4_PMC_INTR_0_SOFTWARE_PENDING 0x1 -#define NV4_PMC_INTR_EN_0 0x140 -#define NV4_PMC_INTR_EN_0_INTA 0 -#define NV4_PMC_INTR_EN_0_INTA_DISABLED 0x0 -#define NV4_PMC_INTR_EN_0_INTA_HARDWARE 0x1 -#define NV4_PMC_INTR_EN_0_INTA_SOFTWARE 0x2 -#define NV4_PMC_INTR_READ_0 0x160 -#define NV4_PMC_INTR_READ_0_INTA 0 -#define NV4_PMC_INTR_READ_0_INTA_LOW 0x0 -#define NV4_PMC_INTR_READ_0_INTA_HIGH 0x1 -#define NV4_PMC_ENABLE 0x200 -#define NV4_PMC_ENABLE_PMEDIA 4 -#define NV4_PMC_ENABLE_PMEDIA_ENABLED 0x1 -#define NV4_PMC_ENABLE_PFIFO 8 -#define NV4_PMC_ENABLE_PFIFO_ENABLED 0x1 -#define NV4_PMC_ENABLE_PGRAPH 12 -#define NV4_PMC_ENABLE_PGRAPH_ENABLED 0x1 -#define NV4_PMC_ENABLE_PPMI 16 -#define NV4_PMC_ENABLE_PPMI_ENABLED 0x1 -#define NV4_PMC_ENABLE_PFB 20 -#define NV4_PMC_ENABLE_PFB_ENABLED 0x1 -#define NV4_PMC_ENABLE_PCRTC 24 -#define NV4_PMC_ENABLE_PCRTC_ENABLED 0x1 -#define NV4_PMC_ENABLE_PVIDEO 28 -#define NV4_PMC_ENABLE_PVIDEO_ENABLED 0x1 - -#define NV4_PBUS_START 0x1000 -#define NV4_PBUS_END 0x1FFF - -#define NV4_PBUS_DEBUG_0 0x1080 -#define NV4_PBUS_DEBUG_0_FBIO_SCLK_DELAY 0 -#define NV4_PBUS_DEBUG_0_FBIO_SCLK_DELAY_8 0x8 -#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC 4 -#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC_NORMAL 0x0 -#define NV4_PBUS_DEBUG_0_FBIO_SCLK_PC_OVERRIDE 0x1 -#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_DELAY 8 -#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_DELAY_8 0x8 -#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC 12 -#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC_NORMAL 0x0 -#define NV4_PBUS_DEBUG_0_FBIO_FBCLK_PC_OVERRIDE 0x1 -#define NV4_PBUS_DEBUG_0_FBIO_ACLK_DELAY 16 -#define NV4_PBUS_DEBUG_0_FBIO_ACLK_DELAY_8 0x8 -#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC 20 -#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC_NORMAL 0x0 -#define NV4_PBUS_DEBUG_0_FBIO_ACLK_PC_OVERRIDE 0x1 -#define NV4_PBUS_DEBUG_0_FBIO_RCLK_DELAY 24 -#define NV4_PBUS_DEBUG_0_FBIO_RCLK_DELAY_8 0x8 -#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC 28 -#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC_NORMAL 0x0 -#define NV4_PBUS_DEBUG_0_FBIO_RCLK_PC_OVERRIDE 0x1 -#define NV4_PBUS_DEBUG_1 0x1084 -#define NV4_PBUS_DEBUG_1_PCIM_THROTTLE 0 -#define NV4_PBUS_DEBUG_1_PCIM_THROTTLE_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIM_CMD 1 -#define NV4_PBUS_DEBUG_1_PCIM_CMD_SIZE_BASED 0x0 -#define NV4_PBUS_DEBUG_1_PCIM_CMD_MRL_ONLY 0x1 -#define NV4_PBUS_DEBUG_1_HASH_DECODE 2 -#define NV4_PBUS_DEBUG_1_HASH_DECODE_1FF 0x0 -#define NV4_PBUS_DEBUG_1_HASH_DECODE_2FF 0x1 -#define NV4_PBUS_DEBUG_1_AGPM_CMD 3 -#define NV4_PBUS_DEBUG_1_AGPM_CMD_HP_ON_1ST 0x0 -#define NV4_PBUS_DEBUG_1_AGPM_CMD_LP_ONLY 0x1 -#define NV4_PBUS_DEBUG_1_AGPM_CMD_HP_ONLY 0x2 -#define NV4_PBUS_DEBUG_1_PCIS_WRITE 5 -#define NV4_PBUS_DEBUG_1_PCIS_WRITE_0_CYCLE 0x0 -#define NV4_PBUS_DEBUG_1_PCIS_WRITE_1_CYCLE 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_2_1 6 -#define NV4_PBUS_DEBUG_1_PCIS_2_1_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_RETRY 7 -#define NV4_PBUS_DEBUG_1_PCIS_RETRY_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_RD_BURST 8 -#define NV4_PBUS_DEBUG_1_PCIS_RD_BURST_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_WR_BURST 9 -#define NV4_PBUS_DEBUG_1_PCIS_WR_BURST_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_EARLY_RTY 10 -#define NV4_PBUS_DEBUG_1_PCIS_EARLY_RTY_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_PCIS_CPUQ 12 -#define NV4_PBUS_DEBUG_1_PCIS_CPUQ_ENABLED 0x1 -#define NV4_PBUS_DEBUG_1_DPSH_DECODE 13 -#define NV4_PBUS_DEBUG_1_DPSH_DECODE_NV4 0x0 -#define NV4_PBUS_DEBUG_1_DPSH_DECODE_NV3 0x1 -#define NV4_PBUS_DEBUG_1_SPARE1 14 -#define NV4_PBUS_DEBUG_1_SPARE2 15 -#define NV4_PBUS_DEBUG_1_SPARE3 16 -#define NV4_PBUS_DEBUG_1_SPARE4 17 -#define NV4_PBUS_DEBUG_1_SPARE5 18 -#define NV4_PBUS_DEBUG_1_SPARE6 19 -#define NV4_PBUS_DEBUG_1_SPARE7 20 -#define NV4_PBUS_DEBUG_1_SPARE8 21 -#define NV4_PBUS_DEBUG_1_SPARE9 22 -#define NV4_PBUS_DEBUG_1_SPARE10 23 -#define NV4_PBUS_DEBUG_2 0x1088 -#define NV4_PBUS_DEBUG_2_AGP_DIFFERENTIAL 0 -#define NV4_PBUS_DEBUG_2_AGP_DIFFERENTIAL_ENABLED 0x1 -#define NV4_PBUS_DEBUG_2_AGP_SB_STB_DELAY 9:4 -#define NV4_PBUS_DEBUG_2_AGP_SB_STB_DELAY_34 0x22 -#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC 12 -#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC_NORMAL 0x0 -#define NV4_PBUS_DEBUG_2_AGP_SB_STB_PC_OVERRIDE 0x1 -#define NV4_PBUS_DEBUG_3 0x108C -#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE 0 -#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_UNLIMITED 0x0 -#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_32_BYTES 0x1 -#define NV4_PBUS_DEBUG_3_AGP_MAX_SIZE_64_BYTES 0x2 -#define NV4_PBUS_DEBUG_CTL 0x1090 -#define NV4_PBUS_DEBUG_CTL_MODE 0 -#define NV4_PBUS_DEBUG_CTL_MODE_ENABLED 0x1 -#define NV4_PBUS_DEBUG_CTL_READ_SELECT 4 -#define NV4_PBUS_DEBUG_CTL_READ_SELECT_0 0x0 -#define NV4_PBUS_DEBUG_CTL_READ_SELECT_1 0x1 -#define NV4_PBUS_DEBUG_READ 0x1094 -#define NV4_PBUS_DEBUG_READ_DATA 0 -#define NV4_PBUS_DEBUG_HOST 0x109C -#define NV4_PBUS_DEBUG_HOST_SEL 0 -#define NV4_PBUS_DEBUG_SEL_0 0x10A0 -#define NV4_PBUS_DEBUG_SEL_0_X 0 -#define NV4_PBUS_DEBUG_SEL_1 0x10A4 -#define NV4_PBUS_DEBUG_SEL_1_X 0 -#define NV4_PBUS_DEBUG_SEL_2 0x10A8 -#define NV4_PBUS_DEBUG_SEL_2_X 0 -#define NV4_PBUS_DEBUG_SEL_3 0x10AC -#define NV4_PBUS_DEBUG_SEL_3_X 0 -#define NV4_PBUS_INTR_0 0x1100 -#define NV4_PBUS_INTR_0_PCI_BUS_ERROR 0 -#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_NOT_PENDING 0x0 -#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_PENDING 0x1 -#define NV4_PBUS_INTR_0_PCI_BUS_ERROR_RESET 0x1 -#define NV4_PBUS_INTR_EN_0 0x1140 -#define NV4_PBUS_INTR_EN_0_PCI_BUS_ERROR 0 -#define NV4_PBUS_INTR_EN_0_PCI_BUS_ERROR_ENABLED 0x1 -#define NV4_PBUS_ROM_CONFIG 0x1200 -#define NV4_PBUS_ROM_CONFIG_TW1 0 -#define NV4_PBUS_ROM_CONFIG_TW1_DEFAULT 0xF -#define NV4_PBUS_ROM_CONFIG_TW0 4 -#define NV4_PBUS_ROM_CONFIG_TW0_DEFAULT 0x3 - -// 86Box uses 8-bit PCI registers so this section was rewritten -#define NV4_PBUS_PCI_VENDOR_ID 0x1800 -#define NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA 0x10DE -#define NV4_PBUS_PCI_DEVICE_ID 0x1802 -#define NV4_PBUS_PCI_DEVICE_ID_NV4 0x0020 // Chip (19:17)= NV4, Func = VGA -#define NV4_PBUS_PCI_COMMAND 0x1804 -#define NV4_PBUS_PCI_COMMAND_H 0x1805 -#define NV4_PBUS_PCI_COMMAND_IO_SPACE 0 -#define NV4_PBUS_PCI_COMMAND_IO_SPACE_ENABLED 0x1 -#define NV4_PBUS_PCI_COMMAND_MEMORY_SPACE 1 -#define NV4_PBUS_PCI_COMMAND_MEMORY_SPACE_ENABLED 0x1 -#define NV4_PBUS_PCI_COMMAND_BUS_MASTER 2 -#define NV4_PBUS_PCI_COMMAND_BUS_MASTER_ENABLED 0x1 -#define NV4_PBUS_PCI_COMMAND_WRITE_AND_INVAL 4 -#define NV4_PBUS_PCI_COMMAND_WRITE_AND_INVAL_ENABLED 0x1 -#define NV4_PBUS_PCI_COMMAND_PALETTE_SNOOP 5 -#define NV4_PBUS_PCI_COMMAND_PALETTE_SNOOP_ENABLED 0x1 -#define NV4_PBUS_PCI_STATUS 0x1806 -#define NV4_PBUS_PCI_STATUS_CAPLIST 4 -#define NV4_PBUS_PCI_STATUS_CAPLIST_NOT_PRESENT 0x0 -#define NV4_PBUS_PCI_STATUS_CAPLIST_PRESENT 0x1 -#define NV4_PBUS_PCI_STATUS_66MHZ 5 -#define NV4_PBUS_PCI_STATUS_66MHZ_INCAPABLE 0x0 -#define NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE 0x1 -#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK 7 -#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK_INCAPABLE 0x0 -#define NV4_PBUS_PCI_STATUS_FAST_BACK2BACK_CAPABLE 0x1 -#define NV4_PBUS_PCI_STATUS_2 0x1807 -#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING 1 -#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST 0x0 -#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_MEDIUM 0x1 -#define NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_SLOW 0x2 -#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET 3 -#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_NO_ABORT 0x0 -#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_ABORT 0x1 -#define NV4_PBUS_PCI_STATUS_2_SIGNALED_TARGET_CLEAR 0x1 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET 4 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_NO_ABORT 0x0 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_ABORT 0x1 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_TARGET_CLEAR 0x1 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER 5 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_NO_ABORT 0x0 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_ABORT 0x1 -#define NV4_PBUS_PCI_STATUS_2_RECEIVED_MASTER_CLEAR 0x1 -#define NV4_PBUS_PCI_REVISION_ID 0x1808 -#define NV4_PBUS_PCI_REVISION_ID_A01 0x0 -#define NV4_PBUS_PCI_REVISION_ID_B01 0x1 -#define NV4_PBUS_PCI_CLASS_CODE 0x180B -#define NV4_PBUS_PCI_CLASS_CODE_VGA 0x30000 -#define NV4_PBUS_PCI_LATENCY_TIMER 0x180D -// Shift left by 3 to get the real value in clocks. 0x0 = 0, 0x1 = 8, 0x1E = 240, 0x1F = 248 are values used. -#define NV4_PBUS_PCI_LATENCY_TIMER_VALUE 3 -// 0x0 = single function (only value that matters) -#define NV4_PBUS_PCI_HEADER_TYPE 0x180E -#define NV4_PBUS_PCI_BAR_SPACE_TYPE 0 -#define NV4_PBUS_PCI_BAR_SPACE_TYPE_MEMORY 0x0 -#define NV4_PBUS_PCI_BAR_SPACE_TYPE_IO 0x1 -#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE 1 -#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_32_BIT 0x0 -#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_20_BIT 0x1 -#define NV4_PBUS_PCI_BAR_ADDRESS_TYPE_64_BIT 0x2 -#define NV4_PBUS_PCI_BAR_PREFETCHABLE 3 -#define NV4_PBUS_PCI_BAR_PREFETCHABLE_NOT 0x0 -#define NV4_PBUS_PCI_BAR_PREFETCHABLE_MERGABLE 0x1 -// Bits 23:4 are resedrved -#define NV4_PBUS_PCI_BAR0_INFO 0x1810 -#define NV4_PBUS_PCI_BAR0_UNUSED1 0x1811 -#define NV4_PBUS_PCI_BAR0_UNUSED2 0x1812 -#define NV4_PBUS_PCI_BAR0_BASE_31_TO_24 0x1813 // Must align to 16MByte -#define NV4_PBUS_PCI_BAR1_INFO 0x1814 -#define NV4_PBUS_PCI_BAR1_UNUSED1 0x1815 -#define NV4_PBUS_PCI_BAR1_UNUSED2 0x1816 -#define NV4_PBUS_PCI_BAR1_BASE_31_TO_24 0x1817 // Must align to 16MByte -#define NV4_PBUS_PCI_BAR_RESERVED_START 0x1818 -#define NV4_PBUS_PCI_BAR_RESERVED_END 0x182B - -//BAR2-5 reserved -#define NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID 0x182C -#define NV4_PBUS_PCI_SUBSYSTEM_ID 0x182E -#define NV4_PBUS_PCI_ROM 0x1830 -#define NV4_PBUS_PCI_ROM_DECODE 0 -#define NV4_PBUS_PCI_ROM_DECODE_ENABLED 0x1 -#define NV4_PBUS_PCI_ROM_BASE 0x1832 -#define NV4_PBUS_PCI_NEXT_PTR 0x1834 -#define NV4_PBUS_PCI_CAP_PTR_AGP 0x44 -#define NV4_PBUS_PCI_CAP_PTR_POWER_MGMT 0x60 -// 0xFF = unknown, otherwise 0x0-0xF = IRQ0-15 -#define NV4_PBUS_PCI_INTR_LINE 0x183C -#define NV4_PBUS_PCI_INTR_LINE_IRQ_NUM 0 -#define NV4_PBUS_PCI_INTR_LINE_IRQ_NUM_UNKNOWN 0xFF -#define NV4_PBUS_PCI_INTR_PIN 0x183D -#define NV4_PBUS_PCI_INTR_PIN_INTA 0x1 -#define NV4_PBUS_PCI_MIN_GNT 0x183E -#define NV4_PBUS_PCI_MIN_GNT_NO_REQUIREMENTS 0x0 -#define NV4_PBUS_PCI_MIN_GNT_750NS 0x3 -#define NV4_PBUS_PCI_MIN_GNT_1250NS 0x5 -#define NV4_PBUS_PCI_MAX_LAT 0x183F -#define NV4_PBUS_PCI_MAX_LAT_NO_REQUIREMENTS 0x0 -#define NV4_PBUS_PCI_MAX_LAT_250NS 0x1 -#define NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE 0x1840 -#define NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE 0x1842 -#define NV4_PBUS_AGP 0x44 -#define NV4_PBUS_AGP_CAPABILITIES 0x1844 -#define NV4_PBUS_AGP_CAPABILITY_AGP 0x2 -// Should be null! - -#define NV4_PBUS_AGP_NEXT_PTR 0x1845 -#define NV4_PBUS_AGP_REV 0x1846 -#define NV4_PBUS_AGP_REV_MINOR 0 -#define NV4_PBUS_AGP_REV_MINOR_0 0x0 -#define NV4_PBUS_AGP_REV_MAJOR 4 -#define NV4_PBUS_AGP_REV_MAJOR_1 0x1 -#define NV4_PBUS_AGP_STATUS_RATE 0x1848 -#define NV4_PBUS_AGP_STATUS_RATE_1X 0x1 -#define NV4_PBUS_AGP_STATUS_RATE_2X 0x2 -#define NV4_PBUS_AGP_STATUS_RATE_1X_AND_2X 0x3 -#define NV4_PBUS_AGP_STATUS_RQ 0x184B -#define NV4_PBUS_AGP_STATUS_RQ_16 0xF -#define NV4_PBUS_AGP_STATUS_SBA 0x1849 -#define NV4_PBUS_AGP_STATUS_SBA_STATUS 1 -#define NV4_PBUS_AGP_STATUS_SBA_STATUS_NONE 0x0 -#define NV4_PBUS_AGP_STATUS_SBA_STATUS_CAPABLE 0x1 -#define NV4_PBUS_AGP_COMMAND 0x184C -#define NV4_PBUS_AGP_COMMAND_DATA_RATE 0 -#define NV4_PBUS_AGP_COMMAND_DATA_RATE_OFF 0x0 -#define NV4_PBUS_AGP_COMMAND_DATA_RATE_1X 0x1 -#define NV4_PBUS_AGP_COMMAND_DATA_RATE_2X 0x2 -#define NV4_PBUS_AGP_COMMAND_2 0x184D -#define NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED 0 // 1 = enabled, 0 = disabled -#define NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED 1 // 1 = enabled, 0 = disabled -#define NV4_PBUS_AGP_COMMAND_RQ_DEPTH 0x184F //DEFAUlt 0 -#define NV4_PBUS_PCI_ROM_SHADOW 0x1850 -#define NV4_PBUS_PCI_ROM_SHADOW_IS_ENABLED 0 // 1 = enabled, 0 = disabled -#define NV4_PBUS_PCI_VGA 0x1854 -#define NV4_PBUS_PCI_VGA_IS_ENABLED 0 // 1 = enabled, 0 = disabled -#define NV4_PBUS_PCI_SCRATCH 0x1858 -#define NV4_PBUS_PCI_SCRATCH_DEFAULT 0x23D6CE -#define NV4_PBUS_PCI_DT 0x185C -#define NV4_PBUS_PCI_DT_TIMEOUT 0 -#define NV4_PBUS_PCI_DT_TIMEOUT_16 0xF -//TODO: Implement -#define NV4_PBUS_PCIPOWER 0x1860 -#define NV4_PBUS_PCIPOWER_CAP_ID 0 -#define NV4_PBUS_PCIPOWER_CAP_ID_POWER_MGMT 0x1 -#define NV4_PBUS_PCIPOWER_NEXT_PTR 0x1861 // should be 0x44=AGP -#define NV4_PBUS_PCIPOWER_2 0x1862 -#define NV4_PBUS_PCIPOWER_2_VERSION 0 -#define NV4_PBUS_PCIPOWER_2_VERSION_1 0x1 -#define NV4_PBUS_PCIPOWER_2_CLOCK 3 -#define NV4_PBUS_PCIPOWER_2_CLOCK_NOT_REQUIRED 0x0 -#define NV4_PBUS_PCIPOWER_2_DSI 5 -#define NV4_PBUS_PCIPOWER_2_DSI_NOT_REQUIRED 0x0 -#define NV4_PBUS_PCIPOWER_SUPPORTED_STATES 0x1863 -#define NV4_PBUS_PCIPOWER_D1 1 -#define NV4_PBUS_PCIPOWER_D1_SUPPORTED 0x1 // 1 = supported -#define NV4_PBUS_PCIPOWER_D2 2 -#define NV4_PBUS_PCIPOWER_D2_SUPPORTED 0x1 // 0 = not supported -#define NV4_PBUS_PCIPOWER_PME_D0 3 -#define NV4_PBUS_PCIPOWER_PME_D0_SUPPORTED 0x1 -#define NV4_PBUS_PCIPOWER_PME_D1 4 -#define NV4_PBUS_PCIPOWER_PME_D1_SUPPORTED 0x1 -#define NV4_PBUS_PCIPOWER_PME_D2 5 -#define NV4_PBUS_PCIPOWER_PME_D2_SUPPORTED 0x1 -#define NV4_PBUS_PCIPOWER_PME_D3_HOT 6 -#define NV4_PBUS_PCIPOWER_PME_D3_HOT_SUPPORTED 0x1 -#define NV4_PBUS_PCIPOWER_PME_D3_COLD 7 -#define NV4_PBUS_PCIPOWER_PME_D3_COLD_SUPPORTED 0x1 -#define NV4_PBUS_PCIPOWER_STATE_CURRENT 0x1864 -#define NV4_PBUS_PCIPOWER_STATE 0 -#define NV4_PBUS_PCIPOWER_STATE_D3_HOT 0x3 -#define NV4_PBUS_PCIPOWER_STATE_D2 0x2 -#define NV4_PBUS_PCIPOWER_STATE_D1 0x1 -#define NV4_PBUS_PCIPOWER_STATE_D0 0x0 -#define NV4_PFIFO_START 0x2000 -#define NV4_PFIFO_END 0x3FFF -#define NV4_PFIFO_DELAY_0 0x2040 -#define NV4_PFIFO_DELAY_0_WAIT_RETRY 0 -#define NV4_PFIFO_DELAY_0_WAIT_RETRY_0 0x0 -#define NV4_PFIFO_DMA_TIMESLICE 0x2044 -#define NV4_PFIFO_DMA_TIMESLICE_SELECT 0 -#define NV4_PFIFO_DMA_TIMESLICE_SELECT_1 0x0 -#define NV4_PFIFO_DMA_TIMESLICE_SELECT_16K 0x3fff -#define NV4_PFIFO_DMA_TIMESLICE_SELECT_32K 0x7fff -#define NV4_PFIFO_DMA_TIMESLICE_SELECT_64K 0xffff -#define NV4_PFIFO_DMA_TIMESLICE_SELECT_128K 0x1ffff -#define NV4_PFIFO_DMA_TIMESLICE_TIMEOUT 24 -#define NV4_PFIFO_DMA_TIMESLICE_TIMEOUT_ENABLED 0x1 -#define NV4_PFIFO_PIO_TIMESLICE 0x2048 -#define NV4_PFIFO_PIO_TIMESLICE_SELECT 0 -#define NV4_PFIFO_PIO_TIMESLICE_SELECT_1 0x0 -#define NV4_PFIFO_PIO_TIMESLICE_SELECT_16K 0x3fff -#define NV4_PFIFO_PIO_TIMESLICE_SELECT_32K 0x7fff -#define NV4_PFIFO_PIO_TIMESLICE_SELECT_64K 0xffff -#define NV4_PFIFO_PIO_TIMESLICE_SELECT_128K 0x1ffff -#define NV4_PFIFO_PIO_TIMESLICE_TIMEOUT 24 -#define NV4_PFIFO_PIO_TIMESLICE_TIMEOUT_ENABLED 0x1 -#define NV4_PFIFO_TIMESLICE 0x204C -#define NV4_PFIFO_TIMESLICE_TIMER 0 -#define NV4_PFIFO_TIMESLICE_TIMER_EXPIRED 0x3FFFF -#define NV4_PFIFO_NEXT_CHANNEL 0x2050 -#define NV4_PFIFO_NEXT_CHANNEL_CHID 0 -#define NV4_PFIFO_NEXT_CHANNEL_MODE 8 -#define NV4_PFIFO_NEXT_CHANNEL_MODE_PIO 0x0 -#define NV4_PFIFO_NEXT_CHANNEL_MODE_DMA 0x1 -#define NV4_PFIFO_NEXT_CHANNEL_SWITCH 12 -#define NV4_PFIFO_NEXT_CHANNEL_SWITCH_NOT_PENDING 0x0 -#define NV4_PFIFO_NEXT_CHANNEL_SWITCH_PENDING 0x1 -#define NV4_PFIFO_DEBUG_0 0x2080 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0 0 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0_NOT_PENDING 0x0 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR0_PENDING 0x1 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1 4 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1_NOT_PENDING 0x0 -#define NV4_PFIFO_DEBUG_0_CACHE_ERROR1_PENDING 0x1 -#define NV4_PFIFO_INTR_0 0x2100 -#define NV4_PFIFO_INTR_0_CACHE_ERROR 0 -#define NV4_PFIFO_INTR_0_CACHE_ERROR_NOT_PENDING 0x0 -#define NV4_PFIFO_INTR_0_CACHE_ERROR_PENDING 0x1 -#define NV4_PFIFO_INTR_0_CACHE_ERROR_RESET 0x1 -#define NV4_PFIFO_INTR_0_RUNOUT 4 -#define NV4_PFIFO_INTR_0_RUNOUT_NOT_PENDING 0x0 -#define NV4_PFIFO_INTR_0_RUNOUT_PENDING 0x1 -#define NV4_PFIFO_INTR_0_RUNOUT_RESET 0x1 -#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW 8 -#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_NOT_PENDING 0x0 -#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_PENDING 0x1 -#define NV4_PFIFO_INTR_0_RUNOUT_OVERFLOW_RESET 0x1 -#define NV4_PFIFO_INTR_0_DMA_PUSHER 12 -#define NV4_PFIFO_INTR_0_DMA_PUSHER_NOT_PENDING 0x0 -#define NV4_PFIFO_INTR_0_DMA_PUSHER_PENDING 0x1 -#define NV4_PFIFO_INTR_0_DMA_PUSHER_RESET 0x1 -#define NV4_PFIFO_INTR_0_DMA_PT 16 -#define NV4_PFIFO_INTR_0_DMA_PT_NOT_PENDING 0x0 -#define NV4_PFIFO_INTR_0_DMA_PT_PENDING 0x1 -#define NV4_PFIFO_INTR_0_DMA_PT_RESET 0x1 -#define NV4_PFIFO_INTR_EN_0 0x2140 -#define NV4_PFIFO_INTR_EN_0_CACHE_ERROR 0 -#define NV4_PFIFO_INTR_EN_0_CACHE_ERROR_ENABLED 0x1 -#define NV4_PFIFO_INTR_EN_0_RUNOUT 4 -#define NV4_PFIFO_INTR_EN_0_RUNOUT_ENABLED 0x1 -#define NV4_PFIFO_INTR_EN_0_RUNOUT_OVERFLOW 8 -#define NV4_PFIFO_INTR_EN_0_RUNOUT_OVERFLOW_ENABLED 0x1 -#define NV4_PFIFO_INTR_EN_0_DMA_PUSHER 12 -#define NV4_PFIFO_INTR_EN_0_DMA_PUSHER_ENABLED 0x1 -#define NV4_PFIFO_INTR_EN_0_DMA_PT 16 -#define NV4_PFIFO_INTR_EN_0_DMA_PT_ENABLED 0x1 -#define NV4_PFIFO_RAMHT 0x2210 -#define NV4_PFIFO_RAMHT_BASE_ADDRESS 4 -#define NV4_PFIFO_RAMHT_BASE_ADDRESS_10000 0x10 -#define NV4_PFIFO_RAMHT_SIZE 16 -#define NV4_PFIFO_RAMHT_SIZE_4K 0x0 -#define NV4_PFIFO_RAMHT_SIZE_8K 0x1 -#define NV4_PFIFO_RAMHT_SIZE_16K 0x2 -#define NV4_PFIFO_RAMHT_SIZE_32K 0x3 -#define NV4_PFIFO_RAMHT_SEARCH 24 -#define NV4_PFIFO_RAMHT_SEARCH_16 0x0 -#define NV4_PFIFO_RAMHT_SEARCH_32 0x1 -#define NV4_PFIFO_RAMHT_SEARCH_64 0x2 -#define NV4_PFIFO_RAMHT_SEARCH_128 0x3 -#define NV4_PFIFO_RAMFC 0x2214 -#define NV4_PFIFO_RAMFC_BASE_ADDRESS 1 -#define NV4_PFIFO_RAMFC_BASE_ADDRESS_11000 0x88 -#define NV4_PFIFO_RAMRO 0x2218 -#define NV4_PFIFO_RAMRO_BASE_ADDRESS 1 -#define NV4_PFIFO_RAMRO_BASE_ADDRESS_11200 0x89 -#define NV4_PFIFO_RAMRO_BASE_ADDRESS_12000 0x90 -#define NV4_PFIFO_RAMRO_SIZE 16 -#define NV4_PFIFO_RAMRO_SIZE_512 0x0 -#define NV4_PFIFO_RAMRO_SIZE_8K 0x1 -#define NV4_PFIFO_CACHES 0x2500 -#define NV4_PFIFO_CACHES_REASSIGN 0 -#define NV4_PFIFO_CACHES_REASSIGN_ENABLED 0x1 -#define NV4_PFIFO_CACHES_DMA_SUSPEND 4 -#define NV4_PFIFO_CACHES_DMA_SUSPEND_IDLE 0x0 -#define NV4_PFIFO_CACHES_DMA_SUSPEND_BUSY 0x1 -#define NV4_PFIFO_MODE 0x2504 -// Valid for all 16 channels. Do we need thees at all? -#define NV4_PFIFO_MODE_CHANNEL_IS_PIO 0x0 -#define NV4_PFIFO_MODE_CHANNEL_IS_DMA 0x1 -#define NV4_PFIFO_MODE_CHANNEL_0 0 -#define NV4_PFIFO_MODE_CHANNEL_1 1 -#define NV4_PFIFO_MODE_CHANNEL_2 2 -#define NV4_PFIFO_MODE_CHANNEL_3 3 -#define NV4_PFIFO_MODE_CHANNEL_4 4 -#define NV4_PFIFO_MODE_CHANNEL_5 5 -#define NV4_PFIFO_MODE_CHANNEL_6 6 -#define NV4_PFIFO_MODE_CHANNEL_7 7 -#define NV4_PFIFO_MODE_CHANNEL_8 8 -#define NV4_PFIFO_MODE_CHANNEL_9 9 -#define NV4_PFIFO_MODE_CHANNEL_10 10 -#define NV4_PFIFO_MODE_CHANNEL_11 11 -#define NV4_PFIFO_MODE_CHANNEL_12 12 -#define NV4_PFIFO_MODE_CHANNEL_13 13 -#define NV4_PFIFO_MODE_CHANNEL_14 14 -#define NV4_PFIFO_MODE_CHANNEL_15 15 -#define NV4_PFIFO_DMA 0x2508 -// 0 = not pending (Valid for all channels) -#define NV4_PFIFO_DMA_CHANNEL_IS_PENDING 0x1 -#define NV4_PFIFO_DMA_CHANNEL_0 0 -#define NV4_PFIFO_DMA_CHANNEL_1 1 -#define NV4_PFIFO_DMA_CHANNEL_2 2 -#define NV4_PFIFO_DMA_CHANNEL_3 3 -#define NV4_PFIFO_DMA_CHANNEL_4 4 -#define NV4_PFIFO_DMA_CHANNEL_5 5 -#define NV4_PFIFO_DMA_CHANNEL_6 6 -#define NV4_PFIFO_DMA_CHANNEL_7 7 -#define NV4_PFIFO_DMA_CHANNEL_8 8 -#define NV4_PFIFO_DMA_CHANNEL_9 9 -#define NV4_PFIFO_DMA_CHANNEL_10 10 -#define NV4_PFIFO_DMA_CHANNEL_11 11 -#define NV4_PFIFO_DMA_CHANNEL_12 12 -#define NV4_PFIFO_DMA_CHANNEL_13 13 -#define NV4_PFIFO_DMA_CHANNEL_14 14 -#define NV4_PFIFO_DMA_CHANNEL_15 15 -#define NV4_PFIFO_SIZE 0x250C -// Valid for all channels -#define NV4_PFIFO_SIZE_CHANNEL_SIZE_IS_124_BYTES 0x0 -#define NV4_PFIFO_SIZE_CHANNEL_SIZE_IS_512_BYTES 0x1 -#define NV4_PFIFO_SIZE_CHANNEL_0 0 -#define NV4_PFIFO_SIZE_CHANNEL_1 1 -#define NV4_PFIFO_SIZE_CHANNEL_2 2 -#define NV4_PFIFO_SIZE_CHANNEL_3 3 -#define NV4_PFIFO_SIZE_CHANNEL_4 4 -#define NV4_PFIFO_SIZE_CHANNEL_5 5 -#define NV4_PFIFO_SIZE_CHANNEL_6 6 -#define NV4_PFIFO_SIZE_CHANNEL_7 7 -#define NV4_PFIFO_SIZE_CHANNEL_8 8 -#define NV4_PFIFO_SIZE_CHANNEL_9 9 -#define NV4_PFIFO_SIZE_CHANNEL_10 10 -#define NV4_PFIFO_SIZE_CHANNEL_11 11 -#define NV4_PFIFO_SIZE_CHANNEL_12 12 -#define NV4_PFIFO_SIZE_CHANNEL_13 13 -#define NV4_PFIFO_SIZE_CHANNEL_14 14 -#define NV4_PFIFO_SIZE_CHANNEL_15 15 -#define NV4_PFIFO_CACHE0_PUSH0 0x3000 -#define NV4_PFIFO_CACHE0_PUSH0_ACCESS 0 -#define NV4_PFIFO_CACHE0_PUSH0_ACCESS_ENABLED 0x1 -#define NV4_PFIFO_CACHE1_PUSH0 0x3200 -#define NV4_PFIFO_CACHE1_PUSH0_ACCESS 0 -#define NV4_PFIFO_CACHE1_PUSH0_ACCESS_ENABLED 0x1 -#define NV4_PFIFO_CACHE0_PUSH1 0x3004 -#define NV4_PFIFO_CACHE0_PUSH1_CHID 0 -#define NV4_PFIFO_CACHE1_PUSH1 0x3204 -#define NV4_PFIFO_CACHE1_PUSH1_CHID 0 -#define NV4_PFIFO_CACHE1_PUSH1_MODE 8 -#define NV4_PFIFO_CACHE1_PUSH1_MODE_PIO 0x0 -#define NV4_PFIFO_CACHE1_PUSH1_MODE_DMA 0x1 -#define NV4_PFIFO_CACHE1_DMA_PUSH 0x3220 -#define NV4_PFIFO_CACHE1_DMA_PUSH_ACCESS 0 -#define NV4_PFIFO_CACHE1_DMA_PUSH_ACCESS_ENABLED 0x1 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE 4 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE_IDLE 0x0 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATE_BUSY 0x1 -#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER 8 -#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER_NOT_EMPTY 0x0 -#define NV4_PFIFO_CACHE1_DMA_PUSH_BUFFER_EMPTY 0x1 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS 12 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS_RUNNING 0x0 -#define NV4_PFIFO_CACHE1_DMA_PUSH_STATUS_SUSPENDED 0x1 -#define NV4_PFIFO_CACHE1_DMA_FETCH 0x3224 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG 7:3 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x0 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x1 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x2 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x3 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x4 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x5 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x6 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x7 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x8 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x9 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0xA -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0xB -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0xC -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0xD -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0xE -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0xF -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x10 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x11 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x12 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x13 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x14 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x15 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x16 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x17 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x18 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x19 -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x1A -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x1B -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x1C -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x1D -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x1E -#define NV4_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x1F -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE 13 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x0 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x1 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x2 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x3 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x4 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x5 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x6 -#define NV4_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x7 -// This is a count. 0x0-0xF = number of requests -#define NV4_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 16 -#define NV4_PFIFO_CACHE1_DMA_PUT 0x3240 -#define NV4_PFIFO_CACHE1_DMA_PUT_OFFSET 2 -#define NV4_PFIFO_CACHE1_DMA_GET 0x3244 -#define NV4_PFIFO_CACHE1_DMA_GET_OFFSET 2 -#define NV4_PFIFO_CACHE1_DMA_DCOUNT 0x32A0 -#define NV4_PFIFO_CACHE1_DMA_DCOUNT_VALUE 2 -#define NV4_PFIFO_CACHE1_DMA_GET_JMP_SHADOW 0x32A4 -#define NV4_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET 2 -#define NV4_PFIFO_CACHE1_DMA_RSVD_SHADOW 0x32A8 -#define NV4_PFIFO_CACHE1_DMA_RSVD_SHADOW_CMD 0 -#define NV4_PFIFO_CACHE1_DMA_DATA_SHADOW 0x32AC -#define NV4_PFIFO_CACHE1_DMA_DATA_SHADOW_VALUE 0 -#define NV4_PFIFO_CACHE1_DMA_STATE 0x3228 -#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD 2 -#define NV4_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL 13 -#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT 18 -#define NV4_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT_0 0x0 -#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR 30 -#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_NONE 0x0 -#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_NON_CACHE 0x1 -#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_RESERVED_CMD 0x2 -#define NV4_PFIFO_CACHE1_DMA_STATE_ERROR_PROTECTION 0x3 -#define NV4_PFIFO_CACHE1_DMA_INSTANCE 0x322C -#define NV4_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS 0 -#define NV4_PFIFO_CACHE1_DMA_CTL 0x3230 -#define NV4_PFIFO_CACHE1_DMA_CTL_ADJUST 11:2 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE 12 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE_NOT_PRESENT 0x0 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_TABLE_PRESENT 0x1 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY 13 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY_NOT_LINEAR 0x0 -#define NV4_PFIFO_CACHE1_DMA_CTL_PAGE_ENTRY_LINEAR 0x1 -#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE 16 -#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE_PCI 0x2 -#define NV4_PFIFO_CACHE1_DMA_CTL_TARGET_NODE_AGP 0x3 -#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO 31 -#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO_INVALID 0x0 -#define NV4_PFIFO_CACHE1_DMA_CTL_AT_INFO_VALID 0x1 -#define NV4_PFIFO_CACHE1_DMA_LIMIT 0x3234 -#define NV4_PFIFO_CACHE1_DMA_LIMIT_OFFSET 2 -#define NV4_PFIFO_CACHE1_DMA_TLB_TAG 0x3238 -#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_ADDRESS 28:12 -#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE 0 -#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE_INVALID 0x0 -#define NV4_PFIFO_CACHE1_DMA_TLB_TAG_STATE_VALID 0x1 -#define NV4_PFIFO_CACHE1_DMA_TLB_PTE 0x323C -#define NV4_PFIFO_CACHE1_DMA_TLB_PTE_FRAME_ADDRESS 12 -#define NV4_PFIFO_CACHE0_PULL0 0x3050 -#define NV4_PFIFO_CACHE0_PULL0_ACCESS 0 -#define NV4_PFIFO_CACHE0_PULL0_ACCESS_ENABLED 0x1 -#define NV4_PFIFO_CACHE0_PULL0_HASH 4 -#define NV4_PFIFO_CACHE0_PULL0_HASH_SUCCEEDED 0x0 -#define NV4_PFIFO_CACHE0_PULL0_HASH_FAILED 0x1 -#define NV4_PFIFO_CACHE0_PULL0_DEVICE 8 -#define NV4_PFIFO_CACHE0_PULL0_DEVICE_HARDWARE 0x0 -#define NV4_PFIFO_CACHE0_PULL0_DEVICE_SOFTWARE 0x1 -#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE 12 -#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE_IDLE 0x0 -#define NV4_PFIFO_CACHE0_PULL0_HASH_STATE_BUSY 0x1 -#define NV4_PFIFO_CACHE1_PULL0 0x3250 -#define NV4_PFIFO_CACHE1_PULL0_ACCESS 0 -#define NV4_PFIFO_CACHE1_PULL0_ACCESS_ENABLED 0x1 -#define NV4_PFIFO_CACHE1_PULL0_HASH 4 -#define NV4_PFIFO_CACHE1_PULL0_HASH_SUCCEEDED 0x0 -#define NV4_PFIFO_CACHE1_PULL0_HASH_FAILED 0x1 -#define NV4_PFIFO_CACHE1_PULL0_DEVICE 8 -#define NV4_PFIFO_CACHE1_PULL0_DEVICE_HARDWARE 0x0 -#define NV4_PFIFO_CACHE1_PULL0_DEVICE_SOFTWARE 0x1 -#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE 12 -#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE_IDLE 0x0 -#define NV4_PFIFO_CACHE1_PULL0_HASH_STATE_BUSY 0x1 -#define NV4_PFIFO_CACHE0_PULL1 0x3054 -#define NV4_PFIFO_CACHE0_PULL1_ENGINE 0 -#define NV4_PFIFO_CACHE0_PULL1_ENGINE_SW 0x0 -#define NV4_PFIFO_CACHE0_PULL1_ENGINE_GRAPHICS 0x1 -#define NV4_PFIFO_CACHE0_PULL1_ENGINE_DVD 0x2 -#define NV4_PFIFO_CACHE1_PULL1 0x3254 -#define NV4_PFIFO_CACHE1_PULL1_ENGINE 0 -#define NV4_PFIFO_CACHE1_PULL1_ENGINE_SW 0x0 -#define NV4_PFIFO_CACHE1_PULL1_ENGINE_GRAPHICS 0x1 -#define NV4_PFIFO_CACHE1_PULL1_ENGINE_DVD 0x2 -#define NV4_PFIFO_CACHE0_HASH 0x3058 -#define NV4_PFIFO_CACHE0_HASH_INSTANCE 0 -#define NV4_PFIFO_CACHE0_HASH_VALID 16 -#define NV4_PFIFO_CACHE1_HASH 0x3258 -#define NV4_PFIFO_CACHE1_HASH_INSTANCE 0 -#define NV4_PFIFO_CACHE1_HASH_VALID 16 -#define NV4_PFIFO_CACHE0_STATUS 0x3014 -#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK 4 -#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK_NOT_EMPTY 0x0 -#define NV4_PFIFO_CACHE0_STATUS_LOW_MARK_EMPTY 0x1 -#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK 8 -#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK_NOT_FULL 0x0 -#define NV4_PFIFO_CACHE0_STATUS_HIGH_MARK_FULL 0x1 -#define NV4_PFIFO_CACHE1_STATUS 0x3214 -#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK 4 -#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK_NOT_EMPTY 0x0 -#define NV4_PFIFO_CACHE1_STATUS_LOW_MARK_EMPTY 0x1 -#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK 8 -#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK_NOT_FULL 0x0 -#define NV4_PFIFO_CACHE1_STATUS_HIGH_MARK_FULL 0x1 -#define NV4_PFIFO_CACHE1_STATUS1 0x3218 -#define NV4_PFIFO_CACHE1_STATUS1_RANOUT 0 -#define NV4_PFIFO_CACHE1_STATUS1_RANOUT_TRUE 0x1 -#define NV4_PFIFO_CACHE0_PUT 0x3010 -#define NV4_PFIFO_CACHE0_PUT_ADDRESS 2 -#define NV4_PFIFO_CACHE1_PUT 0x3210 -#define NV4_PFIFO_CACHE1_PUT_ADDRESS 2 -#define NV4_PFIFO_CACHE0_GET 0x3070 -#define NV4_PFIFO_CACHE0_GET_ADDRESS 2 -#define NV4_PFIFO_CACHE1_GET 0x3270 -#define NV4_PFIFO_CACHE1_GET_ADDRESS 2 -#define NV4_PFIFO_ENGINE_SW 0x0 -#define NV4_PFIFO_ENGINE_GRAPHICS 0x1 -#define NV4_PFIFO_ENGINE_DVD 0x2 -#define NV4_PFIFO_CACHE0_ENGINE 0x3080 -// For these, see above -#define NV4_PFIFO_CACHE0_SUBCHANNEL_0_ENGINE 0 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_1_ENGINE 4 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_2_ENGINE 8 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_3_ENGINE 12 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_4_ENGINE 16 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_5_ENGINE 20 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_6_ENGINE 24 -#define NV4_PFIFO_CACHE0_SUBCHANNEL_7_ENGINE 28 -#define NV4_PFIFO_CACHE1_ENGINE 0x3280 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_0_ENGINE 0 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_1_ENGINE 4 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_2_ENGINE 8 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_3_ENGINE 12 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_4_ENGINE 16 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_5_ENGINE 20 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_6_ENGINE 24 -#define NV4_PFIFO_CACHE1_SUBCHANNEL_7_ENGINE 28 -#define NV4_PFIFO_CACHE0_METHOD(i) (0x3100+(i)*8) -#define NV4_PFIFO_CACHE0_METHOD_SIZE_1 1 -#define NV4_PFIFO_CACHE0_METHOD_ADDRESS 2 -#define NV4_PFIFO_CACHE0_METHOD_SUBCHANNEL 13 -#define NV4_PFIFO_CACHE1_METHOD(i) (0x3800+(i)*8) -#define NV4_PFIFO_CACHE1_METHOD_SIZE_1 128 -#define NV4_PFIFO_CACHE1_METHOD_ADDRESS 2 -#define NV4_PFIFO_CACHE1_METHOD_SUBCHANNEL 13 -#define NV4_PFIFO_CACHE1_METHOD_ALIAS(i) (0x3C00+(i)*8) -#define NV4_PFIFO_CACHE1_METHOD_ALIAS_SIZE_1 128 -#define NV4_PFIFO_CACHE0_DATA(i) (0x3104+(i)*8) -#define NV4_PFIFO_CACHE0_DATA_SIZE_1 1 -#define NV4_PFIFO_CACHE0_DATA_VALUE 0 -#define NV4_PFIFO_CACHE1_DATA(i) (0x3804+(i)*8) -#define NV4_PFIFO_CACHE1_DATA_SIZE_1 128 -#define NV4_PFIFO_CACHE1_DATA_VALUE 0 -#define NV4_PFIFO_CACHE1_DATA_ALIAS(i) (0x3C04+(i)*8) -#define NV4_PFIFO_CACHE1_DATA_ALIAS_SIZE_1 128 -#define NV4_PFIFO_DEVICE(i) (0x2800+(i)*4) -#define NV4_PFIFO_DEVICE_SIZE_1 128 -#define NV4_PFIFO_DEVICE_CHID 0 -#define NV4_PFIFO_DEVICE_SWITCH 24 -#define NV4_PFIFO_DEVICE_SWITCH_AVAILABLE 0x1 -#define NV4_PFIFO_RUNOUT_STATUS 0x2400 -#define NV4_PFIFO_RUNOUT_STATUS_RANOUT 0 -#define NV4_PFIFO_RUNOUT_STATUS_RANOUT_TRUE 0x1 -#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK 4 -#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK_NOT_EMPTY 0x0 -#define NV4_PFIFO_RUNOUT_STATUS_LOW_MARK_EMPTY 0x1 -#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK 8 -#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK_NOT_FULL 0x0 -#define NV4_PFIFO_RUNOUT_STATUS_HIGH_MARK_FULL 0x1 -#define NV4_PFIFO_RUNOUT_PUT 0x2410 -#define NV4_PFIFO_RUNOUT_PUT_ADDRESS 3 // if size=0, 8:3, otherwise 12:3 -#define NV4_PFIFO_RUNOUT_GET 0x2420 -#define NV4_PFIFO_RUNOUT_GET_ADDRESS 3 // 13:3 - -#define NV4_PGRAPH_START 0x400000 -#define NV4_PGRAPH_END 0x401FFF - -#define NV4_PGRAPH_DEBUG_0 0x400080 -#define NV4_PGRAPH_DEBUG_0_STATE 0 -#define NV4_PGRAPH_DEBUG_0_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_FE_STATE 1 -#define NV4_PGRAPH_DEBUG_0_FE_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_FE_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_CACHE_STATE 2 -#define NV4_PGRAPH_DEBUG_0_CACHE_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_CACHE_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE 3 -#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_D3D_PIPE_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_PREROP_STATE 4 -#define NV4_PGRAPH_DEBUG_0_PREROP_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_PREROP_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_ROP_STATE 5 -#define NV4_PGRAPH_DEBUG_0_ROP_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_ROP_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_RSTR_STATE 6 -#define NV4_PGRAPH_DEBUG_0_RSTR_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_RSTR_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE 7 -#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_LIGHT_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_DMA_STATE 8 -#define NV4_PGRAPH_DEBUG_0_DMA_STATE_NORMAL 0x0 -#define NV4_PGRAPH_DEBUG_0_DMA_STATE_RESET 0x1 -#define NV4_PGRAPH_DEBUG_0_SPARE1 12 -#define NV4_PGRAPH_DEBUG_0_SPARE2 13 -#define NV4_PGRAPH_DEBUG_0_MINUSD5 14 -#define NV4_PGRAPH_DEBUG_0_MINUSD5_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_BLIT_DST_LIMIT 15 -#define NV4_PGRAPH_DEBUG_0_BLIT_DST_LIMIT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_LIMIT_CHECK 16 -#define NV4_PGRAPH_DEBUG_0_LIMIT_CHECK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_LIMIT_INT 17 -#define NV4_PGRAPH_DEBUG_0_LIMIT_INT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_OVRFLW_INT 18 -#define NV4_PGRAPH_DEBUG_0_OVRFLW_INT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D 20 -#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_2D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D 21 -#define NV4_PGRAPH_DEBUG_0_WRITE_ONLY_ROPS_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_DRAWDIR_AUTO 24 -#define NV4_PGRAPH_DEBUG_0_DRAWDIR_AUTO_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y 25 -#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y_DECR 0x0 -#define NV4_PGRAPH_DEBUG_0_DRAWDIR_Y_INCR 0x1 -#define NV4_PGRAPH_DEBUG_0_ALPHA_ABORT 28 -#define NV4_PGRAPH_DEBUG_0_ALPHA_ABORT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1 0x400084 -#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET 0 -#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET_NOT_LAST 0x0 -#define NV4_PGRAPH_DEBUG_1_VOLATILE_RESET_LAST 0x1 -#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY 4 -#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY_IGNORE 0x0 -#define NV4_PGRAPH_DEBUG_1_DMA_ACTIVITY_CANCEL 0x1 -#define NV4_PGRAPH_DEBUG_1_PATCH_INV 8 -#define NV4_PGRAPH_DEBUG_1_PATCH_INV_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_ALT_RW_SEQ 10 -#define NV4_PGRAPH_DEBUG_1_ALT_RW_SEQ_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_TRI_OPTS 12 -#define NV4_PGRAPH_DEBUG_1_TRI_OPTS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_TRICLIP_OPTS 13 -#define NV4_PGRAPH_DEBUG_1_TRICLIP_OPTS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_INSTANCE 16 -#define NV4_PGRAPH_DEBUG_1_INSTANCE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_ALOM_BURST 17 -#define NV4_PGRAPH_DEBUG_1_ALOM_BURST_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_BIDIR_DRAIN 18 -#define NV4_PGRAPH_DEBUG_1_BIDIR_DRAIN_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_EARLY_POST 19 -#define NV4_PGRAPH_DEBUG_1_EARLY_POST_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_CTX 20 -#define NV4_PGRAPH_DEBUG_1_CTX_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_FIXED_ADRS 21 -#define NV4_PGRAPH_DEBUG_1_FIXED_ADRS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_2D 22 -#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_2D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_3D 23 -#define NV4_PGRAPH_DEBUG_1_DITHER_RANGE_ADJ_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_CACHE 24 -#define NV4_PGRAPH_DEBUG_1_CACHE_IGNORE 0x0 -#define NV4_PGRAPH_DEBUG_1_CACHE_FLUSH 0x1 -#define NV4_PGRAPH_DEBUG_1_CACHE_MODE 25 -#define NV4_PGRAPH_DEBUG_1_CACHE_MODE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_ZCLAMP 28 -#define NV4_PGRAPH_DEBUG_1_ZCLAMP_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_UCLAMP 29 -#define NV4_PGRAPH_DEBUG_1_UCLAMP_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_RCLAMP 30 -#define NV4_PGRAPH_DEBUG_1_RCLAMP_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_1_DX6_2PIXMODE 31 -#define NV4_PGRAPH_DEBUG_1_DX6_2PIXMODE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2 0x400088 -#define NV4_PGRAPH_DEBUG_2_HONOR_SRCFMT 0 -#define NV4_PGRAPH_DEBUG_2_HONOR_SRCFMT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_PINGPONG 0 -#define NV4_PGRAPH_DEBUG_2_PINGPONG_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_SPARE1 4 -#define NV4_PGRAPH_DEBUG_2_SPARE2 5 -#define NV4_PGRAPH_DEBUG_2_SPARE3 6 -#define NV4_PGRAPH_DEBUG_2_SPARE4 7 -#define NV4_PGRAPH_DEBUG_2_SPARE5 8 -#define NV4_PGRAPH_DEBUG_2_SPARE6 9 -#define NV4_PGRAPH_DEBUG_2_SPARE7 10 -#define NV4_PGRAPH_DEBUG_2_MCLK_RECTS 11 -#define NV4_PGRAPH_DEBUG_2_MCLK_RECTS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_BILINEAR_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_ANISOTROPIC_3D 13 -#define NV4_PGRAPH_DEBUG_2_ANISOTROPIC_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_FOG_3D 14 -#define NV4_PGRAPH_DEBUG_2_FOG_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_SPECULAR_3D 15 -#define NV4_PGRAPH_DEBUG_2_SPECULAR_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_ALPHA_3D 16 -#define NV4_PGRAPH_DEBUG_2_ALPHA_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ 17 -#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_CRZRWCW 0x0 -#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_ZRWCRW 0x1 -#define NV4_PGRAPH_DEBUG_2_ZBUF_SEQ_AUTO 0x2 -#define NV4_PGRAPH_DEBUG_2_COELESCE_D3D 20 -#define NV4_PGRAPH_DEBUG_2_COELESCE_D3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_COELESCE_2D 22 -#define NV4_PGRAPH_DEBUG_2_COELESCE_2D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_FAST_VERTEX_LOAD 23 -#define NV4_PGRAPH_DEBUG_2_FAST_VERTEX_LOAD_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_BLIT_MULTILINE 24 -#define NV4_PGRAPH_DEBUG_2_BLIT_MULTILINE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_2_VOLATILE_RESET 28 -#define NV4_PGRAPH_DEBUG_2_VOLATILE_RESET_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3 0x40008C -#define NV4_PGRAPH_DEBUG_3_CULLING 0 -#define NV4_PGRAPH_DEBUG_3_CULLING_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE 1 -#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE_DX3 0x0 -#define NV4_PGRAPH_DEBUG_3_CULLING_TYPE_DX5 0x1 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_STRTCH 4 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_STRTCH_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_D3D 5 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_D3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_IMAGE 6 -#define NV4_PGRAPH_DEBUG_3_FAST_DATA_IMAGE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_ZFLUSH 7 -#define NV4_PGRAPH_DEBUG_3_ZFLUSH_IGNORE 0x0 -#define NV4_PGRAPH_DEBUG_3_ZFLUSH_ACTIVATE 0x1 -#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_PTZ 8 -#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_PTZ_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D 9 -#define NV4_PGRAPH_DEBUG_3_AUTOZFLUSH_D3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_PTZ 10 -#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_PTZ_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D 11 -#define NV4_PGRAPH_DEBUG_3_SLOT_CONFLICT_D3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_POSTDITHER_2D 12 -#define NV4_PGRAPH_DEBUG_3_POSTDITHER_2D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_POSTDITHER_3D 13 -#define NV4_PGRAPH_DEBUG_3_POSTDITHER_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_PREDITHER_2D 14 -#define NV4_PGRAPH_DEBUG_3_PREDITHER_2D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_PREDITHER_3D 15 -#define NV4_PGRAPH_DEBUG_3_PREDITHER_3D_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_FORCE_CREAD 16 -#define NV4_PGRAPH_DEBUG_3_FORCE_CREAD_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_FORCE_ZREAD 17 -#define NV4_PGRAPH_DEBUG_3_FORCE_ZREAD_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_EARLYZ_ABORT 18 -#define NV4_PGRAPH_DEBUG_3_EARLYZ_ABORT_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_TRIEND_FLUSH 19 -#define NV4_PGRAPH_DEBUG_3_TRIEND_FLUSH_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_DATA_CHECK 20 -#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_FAIL 21 -#define NV4_PGRAPH_DEBUG_3_DATA_CHECK_FAIL_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_FORMAT_CHECK 22 -#define NV4_PGRAPH_DEBUG_3_FORMAT_CHECK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_DMA_CHECK 23 -#define NV4_PGRAPH_DEBUG_3_DMA_CHECK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_RAMREADBACK 24 -#define NV4_PGRAPH_DEBUG_3_RAMREADBACK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_CLIP_METHODS 25 -#define NV4_PGRAPH_DEBUG_3_CLIP_METHODS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_EXCLUDE_ROP_IN_IDLE 27 -#define NV4_PGRAPH_DEBUG_3_EXCLUDE_ROP_IN_IDLE_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_STATE_CHECK 28 -#define NV4_PGRAPH_DEBUG_3_STATE_CHECK_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_CONTEXT_METHODS 29 -#define NV4_PGRAPH_DEBUG_3_CONTEXT_METHODS_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_OPERATION_METHOD 30 -#define NV4_PGRAPH_DEBUG_3_OPERATION_METHOD_ENABLED 0x1 -#define NV4_PGRAPH_DEBUG_3_IGNORE_PATCHVALID 31 -#define NV4_PGRAPH_DEBUG_3_IGNORE_PATCHVALID_ENABLED 0x1 -#define NV4_PGRAPH_INTR 0x400100 -#define NV4_PGRAPH_INTR_NOTIFY 0 -#define NV4_PGRAPH_INTR_NOTIFY_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_NOTIFY_PENDING 0x1 -#define NV4_PGRAPH_INTR_NOTIFY_RESET 0x1 -#define NV4_PGRAPH_INTR_MISSING_HW 4 -#define NV4_PGRAPH_INTR_MISSING_HW_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_MISSING_HW_PENDING 0x1 -#define NV4_PGRAPH_INTR_MISSING_HW_RESET 0x1 -#define NV4_PGRAPH_INTR_TLB_PRESENT_A 8 -#define NV4_PGRAPH_INTR_TLB_PRESENT_A_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_TLB_PRESENT_A_PENDING 0x1 -#define NV4_PGRAPH_INTR_TLB_PRESENT_A_RESET 0x1 -#define NV4_PGRAPH_INTR_TLB_PRESENT_B 9 -#define NV4_PGRAPH_INTR_TLB_PRESENT_B_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_TLB_PRESENT_B_PENDING 0x1 -#define NV4_PGRAPH_INTR_TLB_PRESENT_B_RESET 0x1 -#define NV4_PGRAPH_INTR_CONTEXT_SWITCH 12 -#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_PENDING 0x1 -#define NV4_PGRAPH_INTR_CONTEXT_SWITCH_RESET 0x1 -#define NV4_PGRAPH_INTR_BUFFER_NOTIFY 16 -#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_NOT_PENDING 0x0 -#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_PENDING 0x1 -#define NV4_PGRAPH_INTR_BUFFER_NOTIFY_RESET 0x1 -#define NV4_PGRAPH_NSTATUS 0x400104 -#define NV4_PGRAPH_NSTATUS_STATE_IN_USE 11 -#define NV4_PGRAPH_NSTATUS_STATE_IN_USE_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSTATUS_STATE_IN_USE_PENDING 0x1 -#define NV4_PGRAPH_NSTATUS_INVALID_STATE 12 -#define NV4_PGRAPH_NSTATUS_INVALID_STATE_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSTATUS_INVALID_STATE_PENDING 0x1 -#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT 13 -#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSTATUS_BAD_ARGUMENT_PENDING 0x1 -#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT 14 -#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSTATUS_PROTECTION_FAULT_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE 0x400108 -#define NV4_PGRAPH_NSOURCE_NOTIFICATION 0 -#define NV4_PGRAPH_NSOURCE_NOTIFICATION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_NOTIFICATION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_DATA_ERROR 1 -#define NV4_PGRAPH_NSOURCE_DATA_ERROR_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_DATA_ERROR_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR 2 -#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_PROTECTION_ERROR_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION 3 -#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_RANGE_EXCEPTION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR 4 -#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_LIMIT_COLOR_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_ 5 -#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_LIMIT_ZETA_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD 6 -#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_ILLEGAL_MTHD_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION 7 -#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_DMA_R_PROTECTION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION 8 -#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_DMA_W_PROTECTION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION 9 -#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_FORMAT_EXCEPTION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION 10 -#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_PATCH_EXCEPTION_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_STATE_INVALID 11 -#define NV4_PGRAPH_NSOURCE_STATE_INVALID_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_STATE_INVALID_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY 12 -#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_DOUBLE_NOTIFY_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE 13 -#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_NOTIFY_IN_USE_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_METHOD_CNT 14 -#define NV4_PGRAPH_NSOURCE_METHOD_CNT_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_METHOD_CNT_PENDING 0x1 -#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION 15 -#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION_NOT_PENDING 0x0 -#define NV4_PGRAPH_NSOURCE_BFR_NOTIFICATION_PENDING 0x1 -#define NV4_PGRAPH_INTR_EN 0x400140 -#define NV4_PGRAPH_INTR_EN_NOTIFY 0 -#define NV4_PGRAPH_INTR_EN_NOTIFY_ENABLED 0x1 -#define NV4_PGRAPH_INTR_EN_MISSING_HW 4 -#define NV4_PGRAPH_INTR_EN_MISSING_HW_ENABLED 0x1 -#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_A 8 -#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_A_ENABLED 0x1 -#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_B 9 -#define NV4_PGRAPH_INTR_EN_TLB_PRESENT_B_ENABLED 0x1 -#define NV4_PGRAPH_INTR_EN_CONTEXT_SWITCH 12 -#define NV4_PGRAPH_INTR_EN_CONTEXT_SWITCH_ENABLED 0x1 -#define NV4_PGRAPH_INTR_EN_BUFFER_NOTIFY 16 -#define NV4_PGRAPH_INTR_EN_BUFFER_NOTIFY_ENABLED 0x1 -#define NV4_PGRAPH_CTX_SWITCH1 0x400160 -#define NV4_PGRAPH_CTX_SWITCH1_GRCLASS 0 -#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY 12 -#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY_DISABLE 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_CHROMA_KEY_ENABLE 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP 13 -#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP_DISABLE 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_USER_CLIP_ENABLE 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE 14 -#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE_DISABLE 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_SWIZZLE_ENABLE 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG 17:15 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY_AND 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_ROP_AND 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_BLEND_AND 0x2 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY 0x3 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_SRCCOPY_PRE 0x4 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_CONFIG_BLEND_PRE 0x5 -#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE 20 -#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_COMPATIBILITY 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_DITHER 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_TRUNCATE 0x2 -#define NV4_PGRAPH_CTX_SWITCH1_DITHER_MODE_MS 0x3 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS 24 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS_INVALID 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_PATCH_STATUS_VALID 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE 25 -#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE_INVALID 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_CONTEXT_SURFACE_VALID 0x1 -#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET 31 -#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET_IGNORE 0x0 -#define NV4_PGRAPH_CTX_SWITCH1_VOLATILE_RESET_ENABLED 0x1 -#define NV4_PGRAPH_CTX_SWITCH2 0x400164 -#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT 0 -#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_INVALID 0x00 -#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_CGA6_M1 0x01 -#define NV4_PGRAPH_CTX_SWITCH2_MONO_FORMAT_LE_M1 0x02 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT 13:8 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_INVALID 0x00 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y8 0x01 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16A8Y8 0x02 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X24Y8 0x03 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A1R5G5B5 0x06 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X1R5G5B5 0x07 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16A1R5G5B5 0x08 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X17R5G5B5 0x09 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_R5G6B5 0x0A -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A16R5G6B5 0x0B -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16R5G6B5 0x0C -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A8R8G8B8 0x0D -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X8R8G8B8 0x0E -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y16 0x0F -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_A16Y16 0x10 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_X16Y16 0x11 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_V8YB8U8YA8 0x12 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_YB8V8YA8U8 0x13 -#define NV4_PGRAPH_CTX_SWITCH2_COLOR_FORMAT_LE_Y32 0x14 -#define NV4_PGRAPH_CTX_SWITCH2_NOTIFY_INSTANCE 16 -#define NV4_PGRAPH_CTX_SWITCH2_NOTIFY_INSTANCE_INVALID 0x00 -#define NV4_PGRAPH_CTX_SWITCH3 0x400168 -#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_0 0 -#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_0_INVALID 0x00 -#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_1 16 -#define NV4_PGRAPH_CTX_SWITCH3_DMA_INSTANCE_1_INVALID 0x00 -#define NV4_PGRAPH_CTX_SWITCH4 0x40016C -#define NV4_PGRAPH_CTX_SWITCH4_USER_INSTANCE 0 -#define NV4_PGRAPH_CTX_SWITCH4_USER_INSTANCE_INVALID 0x00 -#define NV4_PGRAPH_CTX_CACHE1(i) (0x400180+(i)*4) -#define NV4_PGRAPH_CTX_CACHE1_SIZE_1 8 -#define NV4_PGRAPH_CTX_CACHE1_GRCLASS 0 -#define NV4_PGRAPH_CTX_CACHE1_CHROMA_KEY 12 -#define NV4_PGRAPH_CTX_CACHE1_USER_CLIP 13 -#define NV4_PGRAPH_CTX_CACHE1_SWIZZLE 14 -#define NV4_PGRAPH_CTX_CACHE1_PATCH_CONFIG 19:15 -#define NV4_PGRAPH_CTX_CACHE1_SPARE1 20 -#define NV4_PGRAPH_CTX_CACHE1_PATCH_STATUS 24 -#define NV4_PGRAPH_CTX_CACHE1_CONTEXT_SURFACE 25 -#define NV4_PGRAPH_CTX_CACHE2(i) (0x4001a0+(i)*4) -#define NV4_PGRAPH_CTX_CACHE2_SIZE_1 8 -#define NV4_PGRAPH_CTX_CACHE2_MONO_FORMAT 0 -#define NV4_PGRAPH_CTX_CACHE2_COLOR_FORMAT 13:8 -#define NV4_PGRAPH_CTX_CACHE2_NOTIFY_INSTANCE 16 -#define NV4_PGRAPH_CTX_CACHE3(i) (0x4001c0+(i)*4) -#define NV4_PGRAPH_CTX_CACHE3_SIZE_1 8 -#define NV4_PGRAPH_CTX_CACHE3_DMA_INSTANCE_0 0 -#define NV4_PGRAPH_CTX_CACHE3_DMA_INSTANCE_1 16 -#define NV4_PGRAPH_CTX_CACHE4(i) (0x4001e0+(i)*4) -#define NV4_PGRAPH_CTX_CACHE4_SIZE_1 8 -#define NV4_PGRAPH_CTX_CACHE4_USER_INSTANCE 0 -#define NV4_PGRAPH_CTX_CONTROL 0x400170 -#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME 0 -#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_33US 0x0 -#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_262US 0x1 -#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_2MS 0x2 -#define NV4_PGRAPH_CTX_CONTROL_MINIMUM_TIME_17MS 0x3 -#define NV4_PGRAPH_CTX_CONTROL_TIME 8 -#define NV4_PGRAPH_CTX_CONTROL_TIME_EXPIRED 0x0 -#define NV4_PGRAPH_CTX_CONTROL_TIME_NOT_EXPIRED 0x1 -#define NV4_PGRAPH_CTX_CONTROL_CHID 16 -#define NV4_PGRAPH_CTX_CONTROL_CHID_INVALID 0x0 -#define NV4_PGRAPH_CTX_CONTROL_CHID_VALID 0x1 -#define NV4_PGRAPH_CTX_CONTROL_CHANGE 20 -#define NV4_PGRAPH_CTX_CONTROL_CHANGE_UNAVAILABLE 0x0 -#define NV4_PGRAPH_CTX_CONTROL_CHANGE_AVAILABLE 0x1 -#define NV4_PGRAPH_CTX_CONTROL_SWITCHING 24 -#define NV4_PGRAPH_CTX_CONTROL_SWITCHING_IDLE 0x0 -#define NV4_PGRAPH_CTX_CONTROL_SWITCHING_BUSY 0x1 -#define NV4_PGRAPH_CTX_CONTROL_DEVICE 28 -#define NV4_PGRAPH_CTX_CONTROL_DEVICE_DISABLED 0x0 -#define NV4_PGRAPH_CTX_CONTROL_DEVICE_ENABLED 0x1 -#define NV4_PGRAPH_CTX_USER 0x400174 -#define NV4_PGRAPH_CTX_USER_SUBCH 13 -#define NV4_PGRAPH_CTX_USER_SUBCH_0 0x0 -#define NV4_PGRAPH_CTX_USER_CHID 24 -#define NV4_PGRAPH_CTX_USER_CHID_0 0x0 -#define NV4_PGRAPH_FIFO 0x400720 -#define NV4_PGRAPH_FIFO_ACCESS 0 -#define NV4_PGRAPH_FIFO_ACCESS_DISABLED 0x0 -#define NV4_PGRAPH_FIFO_ACCESS_ENABLED 0x1 -#define NV4_PGRAPH_FFINTFC_FIFO_0(i) (0x400730+(i)*4) -#define NV4_PGRAPH_FFINTFC_FIFO_0_SIZE_1 4 -#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG 0 -#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG_MTHD 0x0 -#define NV4_PGRAPH_FFINTFC_FIFO_0_TAG_CHSW 0x1 -// Note: shift left by 1 (3:1). Subch number = 0-7 -#define NV4_PGRAPH_FFINTFC_FIFO_0_SUBCH 1 -#define NV4_PGRAPH_FFINTFC_FIFO_0_MTHD 4 -#define NV4_PGRAPH_FFINTFC_FIFO_0_MTHD_CTX_SWITCH 0x0 -#define NV4_PGRAPH_FFINTFC_FIFO_1(i) (0x400740+(i)*4) -#define NV4_PGRAPH_FFINTFC_FIFO_1_SIZE_1 4 -#define NV4_PGRAPH_FFINTFC_FIFO_1_ARGUMENT 0 -#define NV4_PGRAPH_FFINTFC_FIFO_PTR 0x400750 -#define NV4_PGRAPH_FFINTFC_FIFO_PTR_WRITE 0 -#define NV4_PGRAPH_FFINTFC_FIFO_PTR_WRITE_0 0x0 -#define NV4_PGRAPH_FFINTFC_FIFO_PTR_READ 4 -#define NV4_PGRAPH_FFINTFC_FIFO_PTR_READ_0 0x0 -#define NV4_PGRAPH_FFINTFC_ST2 0x400754 -#define NV4_PGRAPH_FFINTFC_ST2_STATUS 0 -#define NV4_PGRAPH_FFINTFC_ST2_STATUS_INVALID 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_STATUS_VALID 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_MTHD 1 -#define NV4_PGRAPH_FFINTFC_ST2_MTHD_CTX_SWITCH 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH 12 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_0 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_1 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_2 0x2 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_3 0x3 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_4 0x4 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_5 0x5 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_6 0x6 -#define NV4_PGRAPH_FFINTFC_ST2_SUBCH_7 0x7 -#define NV4_PGRAPH_FFINTFC_ST2_CHID 15 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_0 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_1 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_2 0x2 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_3 0x3 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_4 0x4 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_5 0x5 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_6 0x6 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_7 0x7 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_8 0x8 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_9 0x9 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_10 0xA -#define NV4_PGRAPH_FFINTFC_ST2_CHID_11 0xB -#define NV4_PGRAPH_FFINTFC_ST2_CHID_12 0xC -#define NV4_PGRAPH_FFINTFC_ST2_CHID_13 0xD -#define NV4_PGRAPH_FFINTFC_ST2_CHID_14 0xE -#define NV4_PGRAPH_FFINTFC_ST2_CHID_15 0xF -#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS 19 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS_INVALID 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_CHID_STATUS_VALID 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT 20 -#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT_CLEAR 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_CH_SWITCH_DETECT_SET 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD 21 -#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD_CLEAR 0x0 -#define NV4_PGRAPH_FFINTFC_ST2_FIFO_HOLD_SET 0x1 -#define NV4_PGRAPH_FFINTFC_ST2_D 0x400758 -#define NV4_PGRAPH_FFINTFC_ST2_D_ARGUMENT 0 -#define NV4_PGRAPH_FFINTFC_ST2_D_ARGUMENT_0 0x0 -#define NV4_PGRAPH_STATUS 0x400700 -#define NV4_PGRAPH_STATUS_STATE 0 -#define NV4_PGRAPH_STATUS_STATE_IDLE 0x0 -#define NV4_PGRAPH_STATUS_STATE_BUSY 0x1 -#define NV4_PGRAPH_STATUS_XY_LOGIC 4 -#define NV4_PGRAPH_STATUS_XY_LOGIC_IDLE 0x0 -#define NV4_PGRAPH_STATUS_XY_LOGIC_BUSY 0x1 -#define NV4_PGRAPH_STATUS_FE 5 -#define NV4_PGRAPH_STATUS_FE_IDLE 0x0 -#define NV4_PGRAPH_STATUS_FE_BUSY 0x1 -#define NV4_PGRAPH_STATUS_RASTERIZER 6 -#define NV4_PGRAPH_STATUS_RASTERIZER_IDLE 0x0 -#define NV4_PGRAPH_STATUS_RASTERIZER_BUSY 0x1 -#define NV4_PGRAPH_STATUS_PORT_NOTIFY 8 -#define NV4_PGRAPH_STATUS_PORT_NOTIFY_IDLE 0x0 -#define NV4_PGRAPH_STATUS_PORT_NOTIFY_BUSY 0x1 -#define NV4_PGRAPH_STATUS_PORT_REGISTER 12 -#define NV4_PGRAPH_STATUS_PORT_REGISTER_IDLE 0x0 -#define NV4_PGRAPH_STATUS_PORT_REGISTER_BUSY 0x1 -#define NV4_PGRAPH_STATUS_PORT_DMA 16 -#define NV4_PGRAPH_STATUS_PORT_DMA_IDLE 0x0 -#define NV4_PGRAPH_STATUS_PORT_DMA_BUSY 0x1 -#define NV4_PGRAPH_STATUS_DMA_ENGINE 17 -#define NV4_PGRAPH_STATUS_DMA_ENGINE_IDLE 0x0 -#define NV4_PGRAPH_STATUS_DMA_ENGINE_BUSY 0x1 -#define NV4_PGRAPH_STATUS_DMA_NOTIFY 20 -#define NV4_PGRAPH_STATUS_DMA_NOTIFY_IDLE 0x0 -#define NV4_PGRAPH_STATUS_DMA_NOTIFY_BUSY 0x1 -#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY 21 -#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY_IDLE 0x0 -#define NV4_PGRAPH_STATUS_DMA_BUFFER_NOTIFY_BUSY 0x1 -#define NV4_PGRAPH_STATUS_D3D 24 -#define NV4_PGRAPH_STATUS_D3D_IDLE 0x0 -#define NV4_PGRAPH_STATUS_D3D_BUSY 0x1 -#define NV4_PGRAPH_STATUS_CACHE 25 -#define NV4_PGRAPH_STATUS_CACHE_IDLE 0x0 -#define NV4_PGRAPH_STATUS_CACHE_BUSY 0x1 -#define NV4_PGRAPH_STATUS_LIGHTING 26 -#define NV4_PGRAPH_STATUS_LIGHTING_IDLE 0x0 -#define NV4_PGRAPH_STATUS_LIGHTING_BUSY 0x1 -#define NV4_PGRAPH_STATUS_PREROP 27 -#define NV4_PGRAPH_STATUS_PREROP_IDLE 0x0 -#define NV4_PGRAPH_STATUS_PREROP_BUSY 0x1 -#define NV4_PGRAPH_STATUS_ROP 28 -#define NV4_PGRAPH_STATUS_ROP_IDLE 0x0 -#define NV4_PGRAPH_STATUS_ROP_BUSY 0x1 -#define NV4_PGRAPH_STATUS_PORT_USER 29 -#define NV4_PGRAPH_STATUS_PORT_USER_IDLE 0x0 -#define NV4_PGRAPH_STATUS_PORT_USER_BUSY 0x1 -#define NV4_PGRAPH_TRAPPED_ADDR 0x400704 -#define NV4_PGRAPH_TRAPPED_ADDR_MTHD 2 -#define NV4_PGRAPH_TRAPPED_ADDR_SUBCH 13 -#define NV4_PGRAPH_TRAPPED_ADDR_CHID 24 -#define NV4_PGRAPH_TRAPPED_DATA 0x400708 -#define NV4_PGRAPH_TRAPPED_DATA_VALUE 0 -#define NV4_PGRAPH_SURFACE 0x40070C -#define NV4_PGRAPH_SURFACE_TYPE 0 -#define NV4_PGRAPH_SURFACE_TYPE_INVALID 0x0 -#define NV4_PGRAPH_SURFACE_TYPE_NON_SWIZZLE 0x1 -#define NV4_PGRAPH_SURFACE_TYPE_SWIZZLE 0x2 -#define NV4_PGRAPH_NOTIFY 0x400714 -#define NV4_PGRAPH_NOTIFY_BUFFER_REQ 0 -#define NV4_PGRAPH_NOTIFY_BUFFER_REQ_NOT_PENDING 0x0 -#define NV4_PGRAPH_NOTIFY_BUFFER_REQ_PENDING 0x1 -#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE 8 -#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE_WRITE_ONLY 0x0 -#define NV4_PGRAPH_NOTIFY_BUFFER_STYLE_WRITE_THEN_AWAKEN 0x1 -#define NV4_PGRAPH_NOTIFY_REQ 16 -#define NV4_PGRAPH_NOTIFY_REQ_NOT_PENDING 0x0 -#define NV4_PGRAPH_NOTIFY_REQ_PENDING 0x1 -#define NV4_PGRAPH_NOTIFY_STYLE 20 -#define NV4_PGRAPH_NOTIFY_STYLE_WRITE_ONLY 0x0 -#define NV4_PGRAPH_NOTIFY_STYLE_WRITE_THEN_AWAKEN 0x1 -#define NV4_PGRAPH_BOFFSET(i) (0x400640+(i)*4) -//used for all -#define NV4_PGRAPH_BOFFSET_LINADRS_DEFAULT 0x0 -#define NV4_PGRAPH_BOFFSET_SIZE_1 6 -#define NV4_PGRAPH_BOFFSET_LINADRS 0 -#define NV4_PGRAPH_BOFFSET_LINADRS_0 0x0 -#define NV4_PGRAPH_BOFFSET0 0x400640 -#define NV4_PGRAPH_BOFFSET0_ALIAS_1 NV_PGRAPH_BOFFSET(0) -#define NV4_PGRAPH_BOFFSET0_LINADRS 0 -#define NV4_PGRAPH_BOFFSET1 0x400644 -#define NV4_PGRAPH_BOFFSET1_ALIAS_1 NV_PGRAPH_BOFFSET(1) -#define NV4_PGRAPH_BOFFSET1_LINADRS 0 -#define NV4_PGRAPH_BOFFSET2 0x400648 -#define NV4_PGRAPH_BOFFSET2_ALIAS_1 NV_PGRAPH_BOFFSET(2) -#define NV4_PGRAPH_BOFFSET2_LINADRS 0 -#define NV4_PGRAPH_BOFFSET3 0x40064C -#define NV4_PGRAPH_BOFFSET3_ALIAS_1 NV_PGRAPH_BOFFSET(3) -#define NV4_PGRAPH_BOFFSET3_LINADRS 0 -#define NV4_PGRAPH_BOFFSET4 0x400650 -#define NV4_PGRAPH_BOFFSET4_ALIAS_1 NV_PGRAPH_BOFFSET(4) -#define NV4_PGRAPH_BOFFSET4_LINADRS 0 -#define NV4_PGRAPH_BOFFSET5 0x400654 -#define NV4_PGRAPH_BOFFSET5_ALIAS_1 NV_PGRAPH_BOFFSET(5) -#define NV4_PGRAPH_BOFFSET5_LINADRS 0 -#define NV4_PGRAPH_BBASE(i) (0x400658+(i)*4) -//used for all -#define NV4_PGRAPH_BBASE_LINADRS_DEFAULT 0x0 -#define NV4_PGRAPH_BBASE_SIZE_1 6 -#define NV4_PGRAPH_BBASE_LINADRS 0 -#define NV4_PGRAPH_BBASE0 0x400658 -#define NV4_PGRAPH_BBASE0_ALIAS_1 NV_PGRAPH_BBASE(0) -#define NV4_PGRAPH_BBASE0_LINADRS 0 -#define NV4_PGRAPH_BBASE1 0x40065c -#define NV4_PGRAPH_BBASE1_ALIAS_1 NV_PGRAPH_BBASE(1) -#define NV4_PGRAPH_BBASE1_LINADRS 0 -#define NV4_PGRAPH_BBASE2 0x400660 -#define NV4_PGRAPH_BBASE2_ALIAS_1 NV_PGRAPH_BBASE(2) -#define NV4_PGRAPH_BBASE2_LINADRS 0 -#define NV4_PGRAPH_BBASE3 0x400664 -#define NV4_PGRAPH_BBASE3_ALIAS_1 NV_PGRAPH_BBASE(3) -#define NV4_PGRAPH_BBASE3_LINADRS 0 -#define NV4_PGRAPH_BBASE4 0x400668 -#define NV4_PGRAPH_BBASE4_ALIAS_1 NV_PGRAPH_BBASE(4) -#define NV4_PGRAPH_BBASE4_LINADRS 0 -#define NV4_PGRAPH_BBASE5 0x40066C -#define NV4_PGRAPH_BBASE5_ALIAS_1 NV_PGRAPH_BBASE(5) -#define NV4_PGRAPH_BBASE5_LINADRS 0 -#define NV4_PGRAPH_BPITCH(i) (0x400670+(i)*4) -//used for all -#define NV4_PGRAPH_BPITCH_LINADRS_DEFAULT 0x0 -#define NV4_PGRAPH_BPITCH_SIZE_1 5 -#define NV4_PGRAPH_BPITCH_VALUE 0 // use bit 0 for all -#define NV4_PGRAPH_BPITCH_VALUE_0 0x0 -#define NV4_PGRAPH_BPITCH0 0x400670 -#define NV4_PGRAPH_BPITCH0_ALIAS_1 NV_PGRAPH_BPITCH(0) -#define NV4_PGRAPH_BPITCH1 0x400674 -#define NV4_PGRAPH_BPITCH1_ALIAS_1 NV_PGRAPH_BPITCH(1) -#define NV4_PGRAPH_BPITCH2 0x400678 -#define NV4_PGRAPH_BPITCH2_ALIAS_1 NV_PGRAPH_BPITCH(2) -#define NV4_PGRAPH_BPITCH3 0x40067C -#define NV4_PGRAPH_BPITCH3_ALIAS_1 NV_PGRAPH_BPITCH(3) -#define NV4_PGRAPH_BPITCH4 0x400680 -#define NV4_PGRAPH_BPITCH4_ALIAS_1 NV_PGRAPH_BPITCH(4) -#define NV4_PGRAPH_BLIMIT(i) (0x400684+(i)*4) -//following two used for all -#define NV4_PGRAPH_BLIMIT_SIZE_1 6 -#define NV4_PGRAPH_BLIMIT_VALUE 0 -#define NV4_PGRAPH_BLIMIT_TYPE 31 -#define NV4_PGRAPH_BLIMIT_TYPE_IN_MEMORY 0x0 -#define NV4_PGRAPH_BLIMIT_TYPE_NULL 0x1 -#define NV4_PGRAPH_BLIMIT0 0x400684 -#define NV4_PGRAPH_BLIMIT0_ALIAS_1 NV_PGRAPH_BLIMIT(0) -#define NV4_PGRAPH_BLIMIT1 0x400688 -#define NV4_PGRAPH_BLIMIT1_ALIAS_1 NV_PGRAPH_BLIMIT(1) -#define NV4_PGRAPH_BLIMIT2 0x40068c -#define NV4_PGRAPH_BLIMIT2_ALIAS_1 NV_PGRAPH_BLIMIT(2) -#define NV4_PGRAPH_BLIMIT3 0x400690 -#define NV4_PGRAPH_BLIMIT3_ALIAS_1 NV_PGRAPH_BLIMIT(3) -#define NV4_PGRAPH_BLIMIT4 0x400694 -#define NV4_PGRAPH_BLIMIT4_ALIAS_1 NV_PGRAPH_BLIMIT(4) -#define NV4_PGRAPH_BLIMIT5 0x400698 -#define NV4_PGRAPH_BLIMIT5_ALIAS_1 NV_PGRAPH_BLIMIT(5) -#define NV4_PGRAPH_BSWIZZLE2 0x40069c -#define NV4_PGRAPH_BSWIZZLE2_WIDTH 16 -#define NV4_PGRAPH_BSWIZZLE2_WIDTH_0 0x0 -#define NV4_PGRAPH_BSWIZZLE2_HEIGHT 24 -#define NV4_PGRAPH_BSWIZZLE2_HEIGHT_0 0x0 -#define NV4_PGRAPH_BSWIZZLE5 0x4006a0 -#define NV4_PGRAPH_BSWIZZLE5_WIDTH 16 -#define NV4_PGRAPH_BSWIZZLE5_WIDTH_0 0x0 -#define NV4_PGRAPH_BSWIZZLE5_HEIGHT 24 -#define NV4_PGRAPH_BSWIZZLE5_HEIGHT_0 0x0 -#define NV4_PGRAPH_BPIXEL 0x400724 -#define NV4_PGRAPH_BPIXEL_DEPTH0 0 -#define NV4_PGRAPH_BPIXEL_DEPTH1 4 -#define NV4_PGRAPH_BPIXEL_DEPTH2 8 -#define NV4_PGRAPH_BPIXEL_DEPTH3 12 -#define NV4_PGRAPH_BPIXEL_DEPTH4 16 -#define NV4_PGRAPH_BPIXEL_DEPTH5 20 - -// valid for depth0-depth5 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_INVALID 0x0 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y8 0x1 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1R5G5B5_Z1R5G5B5 0x2 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1R5G5B5_O1R5G5B5 0x3 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_A1R5G5B5 0x4 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_R5G6B5 0x5 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y16 0x6 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_Z8R8G8B8 0x7 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_O1Z7R8G8B8 0x8 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1A7R8G8B8_Z1A7R8G8B8 0x9 -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X1A7R8G8B8_O1A7R8G8B8 0xA -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_X8R8G8B8_O8R8G8B8 0xB -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_A8R8G8B8 0xC -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_Y32 0xD -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_V8YB8U8YA8 0xE -#define NV4_PGRAPH_BPIXEL_DEPTH_FORMAT_YB8V8YA8U8 0xF - -#define NV4_PGRAPH_LIMIT_VIOL_PIX 0x400610 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_ADRS 0 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_ADRS_0 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT 29 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT_NO_VIOL 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_BLIT_VIOL 0x1 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT 30 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT_NO_VIOL 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_LIMIT_VIOL 0x1 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW 31 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW_NO_VIOL 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_PIX_OVRFLW_VIOL 0x1 -#define NV4_PGRAPH_LIMIT_VIOL_Z 0x400614 -#define NV4_PGRAPH_LIMIT_VIOL_Z_ADRS 0 -#define NV4_PGRAPH_LIMIT_VIOL_Z_ADRS_0 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT 30 -#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT_NO_VIOL 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_Z_LIMIT_VIOL 0x1 -#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW 31 -#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW_NO_VIOL 0x0 -#define NV4_PGRAPH_LIMIT_VIOL_Z_OVRFLW_VIOL 0x1 -#define NV4_PGRAPH_STATE 0x400710 -#define NV4_PGRAPH_STATE_BUFFER_0 0 -#define NV4_PGRAPH_STATE_BUFFER_0_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_0_VALID 0x1 -#define NV4_PGRAPH_STATE_BUFFER_1 1 -#define NV4_PGRAPH_STATE_BUFFER_1_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_1_VALID 0x1 -#define NV4_PGRAPH_STATE_BUFFER_2 2 -#define NV4_PGRAPH_STATE_BUFFER_2_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_2_VALID 0x1 -#define NV4_PGRAPH_STATE_BUFFER_3 3 -#define NV4_PGRAPH_STATE_BUFFER_3_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_3_VALID 0x1 -#define NV4_PGRAPH_STATE_BUFFER_4 4 -#define NV4_PGRAPH_STATE_BUFFER_4_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_4_VALID 0x1 -#define NV4_PGRAPH_STATE_BUFFER_5 5 -#define NV4_PGRAPH_STATE_BUFFER_5_INVALID 0x0 -#define NV4_PGRAPH_STATE_BUFFER_5_VALID 0x1 -#define NV4_PGRAPH_STATE_PITCH_0 8 -#define NV4_PGRAPH_STATE_PITCH_0_INVALID 0x0 -#define NV4_PGRAPH_STATE_PITCH_0_VALID 0x1 -#define NV4_PGRAPH_STATE_PITCH_1 9 -#define NV4_PGRAPH_STATE_PITCH_1_INVALID 0x0 -#define NV4_PGRAPH_STATE_PITCH_1_VALID 0x1 -#define NV4_PGRAPH_STATE_PITCH_2 10 -#define NV4_PGRAPH_STATE_PITCH_2_INVALID 0x0 -#define NV4_PGRAPH_STATE_PITCH_2_VALID 0x1 -#define NV4_PGRAPH_STATE_PITCH_3 11 -#define NV4_PGRAPH_STATE_PITCH_3_INVALID 0x0 -#define NV4_PGRAPH_STATE_PITCH_3_VALID 0x1 -#define NV4_PGRAPH_STATE_PITCH_4 12 -#define NV4_PGRAPH_STATE_PITCH_4_INVALID 0x0 -#define NV4_PGRAPH_STATE_PITCH_4_VALID 0x1 -#define NV4_PGRAPH_STATE_CHROMA_COLOR 16 -#define NV4_PGRAPH_STATE_CHROMA_COLOR_INVALID 0x0 -#define NV4_PGRAPH_STATE_CHROMA_COLOR_VALID 0x1 -#define NV4_PGRAPH_STATE_CHROMA_COLORFMT 17 -#define NV4_PGRAPH_STATE_CHROMA_COLORFMT_INVALID 0x0 -#define NV4_PGRAPH_STATE_CHROMA_COLORFMT_VALID 0x1 -#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT 20 -#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT_INVALID 0x0 -#define NV4_PGRAPH_STATE_CPATTERN_COLORFMT_VALID 0x1 -#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT 21 -#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT_INVALID 0x0 -#define NV4_PGRAPH_STATE_CPATTERN_MONOFMT_VALID 0x1 -#define NV4_PGRAPH_STATE_CPATTERN_SELECT 22 -#define NV4_PGRAPH_STATE_CPATTERN_SELECT_INVALID 0x0 -#define NV4_PGRAPH_STATE_CPATTERN_SELECT_VALID 0x1 -#define NV4_PGRAPH_STATE_PATTERN_COLOR0 24 -#define NV4_PGRAPH_STATE_PATTERN_COLOR0_INVALID 0x0 -#define NV4_PGRAPH_STATE_PATTERN_COLOR0_VALID 0x1 -#define NV4_PGRAPH_STATE_PATTERN_COLOR1 25 -#define NV4_PGRAPH_STATE_PATTERN_COLOR1_INVALID 0x0 -#define NV4_PGRAPH_STATE_PATTERN_COLOR1_VALID 0x1 -#define NV4_PGRAPH_STATE_PATTERN_PATT0 26 -#define NV4_PGRAPH_STATE_PATTERN_PATT0_INVALID 0x0 -#define NV4_PGRAPH_STATE_PATTERN_PATT0_VALID 0x1 -#define NV4_PGRAPH_STATE_PATTERN_PATT1 27 -#define NV4_PGRAPH_STATE_PATTERN_PATT1_INVALID 0x0 -#define NV4_PGRAPH_STATE_PATTERN_PATT1_VALID 0x1 -#define NV4_PGRAPH_CACHE_INDEX 0x400728 -#define NV4_PGRAPH_CACHE_INDEX_BANK 2 -#define NV4_PGRAPH_CACHE_INDEX_BANK_10 0x0 -#define NV4_PGRAPH_CACHE_INDEX_BANK_32 0x1 -#define NV4_PGRAPH_CACHE_INDEX_ADRS 3 -#define NV4_PGRAPH_CACHE_INDEX_ADRS_0 0x0 -#define NV4_PGRAPH_CACHE_INDEX_ADRS_1024 0x400 -#define NV4_PGRAPH_CACHE_INDEX_OP 13 -#define NV4_PGRAPH_CACHE_INDEX_OP_WR_CACHE 0x0 -#define NV4_PGRAPH_CACHE_INDEX_OP_RD_CACHE 0x1 -#define NV4_PGRAPH_CACHE_INDEX_OP_RD_INDEX 0x2 -#define NV4_PGRAPH_CACHE_RAM 0x40072c -#define NV4_PGRAPH_CACHE_RAM_VALUE 0 -#define NV4_PGRAPH_DMA_PITCH 0x400760 -#define NV4_PGRAPH_DMA_PITCH_S0 0 -#define NV4_PGRAPH_DMA_PITCH_S1 16 -#define NV4_PGRAPH_DVD_COLORFMT 0x400764 -#define NV4_PGRAPH_DVD_COLORFMT_IMAGE 0 -#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_INVALID 0x00 -#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_LE_V8YB8U8YA8 0x12 -#define NV4_PGRAPH_DVD_COLORFMT_IMAGE_FORMAT_LE_YB8V8YA8U8 0x13 -#define NV4_PGRAPH_DVD_COLORFMT_OVLY 8 -#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_INVALID 0x00 -#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_LE_A8Y8U8V8 0x01 -#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_LE_A4V6YB6A4U6YA6 0x02 -#define NV4_PGRAPH_DVD_COLORFMT_OVLY_FORMAT_TRANSPARENT 0x03 -#define NV4_PGRAPH_SCALED_FORMAT 0x400768 -#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN 16 -#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_INVALID 0x0 -#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_CENTER 0x1 -#define NV4_PGRAPH_SCALED_FORMAT_ORIGIN_CORNER 0x2 -#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR 24 -#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR_ZOH 0x0 -#define NV4_PGRAPH_SCALED_FORMAT_INTERPOLATOR_FOH 0x1 -#define NV4_PGRAPH_PATT_COLOR0 0x400800 -#define NV4_PGRAPH_PATT_COLOR0_VALUE 0 -#define NV4_PGRAPH_PATT_COLOR1 0x400804 -#define NV4_PGRAPH_PATT_COLOR1_VALUE 0 -#define NV4_PGRAPH_PATT_COLORRAM(i) (0x400900+(i)*4) -#define NV4_PGRAPH_PATT_COLORRAM_SIZE_1 64 -#define NV4_PGRAPH_PATT_COLORRAM_VALUE 0 -#define NV4_PGRAPH_PATTERN(i) (0x400808+(i)*4) -#define NV4_PGRAPH_PATTERN_SIZE_1 2 -#define NV4_PGRAPH_PATTERN_BITMAP 0 -#define NV4_PGRAPH_PATTERN_SHAPE 0x400810 -#define NV4_PGRAPH_PATTERN_SHAPE_VALUE 0 -#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_8X_8Y 0x0 -#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_64X_1Y 0x1 -#define NV4_PGRAPH_PATTERN_SHAPE_VALUE_1X_64Y 0x2 -#define NV4_PGRAPH_PATTERN_SHAPE_SELECT 4 -#define NV4_PGRAPH_PATTERN_SHAPE_SELECT_2COLOR 0x0 -#define NV4_PGRAPH_PATTERN_SHAPE_SELECT_FULLCOLOR 0x1 -#define NV4_PGRAPH_MONO_COLOR0 0x400600 -#define NV4_PGRAPH_MONO_COLOR0_VALUE 0 -#define NV4_PGRAPH_ROP3 0x400604 -#define NV4_PGRAPH_ROP3_VALUE 0 -#define NV4_PGRAPH_CHROMA 0x400814 -#define NV4_PGRAPH_CHROMA_VALUE 0 -#define NV4_PGRAPH_BETA_AND 0x400608 -#define NV4_PGRAPH_BETA_AND_VALUE_FRACTION 23 -#define NV4_PGRAPH_BETA_PREMULT 0x40060c -#define NV4_PGRAPH_BETA_PREMULT_VALUE 0 -#define NV4_PGRAPH_CONTROL0 0x400818 -#define NV4_PGRAPH_CONTROL0_ALPHAREF 0 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC 8 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_NEVER 0x1 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_LESS 0x2 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_EQUAL 0x3 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_LESSEQUAL 0x4 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_GREATER 0x5 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_NOTEQUAL 0x6 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_GREATEREQUAL 0x7 -#define NV4_PGRAPH_CONTROL0_ALPHAFUNC_ALWAYS 0x8 -#define NV4_PGRAPH_CONTROL0_ALPHATESTENABLE 12 -#define NV4_PGRAPH_CONTROL0_ALPHATESTENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_ZENABLE 14 -#define NV4_PGRAPH_CONTROL0_ZENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_ZENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_ZFUNC 16 -#define NV4_PGRAPH_CONTROL0_ZFUNC_NEVER 0x1 -#define NV4_PGRAPH_CONTROL0_ZFUNC_LESS 0x2 -#define NV4_PGRAPH_CONTROL0_ZFUNC_EQUAL 0x3 -#define NV4_PGRAPH_CONTROL0_ZFUNC_LESSEQUAL 0x4 -#define NV4_PGRAPH_CONTROL0_ZFUNC_GREATER 0x5 -#define NV4_PGRAPH_CONTROL0_ZFUNC_NOTEQUAL 0x6 -#define NV4_PGRAPH_CONTROL0_ZFUNC_GREATEREQUAL 0x7 -#define NV4_PGRAPH_CONTROL0_ZFUNC_ALWAYS 0x8 -#define NV4_PGRAPH_CONTROL0_CULLMODE 20 -#define NV4_PGRAPH_CONTROL0_CULLMODE_NONE 0x1 -#define NV4_PGRAPH_CONTROL0_CULLMODE_CW 0x2 -#define NV4_PGRAPH_CONTROL0_CULLMODE_CCW 0x3 -#define NV4_PGRAPH_CONTROL0_DITHERENABLE 22 -#define NV4_PGRAPH_CONTROL0_DITHERENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_DITHERENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE 23 -#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_Z_PERSPECTIVE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE 24 -#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_ZWRITEENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE 25 -#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_STENCIL_WRITE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE 26 -#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_ALPHA_WRITE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE 27 -#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_RED_WRITE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE 28 -#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_GREEN_WRITE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE 29 -#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL0_BLUE_WRITE_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL0_Z_FORMAT 30 -#define NV4_PGRAPH_CONTROL0_Z_FORMAT_FIXED 0x1 -#define NV4_PGRAPH_CONTROL0_Z_FORMAT_FLOAT 0x2 -#define NV4_PGRAPH_CONTROL1 0x40081c -#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE 0 -#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_CONTROL1_STENCIL_TEST_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC 4 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_NEVER 0x1 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_LESS 0x2 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_EQUAL 0x3 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_LESSEQUAL 0x4 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_GREATER 0x5 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_NOTEQUAL 0x6 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_GREATEREQUAL 0x7 -#define NV4_PGRAPH_CONTROL1_STENCIL_FUNC_ALWAYS 0x8 -#define NV4_PGRAPH_CONTROL1_STENCIL_REF 8 -#define NV4_PGRAPH_CONTROL1_STENCIL_MASK_READ 16 -#define NV4_PGRAPH_CONTROL1_STENCIL_MASK_WRITE 24 -#define NV4_PGRAPH_CONTROL2 0x400820 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL 0 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_KEEP 0x1 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_ZERO 0x2 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_REPLACE 0x3 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INCRSAT 0x4 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_DECRSAT 0x5 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INVERT 0x6 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_INCR 0x7 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_FAIL_DECR 0x8 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL 4 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_KEEP 0x1 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_ZERO 0x2 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_REPLACE 0x3 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INCRSAT 0x4 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_DECRSAT 0x5 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INVERT 0x6 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_INCR 0x7 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZFAIL_DECR 0x8 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS 8 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_KEEP 0x1 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_ZERO 0x2 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_REPLACE 0x3 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INCRSAT 0x4 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_DECRSAT 0x5 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INVERT 0x6 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_INCR 0x7 -#define NV4_PGRAPH_CONTROL2_STENCIL_OP_ZPASS_DECR 0x8 -#define NV4_PGRAPH_BLEND 0x400824 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND 0 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECAL 0x1 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATE 0x2 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECALALPHA 0x3 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATEALPHA 0x4 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_DECALMASK 0x5 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_MODULATEMASK 0x6 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_COPY 0x7 -#define NV4_PGRAPH_BLEND_TEXTUREMAPBLEND_ADD 0x8 -#define NV4_PGRAPH_BLEND_MASK_BIT 4 -#define NV4_PGRAPH_BLEND_MASK_BIT_LSB 0x1 -#define NV4_PGRAPH_BLEND_MASK_BIT_MSB 0x2 -#define NV4_PGRAPH_BLEND_SHADEMODE 6 -#define NV4_PGRAPH_BLEND_SHADEMODE_FLAT 0x1 -#define NV4_PGRAPH_BLEND_SHADEMODE_GOURAUD 0x2 -#define NV4_PGRAPH_BLEND_SHADEMODE_PHONG 0x3 -#define NV4_PGRAPH_BLEND_TEXTUREPERSPECTIVE 8 -#define NV4_PGRAPH_BLEND_TEXTUREPERSPECTIVE_TRUE 0x1 -#define NV4_PGRAPH_BLEND_SPECULARENABLE 12 -#define NV4_PGRAPH_BLEND_SPECULARENABLE_TRUE 0x1 -#define NV4_PGRAPH_BLEND_FOGENABLE 16 -#define NV4_PGRAPH_BLEND_FOGENABLE_TRUE 0x1 -#define NV4_PGRAPH_BLEND_ALPHABLENDENABLE 20 -#define NV4_PGRAPH_BLEND_ALPHABLENDENABLE_TRUE 0x1 -#define NV4_PGRAPH_BLEND_SRCBLEND 24 -#define NV4_PGRAPH_BLEND_SRCBLEND_ZERO 0x1 -#define NV4_PGRAPH_BLEND_SRCBLEND_ONE 0x2 -#define NV4_PGRAPH_BLEND_SRCBLEND_SRCCOLOR 0x3 -#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCCOLOR 0x4 -#define NV4_PGRAPH_BLEND_SRCBLEND_SRCALPHA 0x5 -#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCALPHA 0x6 -#define NV4_PGRAPH_BLEND_SRCBLEND_DESTALPHA 0x7 -#define NV4_PGRAPH_BLEND_SRCBLEND_INVDESTALPHA 0x8 -#define NV4_PGRAPH_BLEND_SRCBLEND_DESTCOLOR 0x9 -#define NV4_PGRAPH_BLEND_SRCBLEND_INVDESTCOLOR 0xA -#define NV4_PGRAPH_BLEND_SRCBLEND_SRCALPHASAT 0xB -#define NV4_PGRAPH_BLEND_SRCBLEND_INVSRCALPHASAT 0xC -#define NV4_PGRAPH_BLEND_SRCBLEND_BETA 0xD -#define NV4_PGRAPH_BLEND_DESTBLEND 28 -#define NV4_PGRAPH_BLEND_DESTBLEND_ZERO 0x1 -#define NV4_PGRAPH_BLEND_DESTBLEND_ONE 0x2 -#define NV4_PGRAPH_BLEND_DESTBLEND_SRCCOLOR 0x3 -#define NV4_PGRAPH_BLEND_DESTBLEND_INVSRCCOLOR 0x4 -#define NV4_PGRAPH_BLEND_DESTBLEND_SRCALPHA 0x5 -#define NV4_PGRAPH_BLEND_DESTBLEND_INVSRCALPHA 0x6 -#define NV4_PGRAPH_BLEND_DESTBLEND_DESTALPHA 0x7 -#define NV4_PGRAPH_BLEND_DESTBLEND_INVDESTALPHA 0x8 -#define NV4_PGRAPH_BLEND_DESTBLEND_DESTCOLOR 0x9 -#define NV4_PGRAPH_BLEND_DESTBLEND_INVDESTCOLOR 0xA -#define NV4_PGRAPH_BLEND_DESTBLEND_SRCALPHASAT 0xB -#define NV4_PGRAPH_DPRAM_INDEX 0x400828 -#define NV4_PGRAPH_DPRAM_INDEX_ADRS 0 -#define NV4_PGRAPH_DPRAM_INDEX_ADRS_0 0x0 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT 8 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ADRS_0 0x0 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ADRS_1 0x1 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_DATA_0 0x2 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_DATA_1 0x3 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_WE_0 0x4 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_WE_1 0x5 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ALPHA_0 0x6 -#define NV4_PGRAPH_DPRAM_INDEX_SELECT_ALPHA_1 0x7 -#define NV4_PGRAPH_DPRAM_DATA 0x40082c -#define NV4_PGRAPH_DPRAM_DATA_VALUE 0 -#define NV4_PGRAPH_DPRAM_ADRS_0 0x40082c -#define NV4_PGRAPH_DPRAM_ADRS_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_ADRS_0_VALUE 0 -#define NV4_PGRAPH_DPRAM_ADRS_1 0x40082c -#define NV4_PGRAPH_DPRAM_ADRS_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_ADRS_1_VALUE 0 -#define NV4_PGRAPH_DPRAM_DATA_0 0x40082c -#define NV4_PGRAPH_DPRAM_DATA_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_DATA_0_VALUE 0 -#define NV4_PGRAPH_DPRAM_DATA_1 0x40082c -#define NV4_PGRAPH_DPRAM_DATA_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_DATA_1_VALUE 0 -#define NV4_PGRAPH_DPRAM_WE_0 0x40082c -#define NV4_PGRAPH_DPRAM_WE_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_WE_0_VALUE 0 -#define NV4_PGRAPH_DPRAM_WE_1 0x40082c -#define NV4_PGRAPH_DPRAM_WE_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_WE_1_VALUE 0 -#define NV4_PGRAPH_DPRAM_ALPHA_0 0x40082c -#define NV4_PGRAPH_DPRAM_ALPHA_0_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_ALPHA_0_VALUE 0 -#define NV4_PGRAPH_DPRAM_ALPHA_1 0x40082c -#define NV4_PGRAPH_DPRAM_ALPHA_1_ALIAS_1 NV_PGRAPH_DPRAM_DATA -#define NV4_PGRAPH_DPRAM_ALPHA_1_VALUE 0 -#define NV4_PGRAPH_STORED_FMT 0x400830 -#define NV4_PGRAPH_STORED_FMT_MONO0 0 -#define NV4_PGRAPH_STORED_FMT_PATT0 8 -#define NV4_PGRAPH_STORED_FMT_PATT1 16 -#define NV4_PGRAPH_STORED_FMT_CHROMA 24 -#define NV4_PGRAPH_FORMATS 0x400618 -#define NV4_PGRAPH_FORMATS_ROP 0 -#define NV4_PGRAPH_FORMATS_ROP_Y8 0x0 -#define NV4_PGRAPH_FORMATS_ROP_RGB15 0x1 -#define NV4_PGRAPH_FORMATS_ROP_RGB16 0x2 -#define NV4_PGRAPH_FORMATS_ROP_Y16 0x3 -#define NV4_PGRAPH_FORMATS_ROP_INVALID 0x4 -#define NV4_PGRAPH_FORMATS_ROP_RGB24 0x5 -#define NV4_PGRAPH_FORMATS_ROP_RGB30 0x6 -#define NV4_PGRAPH_FORMATS_ROP_Y32 0x7 -#define NV4_PGRAPH_FORMATS_SRC 4 -#define NV4_PGRAPH_FORMATS_SRC_INVALID 0x0 -#define NV4_PGRAPH_FORMATS_SRC_LE_Y8 0x1 -#define NV4_PGRAPH_FORMATS_SRC_LE_X16A8Y8 0x2 -#define NV4_PGRAPH_FORMATS_SRC_LE_X24Y8 0x3 -#define NV4_PGRAPH_FORMATS_SRC_LE_A1R5G5B5 0x6 -#define NV4_PGRAPH_FORMATS_SRC_LE_X1R5G5B5 0x7 -#define NV4_PGRAPH_FORMATS_SRC_LE_X16A1R5G5B5 0x8 -#define NV4_PGRAPH_FORMATS_SRC_LE_X17R5G5B5 0x9 -#define NV4_PGRAPH_FORMATS_SRC_LE_R5G6B5 0xA -#define NV4_PGRAPH_FORMATS_SRC_LE_A16R5G6B5 0xB -#define NV4_PGRAPH_FORMATS_SRC_LE_X16R5G6B5 0xC -#define NV4_PGRAPH_FORMATS_SRC_LE_A8R8G8B8 0xD -#define NV4_PGRAPH_FORMATS_SRC_LE_X8R8G8B8 0xE -#define NV4_PGRAPH_FORMATS_SRC_LE_Y16 0xF -#define NV4_PGRAPH_FORMATS_SRC_LE_A16Y16 0x10 -#define NV4_PGRAPH_FORMATS_SRC_LE_X16Y16 0x11 -#define NV4_PGRAPH_FORMATS_SRC_LE_V8YB8U8YA8 0x12 -#define NV4_PGRAPH_FORMATS_SRC_LE_YB8V8YA8U8 0x13 -#define NV4_PGRAPH_FORMATS_SRC_LE_Y32 0x14 -#define NV4_PGRAPH_FORMATS_FB 12 -#define NV4_PGRAPH_FORMATS_FB_INVALID 0x0 -#define NV4_PGRAPH_FORMATS_FB_Y8 0x1 -#define NV4_PGRAPH_FORMATS_FB_X1R5G5B5_Z1R5G5B5 0x2 -#define NV4_PGRAPH_FORMATS_FB_X1R5G5B5_O1R5G5B5 0x3 -#define NV4_PGRAPH_FORMATS_FB_A1R5G5B5 0x4 -#define NV4_PGRAPH_FORMATS_FB_R5G6B5 0x5 -#define NV4_PGRAPH_FORMATS_FB_Y16 0x6 -#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_Z8R8G8B8 0x7 -#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_O1Z7R8G8B8 0x8 -#define NV4_PGRAPH_FORMATS_FB_X1A7R8G8B8_Z1A7R8G8B8 0x9 -#define NV4_PGRAPH_FORMATS_FB_X1A7R8G8B8_O1A7R8G8B8 0xA -#define NV4_PGRAPH_FORMATS_FB_X8R8G8B8_O8R8G8B8 0xB -#define NV4_PGRAPH_FORMATS_FB_A8R8G8B8 0xC -#define NV4_PGRAPH_FORMATS_FB_Y32 0xD -#define NV4_PGRAPH_FORMATS_FB_V8YB8U8YA8 0xE -#define NV4_PGRAPH_FORMATS_FB_YB8V8YA8U8 0xF -#define NV4_PGRAPH_ABS_X_RAM(i) (0x400400+(i)*4) -#define NV4_PGRAPH_ABS_X_RAM_SIZE_1 32 -#define NV4_PGRAPH_ABS_X_RAM_VALUE 0 -#define NV4_PGRAPH_X_RAM_BPORT(i) (0x400c00+(i)*4) -#define NV4_PGRAPH_X_RAM_BPORT_SIZE_1 32 -#define NV4_PGRAPH_X_RAM_BPORT_VALUE 0 -#define NV4_PGRAPH_ABS_Y_RAM(i) (0x400480+(i)*4) -#define NV4_PGRAPH_ABS_Y_RAM_SIZE_1 32 -#define NV4_PGRAPH_ABS_Y_RAM_VALUE 0 -#define NV4_PGRAPH_Y_RAM_BPORT(i) (0x400c80+(i)*4) -#define NV4_PGRAPH_Y_RAM_BPORT_SIZE_1 32 -#define NV4_PGRAPH_Y_RAM_BPORT_VALUE 0 -#define NV4_PGRAPH_XY_LOGIC_MISC0 0x400514 -#define NV4_PGRAPH_XY_LOGIC_MISC0_COUNTER 0 -#define NV4_PGRAPH_XY_LOGIC_MISC0_COUNTER_0 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION 20 -#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION_NONZERO 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC0_DIMENSION_ZERO 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC0_INDEX 28 -#define NV4_PGRAPH_XY_LOGIC_MISC0_INDEX_0 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1 0x400518 -#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL 0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL_NEEDED 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_INITIAL_DONE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX 4 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX_NOTNULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPX_NULL 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY 5 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY_NOTNULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_XTRACLIPY_NULL 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX 12 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX_UUMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XIMAX_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX 16 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX_UUMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_YIMAX_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA 20 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA_CLIPMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC1_SEL_XXTRA_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2 0x40051C -#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF 0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF_DISABLE 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_HANDOFF_ENABLE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX 4 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX_NOTNULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPX_NULL 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY 5 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY_NOTNULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_XTRACLIPY_NULL 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX 12 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX_UCMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XIMAX_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX 16 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX_UCMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_YIMAX_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA 20 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA_CLIPMAX 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC2_SEL_XXTRA_IMAGEMAX 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC3 0x400520 -#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0 0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0_NULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_WDIMY_EQ_0_TRUE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY 4 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY_NULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WDIMY_TRUE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX 8 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX_NULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_RELOAD_WX_TRUE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG 12 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG_NULL 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_ALG_TRUE 0x1 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_DIMX 16 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_DIMX_0 0x0 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_WDIMX 24 -#define NV4_PGRAPH_XY_LOGIC_MISC3_TEXT_WDIMX_0 0x0 -#define NV4_PGRAPH_X_MISC 0x400500 -#define NV4_PGRAPH_X_MISC_BIT33_0 0 -#define NV4_PGRAPH_X_MISC_BIT33_0_0 0x0 -#define NV4_PGRAPH_X_MISC_BIT33_1 1 -#define NV4_PGRAPH_X_MISC_BIT33_1_0 0x0 -#define NV4_PGRAPH_X_MISC_BIT33_2 2 -#define NV4_PGRAPH_X_MISC_BIT33_2_0 0x0 -#define NV4_PGRAPH_X_MISC_BIT33_3 3 -#define NV4_PGRAPH_X_MISC_BIT33_3_0 0x0 -#define NV4_PGRAPH_X_MISC_RANGE_0 4 -#define NV4_PGRAPH_X_MISC_RANGE_0_0 0x0 -#define NV4_PGRAPH_X_MISC_RANGE_1 5 -#define NV4_PGRAPH_X_MISC_RANGE_1_0 0x0 -#define NV4_PGRAPH_X_MISC_RANGE_2 6 -#define NV4_PGRAPH_X_MISC_RANGE_2_0 0x0 -#define NV4_PGRAPH_X_MISC_RANGE_3 7 -#define NV4_PGRAPH_X_MISC_RANGE_3_0 0x0 -#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT 28 -#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_EQ_0 0x0 -#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_LT_0 0x1 -#define NV4_PGRAPH_X_MISC_ADDER_OUTPUT_GT_0 0x2 -#define NV4_PGRAPH_Y_MISC 0x400504 -#define NV4_PGRAPH_Y_MISC_BIT33_0 0 -#define NV4_PGRAPH_Y_MISC_BIT33_0_0 0x0 -#define NV4_PGRAPH_Y_MISC_BIT33_1 1 -#define NV4_PGRAPH_Y_MISC_BIT33_1_0 0x0 -#define NV4_PGRAPH_Y_MISC_BIT33_2 2 -#define NV4_PGRAPH_Y_MISC_BIT33_2_0 0x0 -#define NV4_PGRAPH_Y_MISC_BIT33_3 3 -#define NV4_PGRAPH_Y_MISC_BIT33_3_0 0x0 -#define NV4_PGRAPH_Y_MISC_RANGE_0 4 -#define NV4_PGRAPH_Y_MISC_RANGE_0_0 0x0 -#define NV4_PGRAPH_Y_MISC_RANGE_1 5 -#define NV4_PGRAPH_Y_MISC_RANGE_1_0 0x0 -#define NV4_PGRAPH_Y_MISC_RANGE_2 6 -#define NV4_PGRAPH_Y_MISC_RANGE_2_0 0x0 -#define NV4_PGRAPH_Y_MISC_RANGE_3 7 -#define NV4_PGRAPH_Y_MISC_RANGE_3_0 0x0 -#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT 28 -#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_EQ_0 0x0 -#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_LT_0 0x1 -#define NV4_PGRAPH_Y_MISC_ADDER_OUTPUT_GT_0 0x2 -#define NV4_PGRAPH_ABS_UCLIP_XMIN 0x40053C -#define NV4_PGRAPH_ABS_UCLIP_XMIN_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIP_XMAX 0x400544 -#define NV4_PGRAPH_ABS_UCLIP_XMAX_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIP_YMIN 0x400540 -#define NV4_PGRAPH_ABS_UCLIP_YMIN_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIP_YMAX 0x400548 -#define NV4_PGRAPH_ABS_UCLIP_YMAX_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIPA_XMIN 0x400560 -#define NV4_PGRAPH_ABS_UCLIPA_XMIN_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIPA_XMAX 0x400568 -#define NV4_PGRAPH_ABS_UCLIPA_XMAX_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIPA_YMIN 0x400564 -#define NV4_PGRAPH_ABS_UCLIPA_YMIN_VALUE 0 -#define NV4_PGRAPH_ABS_UCLIPA_YMAX 0x40056C -#define NV4_PGRAPH_ABS_UCLIPA_YMAX_VALUE 0 -#define NV4_PGRAPH_SOURCE_COLOR 0x40050C -#define NV4_PGRAPH_SOURCE_COLOR_VALUE 0 -#define NV4_PGRAPH_SOURCE_COLOR_VALUE_0 0x0 -#define NV4_PGRAPH_VALID1 0x400508 -#define NV4_PGRAPH_VALID1_VLD 0 -#define NV4_PGRAPH_VALID1_VLD_0 0x0 -#define NV4_PGRAPH_VALID1_VLD_NOCLIP (0x1<<19) -#define NV4_PGRAPH_VALID1_VLD_SRCCOLOR (0x1<<16) -#define NV4_PGRAPH_VALID1_VLD_GOTMOVE (0x1<<21) -#define NV4_PGRAPH_VALID1_VLD_GOTX01 (0x3<<0) -#define NV4_PGRAPH_VALID1_VLD_GOTX02 (0x7<<0) -#define NV4_PGRAPH_VALID1_VLD_GOTX03 (0xF<<0) -#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN01 (0x3<<4) -#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN02 (0x7<<4) -#define NV4_PGRAPH_VALID1_VLD_GOTXCHAIN03 (0xF<<4) -#define NV4_PGRAPH_VALID1_VLD_GOTY01 (0x3<<8) -#define NV4_PGRAPH_VALID1_VLD_GOTY02 (0x7<<8) -#define NV4_PGRAPH_VALID1_VLD_GOTY03 (0xF<<8) -#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN01 (0x3<<12) -#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN02 (0x7<<12) -#define NV4_PGRAPH_VALID1_VLD_GOTYCHAIN03 (0xF<<12) -#define NV4_PGRAPH_VALID1_VLD_X_OFFSET (0x1<<0) -#define NV4_PGRAPH_VALID1_VLD_XCHAIN_OFFSET (0x1<<4) -#define NV4_PGRAPH_VALID1_VLD_Y_OFFSET (0x1<<8) -#define NV4_PGRAPH_VALID1_VLD_YCHAIN_OFFSET (0x1<<12) -#define NV4_PGRAPH_VALID1_VLD_GOTCOLOR0 (0x1<<17) -#define NV4_PGRAPH_VALID1_VLD_GOTCOLOR1 (0x1<<18) -#define NV4_PGRAPH_VALID1_VLD_GOTCLIP (0x1<<20) -#define NV4_PGRAPH_VALID1_VLD_GOTFONT (0x1<<22) -#define NV4_PGRAPH_VALID1_VLD_GOTOFFSET (0x1<<22) -#define NV4_PGRAPH_VALID1_VLD_GOTBPITCH (0x1<<2) -#define NV4_PGRAPH_VALID1_VLD_GOTBOFFSET (0x1<<3) -#define NV4_PGRAPH_VALID1_VLD_GOTDUDX (0x1<<4) -#define NV4_PGRAPH_VALID1_VLD_GOTDVDY (0x1<<5) -#define NV4_PGRAPH_VALID1_VLD_GOTPOINT (0x1<<8) -#define NV4_PGRAPH_VALID1_VLD_GOTSIZE (0x1<<9) -#define NV4_PGRAPH_VALID1_VLD_GOTPITCH (0x1<<10) -#define NV4_PGRAPH_VALID1_VLD_GOTSTART (0x1<<11) -#define NV4_PGRAPH_VALID1_VLD_GOTDUDX2 (0x1<<12) -#define NV4_PGRAPH_VALID1_VLD_GOTDVDY2 (0x1<<13) -#define NV4_PGRAPH_VALID1_VLD_GOTPOINT2 (0x1<<14) -#define NV4_PGRAPH_VALID1_VLD_GOTSIZE2 (0x1<<15) -#define NV4_PGRAPH_VALID1_VLD_GOTPITCH2 (0x1<<16) -#define NV4_PGRAPH_VALID1_VLD_GOTSTART2 (0x1<<17) -#define NV4_PGRAPH_VALID1_VLD_GOTOFFSIN (0x1<<0) -#define NV4_PGRAPH_VALID1_VLD_GOTOFFSOUT (0x1<<1) -#define NV4_PGRAPH_VALID1_VLD_GOTPITCHIN (0x1<<2) -#define NV4_PGRAPH_VALID1_VLD_GOTPITCHOUT (0x1<<3) -#define NV4_PGRAPH_VALID1_VLD_GOTLENGTH (0x1<<4) -#define NV4_PGRAPH_VALID1_VLD_GOTCOUNT (0x1<<5) -#define NV4_PGRAPH_VALID1_VLD_GOTFORMAT (0x1<<6) -#define NV4_PGRAPH_VALID1_VLD_GOTNOTIFY (0x1<<7) -#define NV4_PGRAPH_VALID1_CLIP_MIN 28 -#define NV4_PGRAPH_VALID1_CLIP_MIN_NO_ERROR 0x0 -#define NV4_PGRAPH_VALID1_CLIP_MIN_ONLY 0x1 -#define NV4_PGRAPH_VALID1_CLIPA_MIN 29 -#define NV4_PGRAPH_VALID1_CLIPA_MIN_NO_ERROR 0x0 -#define NV4_PGRAPH_VALID1_CLIPA_MIN_ONLY 0x1 -#define NV4_PGRAPH_VALID1_CLIP_MAX 30 -#define NV4_PGRAPH_VALID1_CLIP_MAX_NO_ERROR 0x0 -#define NV4_PGRAPH_VALID1_CLIP_MAX_ONLY 0x1 -#define NV4_PGRAPH_VALID1_CLIPA_MAX 31 -#define NV4_PGRAPH_VALID1_CLIPA_MAX_NO_ERROR 0x0 -#define NV4_PGRAPH_VALID1_CLIPA_MAX_ONLY 0x1 -#define NV4_PGRAPH_VALID2 0x400578 -#define NV4_PGRAPH_VALID2_VLD2 0 -#define NV4_PGRAPH_VALID2_VLD2_0 0x0 -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE0A (1<<28) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE0C (1<<27) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE1A (1<<26) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBINE1C (1<<25) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COMBFACTOR (1<<24) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FILTER1 (1<<23) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_OFFSET1 (1<<22) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FORMAT1 (1<<21) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_BLEND (1<<20) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL2 (1<<19) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL1 (1<<18) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_CONTROL0 (1<<17) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FILTER0 (1<<16) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FORMAT0 (1<<15) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_OFFSET0 (1<<14) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_FOGCOLOR (1<<13) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COLORKEY (1<<12) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_V1 (1<<9) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_U1 (1<<8) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_V0 (1<<7) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_U0 (1<<6) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_X (1<<5) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_Y (1<<4) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_ZETA (1<<3) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_M (1<<2) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_COLOR (1<<1) -#define NV4_PGRAPH_VALID2_VLD2_GOT3D_SPECULAR (1<<0) -#define NV4_PGRAPH_VALID2_VLD2_DX3FULLVERTEX (0x7f<<0) -#define NV4_PGRAPH_VALID2_VLD2_DX5FULLVERTEX (0x7f<<0) -#define NV4_PGRAPH_VALID2_VLD2_DX6FULLVERTEX (0x1ff<<0) -#define NV4_PGRAPH_VALID2_VLD2_DX3FULLSTATE (0x3f<<13) -#define NV4_PGRAPH_VALID2_VLD2_DX5FULLSTATE (0x1ff<<12) -#define NV4_PGRAPH_VALID2_VLD2_DX6FULLSTATE (0xFfff<<13) -#define NV4_PGRAPH_ABS_ICLIP_XMAX 0x400534 -#define NV4_PGRAPH_ABS_ICLIP_XMAX_VALUE 0 -#define NV4_PGRAPH_ABS_ICLIP_YMAX 0x400538 -#define NV4_PGRAPH_ABS_ICLIP_YMAX_VALUE 0 -#define NV4_PGRAPH_CLIPX_0 0x400524 -// Valid for all clips! -#define NV4_PGRAPH_CLIP_CONDITION_IS_GT 0x0 -#define NV4_PGRAPH_CLIP_CONDITION_IS_LT 0x1 -#define NV4_PGRAPH_CLIP_CONDITION_IS_EQ 0x2 -#define NV4_PGRAPH_CLIPX_0_CLIP0_MIN 0 -#define NV4_PGRAPH_CLIPX_0_CLIP0_MAX 2 -#define NV4_PGRAPH_CLIPX_0_CLIP1_MIN 4 -#define NV4_PGRAPH_CLIPX_0_CLIP1_MAX 6 -#define NV4_PGRAPH_CLIPX_0_CLIP2_MIN 8 -#define NV4_PGRAPH_CLIPX_0_CLIP2_MAX 10 -#define NV4_PGRAPH_CLIPX_0_CLIP3_MIN 12 -#define NV4_PGRAPH_CLIPX_0_CLIP3_MAX 14 -#define NV4_PGRAPH_CLIPX_0_CLIP4_MIN 16 -#define NV4_PGRAPH_CLIPX_0_CLIP4_MAX 18 -#define NV4_PGRAPH_CLIPX_0_CLIP5_MIN 20 -#define NV4_PGRAPH_CLIPX_0_CLIP5_MAX 22 -#define NV4_PGRAPH_CLIPX_0_CLIP6_MIN 24 -#define NV4_PGRAPH_CLIPX_0_CLIP6_MAX 26 -#define NV4_PGRAPH_CLIPX_0_CLIP7_MIN 28 -#define NV4_PGRAPH_CLIPX_0_CLIP7_MAX 30 -#define NV4_PGRAPH_CLIPX_1 0x400528 -#define NV4_PGRAPH_CLIPX_1_CLIP8_MIN 0 -#define NV4_PGRAPH_CLIPX_1_CLIP8_MAX 2 -#define NV4_PGRAPH_CLIPX_1_CLIP9_MIN 4 -#define NV4_PGRAPH_CLIPX_1_CLIP9_MAX 6 -#define NV4_PGRAPH_CLIPX_1_CLIP10_MIN 8 -#define NV4_PGRAPH_CLIPX_1_CLIP10_MAX 10 -#define NV4_PGRAPH_CLIPX_1_CLIP11_MIN 12 -#define NV4_PGRAPH_CLIPX_1_CLIP11_MAX 14 -#define NV4_PGRAPH_CLIPX_1_CLIP12_MIN 16 -#define NV4_PGRAPH_CLIPX_1_CLIP12_MAX 18 -#define NV4_PGRAPH_CLIPX_1_CLIP13_MIN 20 -#define NV4_PGRAPH_CLIPX_1_CLIP13_MAX 22 -#define NV4_PGRAPH_CLIPX_1_CLIP14_MIN 24 -#define NV4_PGRAPH_CLIPX_1_CLIP14_MAX 26 -#define NV4_PGRAPH_CLIPX_1_CLIP15_MIN 28 -#define NV4_PGRAPH_CLIPX_1_CLIP15_MAX 30 -#define NV4_PGRAPH_CLIPY_0 0x40052c -#define NV4_PGRAPH_CLIPY_0_CLIP0_MIN 0 -#define NV4_PGRAPH_CLIPY_0_CLIP0_MAX 2 -#define NV4_PGRAPH_CLIPY_0_CLIP1_MIN 4 -#define NV4_PGRAPH_CLIPY_0_CLIP1_MAX 6 -#define NV4_PGRAPH_CLIPY_0_CLIP2_MIN 8 -#define NV4_PGRAPH_CLIPY_0_CLIP2_MAX 10 -#define NV4_PGRAPH_CLIPY_0_CLIP3_MIN 12 -#define NV4_PGRAPH_CLIPY_0_CLIP3_MAX 14 -#define NV4_PGRAPH_CLIPY_0_CLIP4_MIN 16 -#define NV4_PGRAPH_CLIPY_0_CLIP4_MAX 18 -#define NV4_PGRAPH_CLIPY_0_CLIP5_MIN 20 -#define NV4_PGRAPH_CLIPY_0_CLIP5_MAX 22 -#define NV4_PGRAPH_CLIPY_0_CLIP6_MIN 24 -#define NV4_PGRAPH_CLIPY_0_CLIP6_MAX 26 -#define NV4_PGRAPH_CLIPY_0_CLIP7_MIN 28 -#define NV4_PGRAPH_CLIPY_0_CLIP7_MAX 30 -#define NV4_PGRAPH_CLIPY_1 0x400530 -#define NV4_PGRAPH_CLIPY_1_CLIP8_MIN 0 -#define NV4_PGRAPH_CLIPY_1_CLIP8_MAX 2 -#define NV4_PGRAPH_CLIPY_1_CLIP9_MIN 4 -#define NV4_PGRAPH_CLIPY_1_CLIP9_MAX 6 -#define NV4_PGRAPH_CLIPY_1_CLIP10_MIN 8 -#define NV4_PGRAPH_CLIPY_1_CLIP10_MAX 10 -#define NV4_PGRAPH_CLIPY_1_CLIP11_MIN 12 -#define NV4_PGRAPH_CLIPY_1_CLIP11_MAX 14 -#define NV4_PGRAPH_CLIPY_1_CLIP12_MIN 16 -#define NV4_PGRAPH_CLIPY_1_CLIP12_MAX 18 -#define NV4_PGRAPH_CLIPY_1_CLIP13_MIN 20 -#define NV4_PGRAPH_CLIPY_1_CLIP13_MAX 22 -#define NV4_PGRAPH_CLIPY_1_CLIP14_MIN 24 -#define NV4_PGRAPH_CLIPY_1_CLIP14_MAX 26 -#define NV4_PGRAPH_CLIPY_1_CLIP15_MIN 28 -#define NV4_PGRAPH_CLIPY_1_CLIP15_MAX 30 -#define NV4_PGRAPH_MISC24_0 0x400510 -#define NV4_PGRAPH_MISC24_0_VALUE 0 -#define NV4_PGRAPH_MISC24_1 0x400570 -#define NV4_PGRAPH_MISC24_1_VALUE 0 -#define NV4_PGRAPH_MISC24_2 0x400574 -#define NV4_PGRAPH_MISC24_2_VALUE 0 -#define NV4_PGRAPH_PASSTHRU_0 0x40057C -#define NV4_PGRAPH_PASSTHRU_0_VALUE 0 -#define NV4_PGRAPH_PASSTHRU_1 0x400580 -#define NV4_PGRAPH_PASSTHRU_1_VALUE 0 -#define NV4_PGRAPH_PASSTHRU_2 0x400584 -#define NV4_PGRAPH_PASSTHRU_2_VALUE 0 -#define NV4_PGRAPH_U_RAM(i) (0x400d00+(i)*4) -#define NV4_PGRAPH_U_RAM_SIZE_1 16 -#define NV4_PGRAPH_U_RAM_VALUE 6 -#define NV4_PGRAPH_V_RAM(i) (0x400d40+(i)*4) -#define NV4_PGRAPH_V_RAM_SIZE_1 16 -#define NV4_PGRAPH_V_RAM_VALUE 6 -#define NV4_PGRAPH_M_RAM(i) (0x400d80+(i)*4) -#define NV4_PGRAPH_M_RAM_SIZE_1 16 -#define NV4_PGRAPH_M_RAM_VALUE 6 -#define NV4_PGRAPH_D3D_XY 0x4005c0 -#define NV4_PGRAPH_D3D_XY_X_VALUE 0 -#define NV4_PGRAPH_D3D_XY_Y_VALUE 16 -#define NV4_PGRAPH_D3D_U0 0x4005c4 -#define NV4_PGRAPH_D3D_U0_VALUE 6 -#define NV4_PGRAPH_D3D_V0 0x4005c8 -#define NV4_PGRAPH_D3D_V0_VALUE 6 -#define NV4_PGRAPH_D3D_U1 0x4005cc -#define NV4_PGRAPH_D3D_U1_VALUE 6 -#define NV4_PGRAPH_D3D_V1 0x4005d0 -#define NV4_PGRAPH_D3D_V1_VALUE 6 -#define NV4_PGRAPH_D3D_ZETA 0x4005d4 -#define NV4_PGRAPH_D3D_ZETA_VALUE 0 -#define NV4_PGRAPH_D3D_RGB 0x4005d8 -#define NV4_PGRAPH_D3D_RGB_VALUE 0 -#define NV4_PGRAPH_D3D_S 0x4005dc -#define NV4_PGRAPH_D3D_S_VALUE 0 -#define NV4_PGRAPH_D3D_M 0x4005e0 -#define NV4_PGRAPH_D3D_M_VALUE 6 -#define NV4_PGRAPH_FORMAT0 0x4005A8 -#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA 1 -#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA_A 0x0 -#define NV4_PGRAPH_FORMAT0_CONTEXT_DMA_B 0x1 -#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE 2 -#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE_FALSE 0x0 -#define NV4_PGRAPH_FORMAT0_COLORKEYENABLE_TRUE 0x1 -#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH 5 -#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH_CENTER 0x0 -#define NV4_PGRAPH_FORMAT0_ORIGIN_ZOH_CORNER 0x1 -#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH 7 -#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH_CENTER 0x0 -#define NV4_PGRAPH_FORMAT0_ORIGIN_FOH_CORNER 0x1 -#define NV4_PGRAPH_FORMAT0_COLOR 8 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_Y8 0x0 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_AY8 0x1 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_A1R5G5B5 0x2 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_X1R5G5B5 0x3 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_A4R4G4B4 0x4 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_R5G6B5 0x5 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_A8R8G8B8 0x6 -#define NV4_PGRAPH_FORMAT0_COLOR_LE_X8R8G8B8 0x7 -// 0x1-0Xf used for 1-16 mipmap levels -#define NV4_PGRAPH_FORMAT0_MIPMAP_LEVELS 12 -#define NV4_PGRAPH_FORMAT0_MIPMAP_LEVELS_INVALID 0x0 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U 16 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_1 0x0 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_2 0x1 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_4 0x2 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_8 0x3 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_16 0x4 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_32 0x5 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_64 0x6 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_128 0x7 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_256 0x8 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_512 0x9 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_1024 0xA -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_U_2048 0xB -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V 20 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_1 0x0 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_2 0x1 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_4 0x2 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_8 0x3 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_16 0x4 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_32 0x5 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_64 0x6 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_128 0x7 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_256 0x8 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_512 0x9 -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_1024 0xA -#define NV4_PGRAPH_FORMAT0_BASE_SIZE_V_2048 0xB -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU 24 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_WRAP 0x1 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_MIRROR 0x2 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_CLAMP 0x3 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSU_BORDER 0x4 -#define NV4_PGRAPH_FORMAT0_WRAPU 27 -#define NV4_PGRAPH_FORMAT0_WRAPU_FALSE 0x0 -#define NV4_PGRAPH_FORMAT0_WRAPU_TRUE 0x1 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV 28 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_WRAP 0x1 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_MIRROR 0x2 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_CLAMP 0x3 -#define NV4_PGRAPH_FORMAT0_TEXTUREADDRESSV_BORDER 0x4 -#define NV4_PGRAPH_FORMAT0_WRAPV 31 -#define NV4_PGRAPH_FORMAT0_WRAPV_FALSE 0x0 -#define NV4_PGRAPH_FORMAT0_WRAPV_TRUE 0x1 -#define NV4_PGRAPH_FORMAT1 0x4005AC -#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA 1 -#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA_A 0x0 -#define NV4_PGRAPH_FORMAT1_CONTEXT_DMA_B 0x1 -#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE 2 -#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE_FALSE 0x0 -#define NV4_PGRAPH_FORMAT1_COLORKEYENABLE_TRUE 0x1 -#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH 5 -#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH_CENTER 0x0 -#define NV4_PGRAPH_FORMAT1_ORIGIN_ZOH_CORNER 0x1 -#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH 7 -#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH_CENTER 0x0 -#define NV4_PGRAPH_FORMAT1_ORIGIN_FOH_CORNER 0x1 -#define NV4_PGRAPH_FORMAT1_COLOR 8 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_Y8 0x0 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_AY8 0x1 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_A1R5G5B5 0x2 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_X1R5G5B5 0x3 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_A4R4G4B4 0x4 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_R5G6B5 0x5 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_A8R8G8B8 0x6 -#define NV4_PGRAPH_FORMAT1_COLOR_LE_X8R8G8B8 0x7 -// 15:12 = number of mipmap levels (1-15. 0 = invalid). Stupid defines removed here -#define NV4_PGRAPH_FORMAT1_MIPMAP_LEVELS 12 -#define NV4_PGRAPH_FORMAT1_MIPMAP_LEVELS_INVALID 0x0 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U 16 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_1 0x0 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_2 0x1 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_4 0x2 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_8 0x3 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_16 0x4 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_32 0x5 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_64 0x6 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_128 0x7 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_256 0x8 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_512 0x9 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_1024 0xA -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_U_2048 0xB -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V 20 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_1 0x0 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_2 0x1 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_4 0x2 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_8 0x3 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_16 0x4 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_32 0x5 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_64 0x6 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_128 0x7 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_256 0x8 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_512 0x9 -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_1024 0xA -#define NV4_PGRAPH_FORMAT1_BASE_SIZE_V_2048 0xB -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU 24 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_WRAP 0x1 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_MIRROR 0x2 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_CLAMP 0x3 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSU_BORDER 0x4 -#define NV4_PGRAPH_FORMAT1_WRAPU 27 -#define NV4_PGRAPH_FORMAT1_WRAPU_TRUE 0x1 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV 28 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_WRAP 0x1 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_MIRROR 0x2 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_CLAMP 0x3 -#define NV4_PGRAPH_FORMAT1_TEXTUREADDRESSV_BORDER 0x4 -#define NV4_PGRAPH_FORMAT1_WRAPV 31 -#define NV4_PGRAPH_FORMAT1_WRAPV_TRUE 0x1 -#define NV4_PGRAPH_FILTER0 0x4005B0 -#define NV4_PGRAPH_FILTER0_KERNEL_SIZE_X 1 -#define NV4_PGRAPH_FILTER0_KERNEL_SIZE_Y 9 -#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE 15 -#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE_FALSE 0x0 -#define NV4_PGRAPH_FILTER0_MIPMAP_DITHER_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_FILTER0_MIPMAPLODBIAS 16 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN 24 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_NEAREST 0x1 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEAR 0x2 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_MIPNEAREST 0x3 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_MIPLINEAR 0x4 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEARMIPNEAREST 0x5 -#define NV4_PGRAPH_FILTER0_TEXTUREMIN_LINEARMIPLINEAR 0x6 -#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MIN_ENABLE 27 -#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MIN_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG 28 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_NEAREST 0x1 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEAR 0x2 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_MIPNEAREST 0x3 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_MIPLINEAR 0x4 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEARMIPNEAREST 0x5 -#define NV4_PGRAPH_FILTER0_TEXTUREMAG_LINEARMIPLINEAR 0x6 -#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MAG_ENABLE 31 -#define NV4_PGRAPH_FILTER0_ANISOTROPIC_MAG_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_FILTER1 0x4005B4 -#define NV4_PGRAPH_FILTER1_KERNEL_SIZE_X 1 -#define NV4_PGRAPH_FILTER1_KERNEL_SIZE_Y 9 -#define NV4_PGRAPH_FILTER1_MIPMAP_DITHER_ENABLE 15 -#define NV4_PGRAPH_FILTER1_MIPMAP_DITHER_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_FILTER1_MIPMAPLODBIAS 16 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN 24 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_NEAREST 0x1 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEAR 0x2 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_MIPNEAREST 0x3 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_MIPLINEAR 0x4 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEARMIPNEAREST 0x5 -#define NV4_PGRAPH_FILTER1_TEXTUREMIN_LINEARMIPLINEAR 0x6 -#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MIN_ENABLE 27 -#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MIN_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG 28 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_NEAREST 0x1 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEAR 0x2 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_MIPNEAREST 0x3 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_MIPLINEAR 0x4 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEARMIPNEAREST 0x5 -#define NV4_PGRAPH_FILTER1_TEXTUREMAG_LINEARMIPLINEAR 0x6 -#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MAG_ENABLE 31 -#define NV4_PGRAPH_FILTER1_ANISOTROPIC_MAG_ENABLE_TRUE 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA 0x400590 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0 0 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0 4:2 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_0_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1 8 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_1_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1 10 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_1_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2 16 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_2_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2 18 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_2_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3 24 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0ALPHA_INVERSE_3_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3 26 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0ALPHA_ARGUMENT_3_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION 29 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD 0x1 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD2 0x2 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADD4 0x3 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDSIGNED 0x4 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_MUX 0x5 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDCOMPLEMENT 0x6 -#define NV4_PGRAPH_COMBINE0ALPHA_OPERATION_ADDSIGNED2 0x7 -#define NV4_PGRAPH_COMBINE0COLOR 0x400594 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0 0 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0 1 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_COLOR 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_0_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0 2 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_0_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1 8 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_1_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1 9 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1_COLOR 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_1_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1 10 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_1_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2 16 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_2_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2 17 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2_COLOR 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_2_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2 18 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_2_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3 24 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_INVERSE_3_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3 25 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3_COLOR 0x0 -#define NV4_PGRAPH_COMBINE0COLOR_ALPHA_3_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3 26 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_ZERO 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_INPUT 0x4 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE0COLOR_ARGUMENT_3_TEXTURELOD 0x7 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION 29 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD 0x1 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD2 0x2 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADD4 0x3 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDSIGNED 0x4 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_MUX 0x5 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDCOMPLEMENT 0x6 -#define NV4_PGRAPH_COMBINE0COLOR_OPERATION_ADDSIGNED2 0x7 -#define NV4_PGRAPH_COMBINE1ALPHA 0x400598 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0 0 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0 2 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_0_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1 8 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_1_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1 10 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_1_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2 16 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_2_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2 18 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_2_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3 24 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1ALPHA_INVERSE_3_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3 26 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1ALPHA_ARGUMENT_3_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION 29 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD 0x1 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD2 0x2 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADD4 0x3 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDSIGNED 0x4 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_MUX 0x5 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDCOMPLEMENT 0x6 -#define NV4_PGRAPH_COMBINE1ALPHA_OPERATION_ADDSIGNED2 0x7 -#define NV4_PGRAPH_COMBINE1COLOR 0x40059C -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0 0 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_0_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0 1 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0_COLOR 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_0_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0 4:2 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_0_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1 8 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_1_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1 9 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1_COLOR 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_1_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1 10 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_1_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2 16 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_2_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2 17 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2_COLOR 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_2_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2 18 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_2_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3 24 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3_NORMAL 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_INVERSE_3_INVERSE 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3 25 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3_COLOR 0x0 -#define NV4_PGRAPH_COMBINE1COLOR_ALPHA_3_ALPHA 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3 26 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_ZERO 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_FACTOR 0x2 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_DIFFUSE 0x3 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_INPUT 0x4 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_TEXTURE0 0x5 -#define NV4_PGRAPH_COMBINE1COLOR_ARGUMENT_3_TEXTURE1 0x6 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION 29 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD 0x1 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD2 0x2 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADD4 0x3 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDSIGNED 0x4 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_MUX 0x5 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDCOMPLEMENT 0x6 -#define NV4_PGRAPH_COMBINE1COLOR_OPERATION_ADDSIGNED2 0x7 -#define NV4_PGRAPH_DMA_START_0 0x401000 -#define NV4_PGRAPH_DMA_START_0_VALUE 0 -#define NV4_PGRAPH_DMA_START_1 0x401004 -#define NV4_PGRAPH_DMA_START_1_VALUE 0 -#define NV4_PGRAPH_DMA_LENGTH 0x401008 -#define NV4_PGRAPH_DMA_LENGTH_VALUE 0 -#define NV4_PGRAPH_DMA_MISC 0x40100C -#define NV4_PGRAPH_DMA_MISC_COUNT 0 -#define NV4_PGRAPH_DMA_MISC_FMT_SRC 16 -#define NV4_PGRAPH_DMA_MISC_FMT_DST 20 -#define NV4_PGRAPH_DMA_DATA_0 0x401020 -#define NV4_PGRAPH_DMA_DATA_0_VALUE 0 -#define NV4_PGRAPH_DMA_DATA_1 0x401024 -#define NV4_PGRAPH_DMA_DATA_1_VALUE 0 -#define NV4_PGRAPH_DMA_RM 0x401030 -#define NV4_PGRAPH_DMA_RM_ASSIST_A 0 -#define NV4_PGRAPH_DMA_RM_ASSIST_A_NOT_PENDING 0x0 -#define NV4_PGRAPH_DMA_RM_ASSIST_A_PENDING 0x1 -#define NV4_PGRAPH_DMA_RM_ASSIST_A_RESET 0x1 -#define NV4_PGRAPH_DMA_RM_ASSIST_B 1 -#define NV4_PGRAPH_DMA_RM_ASSIST_B_NOT_PENDING 0x0 -#define NV4_PGRAPH_DMA_RM_ASSIST_B_PENDING 0x1 -#define NV4_PGRAPH_DMA_RM_ASSIST_B_RESET 0x1 -#define NV4_PGRAPH_DMA_RM_WRITE_REQ 4 -#define NV4_PGRAPH_DMA_RM_WRITE_REQ_NOT_PENDING 0x0 -#define NV4_PGRAPH_DMA_RM_WRITE_REQ_PENDING 0x1 -#define NV4_PGRAPH_DMA_A_XLATE_INST 0x401040 -#define NV4_PGRAPH_DMA_A_XLATE_INST_VALUE 0 -#define NV4_PGRAPH_DMA_A_CONTROL 0x401044 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE 12 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE_NOT_PRESENT 0x0 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_TABLE_PRESENT 0x1 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY 13 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY_NOT_LINEAR 0x0 -#define NV4_PGRAPH_DMA_A_CONTROL_PAGE_ENTRY_LINEAR 0x1 -#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE 16 -#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_NVM 0x0 -#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_PCI 0x2 -#define NV4_PGRAPH_DMA_A_CONTROL_TARGET_NODE_AGP 0x3 -#define NV4_PGRAPH_DMA_A_CONTROL_ADJUST 20 -#define NV4_PGRAPH_DMA_A_LIMIT 0x401048 -#define NV4_PGRAPH_DMA_A_LIMIT_OFFSET 0 -#define NV4_PGRAPH_DMA_A_TLB_PTE 0x40104C -#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS 1 -#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS_READ_ONLY 0x0 -#define NV4_PGRAPH_DMA_A_TLB_PTE_ACCESS_READ_WRITE 0x1 -#define NV4_PGRAPH_DMA_A_TLB_PTE_FRAME_ADDRESS 12 -#define NV4_PGRAPH_DMA_A_TLB_TAG 0x401050 -#define NV4_PGRAPH_DMA_A_TLB_TAG_ADDRESS 12 -#define NV4_PGRAPH_DMA_A_ADJ_OFFSET 0x401054 -#define NV4_PGRAPH_DMA_A_ADJ_OFFSET_VALUE 0 -#define NV4_PGRAPH_DMA_A_OFFSET 0x401058 -#define NV4_PGRAPH_DMA_A_OFFSET_VALUE 0 -#define NV4_PGRAPH_DMA_A_SIZE 0x40105C -#define NV4_PGRAPH_DMA_A_SIZE_VALUE 0 -#define NV4_PGRAPH_DMA_A_Y_SIZE 0x401060 -#define NV4_PGRAPH_DMA_A_Y_SIZE_VALUE 0 -#define NV4_PGRAPH_DMA_B_XLATE_INST 0x401080 -#define NV4_PGRAPH_DMA_B_XLATE_INST_VALUE 0 -#define NV4_PGRAPH_DMA_B_CONTROL 0x401084 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE 12 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE_NOT_PRESENT 0x0 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_TABLE_PRESENT 0x1 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY 13 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY_NOT_LINEAR 0x0 -#define NV4_PGRAPH_DMA_B_CONTROL_PAGE_ENTRY_LINEAR 0x1 -#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE 16 -#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_NVM 0x0 -#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_PCI 0x2 -#define NV4_PGRAPH_DMA_B_CONTROL_TARGET_NODE_AGP 0x3 -#define NV4_PGRAPH_DMA_B_CONTROL_ADJUST 20 -#define NV4_PGRAPH_DMA_B_LIMIT 0x401088 -#define NV4_PGRAPH_DMA_B_LIMIT_OFFSET 0 -#define NV4_PGRAPH_DMA_B_TLB_PTE 0x40108C -#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS 1 -#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS_READ_ONLY 0x0 -#define NV4_PGRAPH_DMA_B_TLB_PTE_ACCESS_READ_WRITE 0x1 -#define NV4_PGRAPH_DMA_B_TLB_PTE_FRAME_ADDRESS 12 -#define NV4_PGRAPH_DMA_B_TLB_TAG 0x401090 -#define NV4_PGRAPH_DMA_B_TLB_TAG_ADDRESS 12 -#define NV4_PGRAPH_DMA_B_ADJ_OFFSET 0x401094 -#define NV4_PGRAPH_DMA_B_ADJ_OFFSET_VALUE 0 -#define NV4_PGRAPH_DMA_B_OFFSET 0x401098 -#define NV4_PGRAPH_DMA_B_OFFSET_VALUE 0 -#define NV4_PGRAPH_DMA_B_SIZE 0x40109C -#define NV4_PGRAPH_DMA_B_SIZE_VALUE 0 -#define NV4_PGRAPH_DMA_B_Y_SIZE 0x4010A0 -#define NV4_PGRAPH_DMA_B_Y_SIZE_VALUE 0 -#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR 0 -#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_ZOH_MS 0x0 -#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_ZOH 0x1 -#define NV4_PGRAPH_CONTROL_OUT_INTERPOLATOR_FOH 0x2 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_U 4 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_CYLINDRICAL 0x0 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_WRAP 0x1 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_MIRROR 0x2 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_U_CLAMP 0x3 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_V 6 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_CYLINDRICAL 0x0 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_WRAP 0x1 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_MIRROR 0x2 -#define NV4_PGRAPH_CONTROL_OUT_WRAP_V_CLAMP 0x3 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT 8 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT_LE_X8R8G8B8 0x0 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_FORMAT_LE_A8R8G8B8 0x1 -#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR 10 -#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_NORMAL 0x0 -#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_COLOR_INVERSE 0x1 -#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_ALPHA_INVERSE 0x2 -#define NV4_PGRAPH_CONTROL_OUT_SRCCOLOR_ALPHA_ONE 0x3 -#define NV4_PGRAPH_CONTROL_OUT_CULLING 12 -#define NV4_PGRAPH_CONTROL_OUT_CULLING_ILLEGAL 0x0 -#define NV4_PGRAPH_CONTROL_OUT_CULLING_NONE 0x1 -#define NV4_PGRAPH_CONTROL_OUT_CULLING_COUNTERCLOCKWISE 0x2 -#define NV4_PGRAPH_CONTROL_OUT_CULLING_CLOCKWISE 0x3 -#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER 15 -#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER_SCREEN 0x0 -#define NV4_PGRAPH_CONTROL_OUT_ZBUFFER_LINEAR 0x1 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE 16 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_ILLEGAL 0x0 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_FALSE 0x1 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_LT 0x2 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_EQ 0x3 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_LE 0x4 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_GT 0x5 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_NE 0x6 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_GE 0x7 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_COMPARE_TRUE 0x8 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE 20 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_NEVER 0x0 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALPHA 0x1 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALPHA_ZETA 0x2 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ZETA 0x3 -#define NV4_PGRAPH_CONTROL_OUT_ZETA_WRITE_ALWAYS 0x4 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE 24 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_NEVER 0x0 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ALPHA 0x1 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ALPHA_ZETA 0x2 -#define NV4_PGRAPH_CONTROL_OUT_COLOR_WRITE_ZETA 0x3 -#define NV4_PGRAPH_CONTROL_OUT_ROP 28 -#define NV4_PGRAPH_CONTROL_OUT_ROP_BLEND_AND 0x0 -#define NV4_PGRAPH_CONTROL_OUT_ROP_ADD_WITH_SATURATION 0x1 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA 29 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA_SRCALPHA 0x0 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_BETA_DESTCOLOR 0x1 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0 30 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0_DESTCOLOR 0x0 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT0_ZERO 0x1 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1 31 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1_SRCCOLOR 0x0 -#define NV4_PGRAPH_CONTROL_OUT_BLEND_INPUT1_ZERO 0x1 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_KEY 0 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE 8 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_ILLEGAL 0x0 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_FALSE 0x1 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_LT 0x2 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_EQ 0x3 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_LE 0x4 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_GT 0x5 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_NE 0x6 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_GE 0x7 -#define NV4_PGRAPH_ALPHACNTRL_ALPHA_COMPARE_TRUE 0x8 - -#define NV4_PVIDEO_START 0x680000 -#define NV4_PVIDEO_END 0x6802FF - -#define NV4_PVIDEO_INTR_0 0x680100 -#define NV4_PVIDEO_INTR_0_NOTIFY 0 -#define NV4_PVIDEO_INTR_0_NOTIFY_NOT_PENDING 0x0 -#define NV4_PVIDEO_INTR_0_NOTIFY_PENDING 0x1 -#define NV4_PVIDEO_INTR_0_NOTIFY_RESET 0x1 -#define NV4_PVIDEO_INTR_EN_0 0x680140 -#define NV4_PVIDEO_INTR_EN_0_NOTIFY 0 -#define NV4_PVIDEO_INTR_EN_0_NOTIFY_ENABLED 0x1 -#define NV4_PVIDEO_STEP_SIZE 0x680200 -#define NV4_PVIDEO_STEP_SIZE_X 0 -#define NV4_PVIDEO_STEP_SIZE_Y 16 -#define NV4_PVIDEO_CONTROL_Y 0x680204 -#define NV4_PVIDEO_CONTROL_Y_BLUR 0 -#define NV4_PVIDEO_CONTROL_Y_BLUR_OFF 0x0 -#define NV4_PVIDEO_CONTROL_Y_BLUR_ON 0x1 -#define NV4_PVIDEO_CONTROL_Y_LINE 4 -#define NV4_PVIDEO_CONTROL_Y_LINE_HALF 0x0 -#define NV4_PVIDEO_CONTROL_Y_LINE_FULL 0x1 -#define NV4_PVIDEO_CONTROL_X 0x680208 -#define NV4_PVIDEO_CONTROL_X_WEIGHT 0 -#define NV4_PVIDEO_CONTROL_X_WEIGHT_LIGHT 0x0 -#define NV4_PVIDEO_CONTROL_X_WEIGHT_HEAVY 0x1 -#define NV4_PVIDEO_CONTROL_X_SHARPENING 4 -#define NV4_PVIDEO_CONTROL_X_SHARPENING_OFF 0x0 -#define NV4_PVIDEO_CONTROL_X_SHARPENING_ON 0x1 -#define NV4_PVIDEO_CONTROL_X_SMOOTHING 8 -#define NV4_PVIDEO_CONTROL_X_SMOOTHING_OFF 0x0 -#define NV4_PVIDEO_CONTROL_X_SMOOTHING_ON 0x1 -#define NV4_PVIDEO_BUFF0_START 0x68020c -#define NV4_PVIDEO_BUFF0_START_ADDRESS 2 -#define NV4_PVIDEO_BUFF1_START 0x680210 -#define NV4_PVIDEO_BUFF1_START_ADDRESS 2 -#define NV4_PVIDEO_BUFF0_PITCH 0x680214 -#define NV4_PVIDEO_BUFF0_PITCH_LENGTH 4 -#define NV4_PVIDEO_BUFF1_PITCH 0x680218 -#define NV4_PVIDEO_BUFF1_PITCH_LENGTH 4 -#define NV4_PVIDEO_BUFF0_OFFSET 0x68021c -#define NV4_PVIDEO_BUFF0_OFFSET_X 0 -#define NV4_PVIDEO_BUFF0_OFFSET_Y 4 -#define NV4_PVIDEO_BUFF0_OFFSET_Y_OFF 0x0 -#define NV4_PVIDEO_BUFF0_OFFSET_Y_QUARTER 0x1 -#define NV4_PVIDEO_BUFF0_OFFSET_Y_HALF 0x2 -#define NV4_PVIDEO_BUFF1_OFFSET 0x680220 -#define NV4_PVIDEO_BUFF1_OFFSET_X 0 -#define NV4_PVIDEO_BUFF1_OFFSET_Y 4 -#define NV4_PVIDEO_BUFF1_OFFSET_Y_OFF 0x0 -#define NV4_PVIDEO_BUFF1_OFFSET_Y_QUARTER 0x1 -#define NV4_PVIDEO_BUFF1_OFFSET_Y_HALF 0x2 -#define NV4_PVIDEO_OE_STATE 0x680224 -#define NV4_PVIDEO_OE_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PVIDEO_OE_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PVIDEO_OE_STATE_BUFF0_ERROR 8 -#define NV4_PVIDEO_OE_STATE_BUFF1_ERROR 12 -#define NV4_PVIDEO_OE_STATE_BUFF0_IN_USE 16 -#define NV4_PVIDEO_OE_STATE_BUFF1_IN_USE 20 -#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER 24 -#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER_0 0x0 -#define NV4_PVIDEO_OE_STATE_CURRENT_BUFFER_1 0x1 -#define NV4_PVIDEO_SU_STATE 0x680228 -#define NV4_PVIDEO_SU_STATE_BUFF0_IN_USE 16 -#define NV4_PVIDEO_SU_STATE_BUFF1_IN_USE 20 -#define NV4_PVIDEO_RM_STATE 0x68022c -#define NV4_PVIDEO_RM_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PVIDEO_RM_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PVIDEO_WINDOW_START 0x680230 -#define NV4_PVIDEO_WINDOW_START_X 0 -#define NV4_PVIDEO_WINDOW_START_Y 16 -#define NV4_PVIDEO_WINDOW_SIZE 0x680234 -#define NV4_PVIDEO_WINDOW_SIZE_X 0 -#define NV4_PVIDEO_WINDOW_SIZE_Y 16 -#define NV4_PVIDEO_FIFO_THRES 0x680238 -#define NV4_PVIDEO_FIFO_THRES_SIZE 3 -#define NV4_PVIDEO_FIFO_BURST 0x68023c -#define NV4_PVIDEO_FIFO_BURST_LENGTH 0 -#define NV4_PVIDEO_FIFO_BURST_LENGTH_32 0x1 -#define NV4_PVIDEO_FIFO_BURST_LENGTH_64 0x2 -#define NV4_PVIDEO_FIFO_BURST_LENGTH_128 0x3 -#define NV4_PVIDEO_KEY 0x680240 -#define NV4_PVIDEO_KEY_INDEX 0 -#define NV4_PVIDEO_KEY_565 0 -#define NV4_PVIDEO_KEY_555 0 -#define NV4_PVIDEO_KEY_888 0 -#define NV4_PVIDEO_KEY_PACK 24 -#define NV4_PVIDEO_OVERLAY 0x680244 -#define NV4_PVIDEO_OVERLAY_VIDEO 0 -#define NV4_PVIDEO_OVERLAY_VIDEO_OFF 0x0 -#define NV4_PVIDEO_OVERLAY_VIDEO_ON 0x1 -#define NV4_PVIDEO_OVERLAY_KEY 4 -#define NV4_PVIDEO_OVERLAY_KEY_OFF 0x0 -#define NV4_PVIDEO_OVERLAY_KEY_ON 0x1 -#define NV4_PVIDEO_OVERLAY_FORMAT 8 -#define NV4_PVIDEO_OVERLAY_FORMAT_CCIR 0x0 -#define NV4_PVIDEO_OVERLAY_FORMAT_YUY2 0x1 -#define NV4_PVIDEO_RED_CSC 0x680280 -#define NV4_PVIDEO_RED_CSC_OFFSET 0 -#define NV4_PVIDEO_GREEN_CSC 0x680284 -#define NV4_PVIDEO_GREEN_CSC_OFFSET 0 -#define NV4_PVIDEO_BLUE_CSC 0x680288 -#define NV4_PVIDEO_BLUE_CSC_OFFSET 0 -#define NV4_PVIDEO_CSC_ADJUST 0x68028c -#define NV4_PVIDEO_CSC_ADJUST_B_FLAG 0 -#define NV4_PVIDEO_CSC_ADJUST_B_FLAG_OFF 0x0 -#define NV4_PVIDEO_CSC_ADJUST_B_FLAG_ON 0x1 -#define NV4_PVIDEO_CSC_ADJUST_G_FLAG 4 -#define NV4_PVIDEO_CSC_ADJUST_G_FLAG_OFF 0x0 -#define NV4_PVIDEO_CSC_ADJUST_G_FLAG_ON 0x1 -#define NV4_PVIDEO_CSC_ADJUST_R_FLAG 8 -#define NV4_PVIDEO_CSC_ADJUST_R_FLAG_OFF 0x0 -#define NV4_PVIDEO_CSC_ADJUST_R_FLAG_ON 0x1 -#define NV4_PVIDEO_CSC_ADJUST_L_FLAG 12 -#define NV4_PVIDEO_CSC_ADJUST_L_FLAG_OFF 0x0 -#define NV4_PVIDEO_CSC_ADJUST_L_FLAG_ON 0x1 -#define NV4_PVIDEO_CSC_ADJUST_CHROMA 16 -#define NV4_PVIDEO_CSC_ADJUST_CHROMA_OFF 0x0 -#define NV4_PVIDEO_CSC_ADJUST_CHROMA_ON 0x1 - -#define NV4_PRMCIO_START 0x601000 -#define NV4_PRMCIO_END 0x601FFF - -#define NV4_PRMCIO_INP0 0x6013c2 -#define NV4_PRMCIO_INP0_MONO 0x6013ba -#define NV4_PRMCIO_INP0_COLOR 0x6013da -#define NV4_PRMCIO_INP0_READ_MONO 0x6013ca -#define NV4_PRMCIO_INP0_WRITE_MONO 0x6013ba -#define NV4_PRMCIO_INP0_WRITE_COLOR 0x6013da -// DEFAULT = Palette -#define NV4_PRMCIO_ARX 0x6013c0 -#define NV4_PRMCIO_AR_WRITE 0x6013c0 // After ARX -#define NV4_PRMCIO_AR_READ 0x6013c1 -#define NV4_PRMCIO_AR_PALETTE_WRITE 0x6013c0 -#define NV4_PRMCIO_AR_PALETTE_READ 0x6013c1 -#define NV4_PRMCIO_AR_MODE_INDEX 0x10 -#define NV4_PRMCIO_AR_OSCAN_INDEX 0x11 -#define NV4_PRMCIO_AR_PLANE_INDEX 0x12 -#define NV4_PRMCIO_AR_HPP_INDEX 0x13 -#define NV4_PRMCIO_AR_CSEL_INDEX 0x14 -#define NV4_PRMCIO_CRX_MONO 0x6013b4 -#define NV4_PRMCIO_CRX_COLOR 0x6013d4 -#define NV4_PRMCIO_CR_MONO 0x6013b5 -#define NV4_PRMCIO_CR_COLOR 0x6013d5 -#define NV4_PRMCIO_CRE_MONO 0x6013b5 -#define NV4_PRMCIO_CRE_COLOR 0x6013d5 - -#define NV4_PCRTC_INTR_0 0x600100 -#define NV4_PCRTC_INTR_0_VBLANK 0 -#define NV4_PCRTC_INTR_0_VBLANK_NOT_PENDING 0x0 -#define NV4_PCRTC_INTR_0_VBLANK_PENDING 0x1 -#define NV4_PCRTC_INTR_0_VBLANK_RESET 0x1 -#define NV4_PCRTC_INTR_EN_0 0x600140 -#define NV4_PCRTC_INTR_EN_0_VBLANK 0 -#define NV4_PCRTC_INTR_EN_0_VBLANK_ENABLED 0x1 -#define NV4_PCRTC_START 0x600800 -#define NV4_PCRTC_START_ADDRESS 2 -#define NV4_PCRTC_CONFIG 0x600804 -#define NV4_PCRTC_CONFIG_START_ADDRESS 0 -#define NV4_PCRTC_CONFIG_START_ADDRESS_VGA 0x0 -#define NV4_PCRTC_CONFIG_START_ADDRESS_NON_VGA 0x1 -#define NV4_PCRTC_CONFIG_START_ADDRESS_HSYNC 0x2 -#define NV4_PCRTC_RASTER 0x600808 -#define NV4_PCRTC_RASTER_POSITION 0 -#define NV4_PCRTC_RASTER_SA_LOAD 12 -#define NV4_PCRTC_RASTER_SA_LOAD_DISPLAY 0x0 -#define NV4_PCRTC_RASTER_SA_LOAD_BEFORE 0x1 -#define NV4_PCRTC_RASTER_SA_LOAD_AFTER 0x2 -#define NV4_PCRTC_RASTER_VERT_BLANK 16 -#define NV4_PCRTC_RASTER_VERT_BLANK_ACTIVE 0x1 -#define NV4_PCRTC_RASTER_VERT_BLANK_INACTIVE 0x0 - -#define NV4_CIO_START 0x3B0 -#define NV4_CIO_END 0x3DF -#define NV4_CIO_SIZE NV4_CIO_END - NV4_CIO_START - - -#define NV4_CIO_INP0 0x3c2 -#define NV4_CIO_INP0_MONO 0x3ba -#define NV4_CIO_INP0_COLOR 0x3da -#define NV4_CIO_INP0_READ_MONO 0x3ca -#define NV4_CIO_INP0_WRITE_MONO 0x3ba -#define NV4_CIO_INP0_WRITE_COLOR 0x3da -#define NV4_CIO_ARX 0x3c0 // if index is not 0x10-0x13 write palette -#define NV4_CIO_AR_PALETTE_WRITE 0x3c0 -#define NV4_CIO_AR_PALETTE_READ 0x3c1 -#define NV4_CIO_AR_MODE_WRITE 0x3c0 -#define NV4_CIO_AR_MODE_READ 0x3c1 -#define NV4_CIO_AR_MODE_INDEX 0x10 -#define NV4_CIO_AR_OSCAN_WRITE 0x3c0 -#define NV4_CIO_AR_OSCAN_READ 0x3c1 -#define NV4_CIO_AR_OSCAN_INDEX 0x11 -#define NV4_CIO_AR_PLANE_WRITE 0x3c0 -#define NV4_CIO_AR_PLANE_READ 0x3c1 -#define NV4_CIO_AR_PLANE_INDEX 0x12 -#define NV4_CIO_AR_HPP_WRITE 0x3c0 -#define NV4_CIO_AR_HPP_READ 0x3c1 -#define NV4_CIO_AR_HPP_INDEX 0x13 -#define NV4_CIO_AR_CSEL_WRITE 0x3c0 -#define NV4_CIO_AR_CSEL_READ 0x3c1 -#define NV4_CIO_AR_CSEL_INDEX 0x14 -#define NV4_CIO_CRX_MONO 0x3b4 -#define NV4_CIO_CRX_COLOR 0x3d4 -#define NV4_CIO_CR_MONO 0x3b5 -#define NV4_CIO_CR_COLOR 0x3d5 -#define NV4_CIO_CR_HDT_INDEX 0x0 -#define NV4_CIO_CR_HDE_INDEX 0x1 -#define NV4_CIO_CR_HBS_INDEX 0x2 -#define NV4_CIO_CR_HBE_INDEX 0x3 -#define NV4_CIO_CR_HBE_4_0 0 -#define NV4_CIO_CR_HRS_INDEX 0x4 -#define NV4_CIO_CR_HRE_INDEX 0x5 -#define NV4_CIO_CR_HRE_HBE_5 7 -#define NV4_CIO_CR_HRE_4_0 0 -#define NV4_CIO_CR_VDT_INDEX 0x6 -#define NV4_CIO_CR_OVL_INDEX 0x7 -#define NV4_CIO_CR_OVL_VDE_8 1 -#define NV4_CIO_CR_OVL_VDE_9 6 -#define NV4_CIO_CR_OVL_VDT_8 0 -#define NV4_CIO_CR_OVL_VDT_9 5 -#define NV4_CIO_CR_OVL_VBS_8 3 -#define NV4_CIO_CR_OVL_VRS_8 2 -#define NV4_CIO_CR_OVL_VRS_9 7 -#define NV4_CIO_CR_RSAL_INDEX 0x8 -#define NV4_CIO_CR_RSAL_PANNING 5 -#define NV4_CIO_CR_CELL_HT_INDEX 0x9 -#define NV4_CIO_CR_CELL_HT_SCANDBL 7 -#define NV4_CIO_CR_CELL_HT_VBS_9 5 -#define NV4_CIO_CR_CURS_ST_INDEX 0xA -#define NV4_CIO_CR_CURS_END_INDEX 0xB -#define NV4_CIO_CR_SA_HI_INDEX 0xC -#define NV4_CIO_CR_SA_LO_INDEX 0xD -#define NV4_CIO_CR_TCOFF_HI_INDEX 0xE -#define NV4_CIO_CR_TCOFF_LO_INDEX 0xF -#define NV4_CIO_CR_VRS_INDEX 0x10 -#define NV4_CIO_CR_VRE_INDEX 0x11 -#define NV4_CIO_CR_VRE_3_0 0 -#define NV4_CIO_CR_VDE_INDEX 0x12 -#define NV4_CIO_CR_OFFSET_INDEX 0x13 -#define NV4_CIO_CR_ULINE_INDEX 0x14 -#define NV4_CIO_CR_VBS_INDEX 0x15 -#define NV4_CIO_CR_VBE_INDEX 0x16 -#define NV4_CIO_CR_MODE_INDEX 0x17 -#define NV4_CIO_CR_LCOMP_INDEX 0x18 -#define NV4_CIO_CR_GDATA_INDEX 0x22 -#define NV4_CIO_CR_ARFF_INDEX 0x24 -#define NV4_CIO_CR_ARX_INDEX 0x26 -#define NV4_CIO_CRE_MONO 0x3b5 -#define NV4_CIO_CRE_COLOR 0x3d5 -#define NV4_CIO_CRE_RPC0_INDEX 0x19 -#define NV4_CIO_CRE_RPC0_START 0 -#define NV4_CIO_CRE_RPC0_OFFSET_10_8 5 -#define NV4_CIO_CRE_RPC1_INDEX 0x1A -#define NV4_CIO_CRE_RPC1_LARGE 2 -#define NV4_CIO_CRE_FF_INDEX 0x1B -#define NV4_CIO_CRE_FF_BURST 0 -#define NV4_CIO_CRE_FF_BURST_8 0x0 -#define NV4_CIO_CRE_FF_BURST_32 0x1 -#define NV4_CIO_CRE_FF_BURST_64 0x2 -#define NV4_CIO_CRE_FF_BURST_128 0x3 -#define NV4_CIO_CRE_FF_BURST_256 0x4 -#define NV4_CIO_CRE_ENH_INDEX 0x1C -#define NV4_CIO_CRE_PAGE0_INDEX 0x1D -#define NV4_CIO_CRE_PAGE1_INDEX 0x1E -#define NV4_CIO_SR_LOCK_INDEX 0x1F -#define NV4_CIO_SR_UNLOCK_RW_VALUE 0x57 -#define NV4_CIO_SR_UNLOCK_RO_VALUE 0x75 -#define NV4_CIO_SR_LOCK_VALUE 0x99 -#define NV4_SR_UNLOCK_RW_VALUE 0x57 -#define NV4_SR_UNLOCK_RO_VALUE 0x75 -#define NV4_SR_LOCK_VALUE 0x99 -#define NV4_CIO_CRE_FFLWM_INDEX 0x20 -#define NV4_CIO_CRE_FFLWM_LWM 0 -#define NV4_CIO_CRE_FABID_INDEX 0x25 -#define NV4_CIO_CRE_LSR_INDEX 0x25 -#define NV4_CIO_CRE_LSR_VDE_10 1 -#define NV4_CIO_CRE_LSR_VDT_10 0 -#define NV4_CIO_CRE_LSR_HBE_6 4 -#define NV4_CIO_CRE_LSR_VBS_10 3 -#define NV4_CIO_CRE_LSR_VRS_10 2 -#define NV4_CIO_CRE_CHIP_ID_INDEX 0x27 -#define NV4_CIO_CRE_PIXEL_INDEX 0x28 -#define NV4_CIO_CRE_PIXEL_TV_ADJ 3 -#define NV4_CIO_CRE_PIXEL_FORMAT 0 -#define NV4_CIO_CRE_PIXEL_FORMAT_VGA 0x0 -#define NV4_CIO_CRE_PIXEL_FORMAT_8BPP 0x1 -#define NV4_CIO_CRE_PIXEL_FORMAT_16BPP 0x2 -#define NV4_CIO_CRE_PIXEL_FORMAT_32BPP 0x3 -#define NV4_CIO_CRE_PAGE_OVFL_INDEX 0x29 -#define NV4_CIO_CRE_OSCOL_INDEX 0x2A -#define NV4_CIO_CRE_SCRATCH0_INDEX 0x2B -#define NV4_CIO_CRE_SCRATCH1_INDEX 0x2C -#define NV4_CIO_CRE_HEB_INDEX 0x2D -#define NV4_CIO_CRE_HEB_SA_23 5 -#define NV4_CIO_CRE_HEB_ILC_8 4 -#define NV4_CIO_CRE_HEB_HRS_8 3 -#define NV4_CIO_CRE_HEB_HBS_8 2 -#define NV4_CIO_CRE_HEB_HDE_8 1 -#define NV4_CIO_CRE_HEB_HDT_8 0 -#define NV4_CIO_CRE_HCUR_ADDR2_INDEX 0x2f -#define NV4_CIO_CRE_HCUR_ADDR0_INDEX 0x30 -#define NV4_CIO_CRE_HCUR_ASI 7 -#define NV4_CIO_CRE_HCUR_ASI_FRAMEBUFFER 0x1 -#define NV4_CIO_CRE_HCUR_ASI_INSTMEM 0x0 -#define NV4_CIO_CRE_HCUR_ADDR0_ADR 0 -#define NV4_CIO_CRE_HCUR_ADDR1_INDEX 0x31 -#define NV4_CIO_CRE_HCUR_ADDR1_ADR 2 -#define NV4_CIO_CRE_HCUR_ADDR1_CUR_DBL 1 -#define NV4_CIO_CRE_HCUR_ADDR1_ENABLE 0 -#define NV4_CIO_CRE_VID_END0_INDEX 0x32 -#define NV4_CIO_CRE_VID_END_7_0 0 -#define NV4_CIO_CRE_VID_END1_INDEX 0x33 -#define NV4_CIO_CRE_VID_END_ENABLE 4 -#define NV4_CIO_CRE_VID_END_10_8 0 -#define NV4_CIO_CRE_RL0_INDEX 0x34 -#define NV4_CIO_CRE_RL1_INDEX 0x35 -#define NV4_CIO_CRE_RMA_INDEX 0x38 -#define NV4_CIO_CRE_ILACE_INDEX 0x39 -#define NV4_CIO_CRE_SCRATCH2_INDEX 0x3A -#define NV4_CIO_CRE_SCRATCH3_INDEX 0x3B -#define NV4_CIO_CRE_SCRATCH4_INDEX 0x3C -#define NV4_CIO_CRE_TREG_INDEX 0x3D -#define NV4_CIO_CRE_TREG_HCNT 6 -#define NV4_CIO_CRE_TREG_VCNT 4 -#define NV4_CIO_CRE_TREG_SHADOW 0 -#define NV4_CIO_CRE_TREG_HCNT_INDEX 0x0 -#define NV4_CIO_CRE_TREG_VCNTA_INDEX 0x6 -#define NV4_CIO_CRE_TREG_VCNTB_INDEX 0x7 -#define NV4_CIO_CRE_DDC_STATUS_INDEX 0x3E -#define NV4_CIO_CRE_DDC_WR_INDEX 0x3F // Write to i2c for EDID/DDC -#define NV4_CIO_CRE_PCI_TO_INDEX 0x40 -#define NV4_CIO_CRE_PCI_TO_DELAY 0 - -#define NV4_VIO_MBEN 0x94 -#define NV4_VIO_ADDEN 0x46e8 -#define NV4_VIO_VSE1 0x102 -#define NV4_VIO_VSE2 0x3c3 -#define NV4_VIO_MISC_READ 0x3cc -#define NV4_VIO_MISC_WRITE 0x3c2 -#define NV4_VIO_SRX 0x3c4 -#define NV4_VIO_SR 0x3c5 -#define NV4_VIO_SR_RESET_INDEX 0x0 -#define NV4_VIO_SR_CLOCK_INDEX 0x1 -#define NV4_VIO_SR_PLANE_MASK_INDEX 0x2 -#define NV4_VIO_SR_CHAR_MAP_INDEX 0x3 -#define NV4_VIO_SR_MEM_MODE_INDEX 0x4 -#define NV4_VIO_GRX 0x3ce -#define NV4_VIO_GX_SR 0x3cf -#define NV4_VIO_GX_SR_INDEX 0x0 -#define NV4_VIO_GX_SREN_INDEX 0x1 -#define NV4_VIO_GX_CCOMP_INDEX 0x2 -#define NV4_VIO_GX_ROP_INDEX 0x3 -#define NV4_VIO_GX_READ_MAP_INDEX 0x4 -#define NV4_VIO_GX_MODE_INDEX 0x5 -#define NV4_VIO_GX_MISC_INDEX 0x6 -#define NV4_VIO_GX_DONT_CARE_INDEX 0x7 -#define NV4_VIO_GX_BIT_MASK_INDEX 0x8 - -#define NV4_PRMVIO_START 0xC0000 -#define NV4_PRMVIO_END 0xC7FFF - -#define NV4_PRMVIO_MBEN 0xC0094 -#define NV4_PRMVIO_ADDEN 0xC46e8 -#define NV4_PRMVIO_VSE1 0xC0102 -#define NV4_PRMVIO_VSE2 0xC03c3 -#define NV4_PRMVIO_MISC_READ 0xC03cc -#define NV4_PRMVIO_MISC_WRITE 0xC03c2 -#define NV4_PRMVIO_SRX 0xC03c4 -#define NV4_PRMVIO_SR_RESET 0xC03c5 -#define NV4_PRMVIO_SR_RESET_INDEX 0x0 -#define NV4_PRMVIO_SR_CLOCK_INDEX 0x1 -#define NV4_PRMVIO_SR_PLANE_MASK_INDEX 0x2 -#define NV4_PRMVIO_SR_CHAR_MAP_INDEX 0x3 -#define NV4_PRMVIO_SR_MEM_MODE_INDEX 0x4 -#define NV4_PRMVIO_GX_SR 0xC03cf -#define NV4_PRMVIO_GX_SR_INDEX 0x0 -#define NV4_PRMVIO_GX_SREN_INDEX 0x1 -#define NV4_PRMVIO_GX_CCOMP_INDEX 0x2 -#define NV4_PRMVIO_GX_ROP_INDEX 0x3 -#define NV4_PRMVIO_GX_READ_MAP_INDEX 0x4 -#define NV4_PRMVIO_GX_MODE_INDEX 0x5 -#define NV4_PRMVIO_GX_MISC_INDEX 0x6 -#define NV4_PRMVIO_GX_MISC_BANKED_128K_A0000 0x00 -#define NV4_PRMVIO_GX_MISC_BANKED_64K_A0000 0x04 -#define NV4_PRMVIO_GX_MISC_BANKED_32K_B0000 0x08 -#define NV4_PRMVIO_GX_MISC_BANKED_32K_B8000 0x0C -#define NV4_PRMVIO_GX_DONT_CARE_INDEX 0x7 -#define NV4_PRMVIO_GX_BIT_MASK_INDEX 0x8 - -#define NV4_PRMVGA 0xA0000 -#define NV4_PRMVGA_END 0xBFFFF - -#define NV4_PME 0x200FFF:0x200000 -#define NV4_PME_DEBUG_0 0x200080 -#define NV4_PME_DEBUG_0_DET_FIELD_SWITCH 0 -#define NV4_PME_DEBUG_0_DET_FIELD_SWITCH_ENABLED 0x1 -#define NV4_PME_DEBUG_0_CAPTURE_00_FF 4 -#define NV4_PME_DEBUG_0_CAPTURE_00_FF_ENABLED 0x1 -#define NV4_PME_DEBUG_1 0x200084 -#define NV4_PME_DEBUG_1_SEL 0 -#define NV4_PME_DEBUG_1_SEL_VIPCLK 0x0 -#define NV4_PME_DEBUG_1_SEL_MCLK 0x1 -#define NV4_PME_DEBUG_1_SEL_GLOB 0x2 -#define NV4_PME_DEBUG_1_VIPCLK_SEL 4 -#define NV4_PME_DEBUG_1_VIPCLK_SEL_DEFAULT 0x0 -#define NV4_PME_DEBUG_1_MCLK_SEL 8 -#define NV4_PME_DEBUG_1_MCLK_SEL_DEFAULT 0x0 -#define NV4_PME_INTR_0 0x200100 -#define NV4_PME_INTR_0_IMAGE_NOTIFY 0 -#define NV4_PME_INTR_0_IMAGE_NOTIFY_NOT_PENDING 0x0 -#define NV4_PME_INTR_0_IMAGE_NOTIFY_PENDING 0x1 -#define NV4_PME_INTR_0_IMAGE_NOTIFY_RESET 0x1 -#define NV4_PME_INTR_0_VBI_NOTIFY 4 -#define NV4_PME_INTR_0_VBI_NOTIFY_NOT_PENDING 0x0 -#define NV4_PME_INTR_0_VBI_NOTIFY_PENDING 0x1 -#define NV4_PME_INTR_0_VBI_NOTIFY_RESET 0x1 -#define NV4_PME_INTR_0_VID_NOTIFY 8 -#define NV4_PME_INTR_0_VID_NOTIFY_NOT_PENDING 0x0 -#define NV4_PME_INTR_0_VID_NOTIFY_PENDING 0x1 -#define NV4_PME_INTR_0_VID_NOTIFY_RESET 0x1 -#define NV4_PME_INTR_0_AUD_NOTIFY 12 -#define NV4_PME_INTR_0_AUD_NOTIFY_NOT_PENDING 0x0 -#define NV4_PME_INTR_0_AUD_NOTIFY_PENDING 0x1 -#define NV4_PME_INTR_0_AUD_NOTIFY_RESET 0x1 -#define NV4_PME_INTR_0_VMI 16 -#define NV4_PME_INTR_0_VMI_NOT_PENDING 0x0 -#define NV4_PME_INTR_0_VMI_PENDING 0x1 -#define NV4_PME_INTR_0_VMI_RESET 0x1 -#define NV4_PME_INTR_EN_0 0x200140 -#define NV4_PME_INTR_EN_0_IMAGE_NOTIFY 0 -#define NV4_PME_INTR_EN_0_IMAGE_NOTIFY_ENABLED 0x1 -#define NV4_PME_INTR_EN_0_VBI_NOTIFY 4 -#define NV4_PME_INTR_EN_0_VBI_NOTIFY_ENABLED 0x1 -#define NV4_PME_INTR_EN_0_VID_NOTIFY 8 -#define NV4_PME_INTR_EN_0_VID_NOTIFY_ENABLED 0x1 -#define NV4_PME_INTR_EN_0_AUD_NOTIFY 12 -#define NV4_PME_INTR_EN_0_AUD_NOTIFY_ENABLED 0x1 -#define NV4_PME_INTR_EN_0_VMI 16 -#define NV4_PME_INTR_EN_0_VMI_ENABLED 0x1 -#define NV4_PME_CONFIG_0 0x200200 -#define NV4_PME_CONFIG_0_BUS_MODE 0 -#define NV4_PME_CONFIG_0_BUS_MODE_DISABLED 0x0 -#define NV4_PME_CONFIG_0_BUS_MODE_VMI 0x1 -#define NV4_PME_CONFIG_0_BUS_MODE_CCIR656 0x2 -#define NV4_PME_CONFIG_0_IMAGE 4 -#define NV4_PME_CONFIG_0_IMAGE_ENABLED 0x1 -#define NV4_PME_CONFIG_0_VBI_MODE 8 -#define NV4_PME_CONFIG_0_VBI_MODE_DISABLED 0x0 -#define NV4_PME_CONFIG_0_VBI_MODE_1 0x1 -#define NV4_PME_CONFIG_0_VBI_MODE_2 0x2 -#define NV4_PME_CONFIG_0_VID_CD 12 -#define NV4_PME_CONFIG_0_VID_CD_ENABLED 0x1 -#define NV4_PME_CONFIG_0_AUD_CD 16 -#define NV4_PME_CONFIG_0_AUD_CD_ENABLED 0x1 -#define NV4_PME_CONFIG_1 0x200204 -#define NV4_PME_CONFIG_1_BUFFS 0 -#define NV4_PME_CONFIG_1_BUFFS_PNVM 0x0 -#define NV4_PME_CONFIG_1_BUFFS_SYS 0x1 -#define NV4_PME_CONFIG_1_HOST 4 -#define NV4_PME_CONFIG_1_HOST_PCI 0x0 -#define NV4_PME_CONFIG_1_HOST_AGP 0x1 -#define NV4_PME_NULL_DATA 0x200208 -#define NV4_PME_NULL_DATA_COMPARE 0 -#define NV4_PME_NULL_DATA_COMPARE_ENABLED 0x1 -#define NV4_PME_NULL_DATA_LINE_DETECT 4 -#define NV4_PME_NULL_DATA_LINE_DETECT_ENABLED 0x1 -#define NV4_PME_NULL_DATA_BYTE 24 -#define NV4_PME_VID_BUFF0_START_SYS 0x200300 -#define NV4_PME_VID_BUFF0_START_SYS_ADDRESS 4 -#define NV4_PME_VID_BUFF1_START_SYS 0x200304 -#define NV4_PME_VID_BUFF1_START_SYS_ADDRESS 4 -#define NV4_PME_VID_BUFF0_START_PNVM 0x200308 -#define NV4_PME_VID_BUFF0_START_PNVM_ADDRESS 4 -#define NV4_PME_VID_BUFF1_START_PNVM 0x20030c -#define NV4_PME_VID_BUFF1_START_PNVM_ADDRESS 4 -#define NV4_PME_VID_BUFF0_LENGTH 0x200310 -#define NV4_PME_VID_BUFF0_LENGTH_BITS 12 -#define NV4_PME_VID_BUFF1_LENGTH 0x200314 -#define NV4_PME_VID_BUFF1_LENGTH_BITS 12 -#define NV4_PME_VID_ME_STATE 0x200318 -#define NV4_PME_VID_ME_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_VID_ME_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_VID_ME_STATE_BUFF0_INTR_CHAINGAP 8 -#define NV4_PME_VID_ME_STATE_BUFF1_INTR_CHAINGAP 12 -#define NV4_PME_VID_ME_STATE_BUFF0_IN_USE 16 -#define NV4_PME_VID_ME_STATE_BUFF1_IN_USE 20 -#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER 24 -#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER_0 0x0 -#define NV4_PME_VID_ME_STATE_CURRENT_BUFFER_1 0x1 -#define NV4_PME_VID_SU_STATE 0x20031c -#define NV4_PME_VID_SU_STATE_BUFF0_IN_USE 16 -#define NV4_PME_VID_SU_STATE_BUFF1_IN_USE 20 -#define NV4_PME_VID_RM_STATE 0x200320 -#define NV4_PME_VID_RM_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_VID_RM_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_VID_RM_STATE_BUFF0_INTR_CHAINGAP 8 -#define NV4_PME_VID_RM_STATE_BUFF1_INTR_CHAINGAP 12 -#define NV4_PME_VID_CURRENT 0x200324 -#define NV4_PME_VID_CURRENT_POS 2 -#define NV4_PME_AUD_BUFF0_START_SYS 0x200340 -#define NV4_PME_AUD_BUFF0_START_SYS_ADDRESS 4 -#define NV4_PME_AUD_BUFF1_START_SYS 0x200344 -#define NV4_PME_AUD_BUFF1_START_SYS_ADDRESS 4 -#define NV4_PME_AUD_BUFF0_START_PNVM 0x200348 -#define NV4_PME_AUD_BUFF0_START_PNVM_ADDRESS 4 -#define NV4_PME_AUD_BUFF1_START_PNVM 0x20034c -#define NV4_PME_AUD_BUFF1_START_PNVM_ADDRESS 4 -#define NV4_PME_AUD_BUFF0_LENGTH 0x200350 -#define NV4_PME_AUD_BUFF0_LENGTH_BITS 10 -#define NV4_PME_AUD_BUFF1_LENGTH 0x200354 -#define NV4_PME_AUD_BUFF1_LENGTH_BITS 10 -#define NV4_PME_AUD_ME_STATE 0x200358 -#define NV4_PME_AUD_ME_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_AUD_ME_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_AUD_ME_STATE_BUFF0_INTR_CHAINGAP 8 -#define NV4_PME_AUD_ME_STATE_BUFF1_INTR_CHAINGAP 12 -#define NV4_PME_AUD_ME_STATE_BUFF0_IN_USE 16 -#define NV4_PME_AUD_ME_STATE_BUFF1_IN_USE 20 -#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER 24 -#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER_0 0x0 -#define NV4_PME_AUD_ME_STATE_CURRENT_BUFFER_1 0x1 -#define NV4_PME_AUD_SU_STATE 0x20035c -#define NV4_PME_AUD_SU_STATE_BUFF0_IN_USE 16 -#define NV4_PME_AUD_SU_STATE_BUFF1_IN_USE 20 -#define NV4_PME_AUD_RM_STATE 0x200360 -#define NV4_PME_AUD_RM_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_AUD_RM_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_AUD_RM_STATE_BUFF0_INTR_CHAINGAP 8 -#define NV4_PME_AUD_RM_STATE_BUFF1_INTR_CHAINGAP 12 -#define NV4_PME_AUD_CURRENT 0x200364 -#define NV4_PME_AUD_CURRENT_POS 2 -#define NV4_PME_VBI_BUFF0_START 0x200380 -#define NV4_PME_VBI_BUFF0_START_ADDRESS 4 -#define NV4_PME_VBI_BUFF1_START 0x200384 -#define NV4_PME_VBI_BUFF1_START_ADDRESS 4 -#define NV4_PME_VBI_BUFF0_PITCH 0x200388 -#define NV4_PME_VBI_BUFF0_PITCH_VALUE 4 -#define NV4_PME_VBI_BUFF1_PITCH 0x20038c -#define NV4_PME_VBI_BUFF1_PITCH_VALUE 4 -#define NV4_PME_VBI_BUFF0_LENGTH 0x200390 -#define NV4_PME_VBI_BUFF0_LENGTH_BITS 4 -#define NV4_PME_VBI_BUFF1_LENGTH 0x200394 -#define NV4_PME_VBI_BUFF1_LENGTH_BITS 4 -#define NV4_PME_VBI_ME_STATE 0x200398 -#define NV4_PME_VBI_ME_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_VBI_ME_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_VBI_ME_STATE_BUFF0_ERROR_CODE 8 -#define NV4_PME_VBI_ME_STATE_BUFF1_ERROR_CODE 12 -#define NV4_PME_VBI_ME_STATE_BUFF0_IN_USE 16 -#define NV4_PME_VBI_ME_STATE_BUFF1_IN_USE 20 -#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER 24 -#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER_0 0x0 -#define NV4_PME_VBI_ME_STATE_CURRENT_BUFFER_1 0x1 -#define NV4_PME_VBI_SU_STATE 0x20039c -#define NV4_PME_VBI_SU_STATE_BUFF0_FIELD 8 -#define NV4_PME_VBI_SU_STATE_BUFF1_FIELD 12 -#define NV4_PME_VBI_SU_STATE_BUFF0_IN_USE 16 -#define NV4_PME_VBI_SU_STATE_BUFF1_IN_USE 20 -#define NV4_PME_VBI_RM_STATE 0x2003a0 -#define NV4_PME_VBI_RM_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_VBI_RM_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_VBI 0x2003a4 -#define NV4_PME_VBI_START_LINE 0 -#define NV4_PME_VBI_NUM_LINES 16 -#define NV4_PME_IMAGE_BUFF0_START 0x200400 -#define NV4_PME_IMAGE_BUFF0_START_ADDRESS 4 -#define NV4_PME_IMAGE_BUFF1_START 0x200404 -#define NV4_PME_IMAGE_BUFF1_START_ADDRESS 4 -#define NV4_PME_IMAGE_BUFF0_PITCH 0x200408 -#define NV4_PME_IMAGE_BUFF0_PITCH_VALUE 4 -#define NV4_PME_IMAGE_BUFF1_PITCH 0x20040c -#define NV4_PME_IMAGE_BUFF1_PITCH_VALUE 4 -#define NV4_PME_IMAGE_BUFF0_LENGTH 0x200410 -#define NV4_PME_IMAGE_BUFF0_LENGTH_BITS 4 -#define NV4_PME_IMAGE_BUFF1_LENGTH 0x200414 -#define NV4_PME_IMAGE_BUFF1_LENGTH_BITS 4 -#define NV4_PME_IMAGE_ME_STATE 0x200418 -#define NV4_PME_IMAGE_ME_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_IMAGE_ME_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_IMAGE_ME_STATE_BUFF0_ERROR_CODE 8 -#define NV4_PME_IMAGE_ME_STATE_BUFF1_ERROR_CODE 12 -#define NV4_PME_IMAGE_ME_STATE_BUFF0_IN_USE 16 -#define NV4_PME_IMAGE_ME_STATE_BUFF1_IN_USE 20 -#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER 24 -#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER_0 0x0 -#define NV4_PME_IMAGE_ME_STATE_CURRENT_BUFFER_1 0x1 -#define NV4_PME_IMAGE_SU_STATE 0x20041c -#define NV4_PME_IMAGE_SU_STATE_BUFF0_FIELD 8 -#define NV4_PME_IMAGE_SU_STATE_BUFF1_FIELD 12 -#define NV4_PME_IMAGE_SU_STATE_BUFF0_IN_USE 16 -#define NV4_PME_IMAGE_SU_STATE_BUFF1_IN_USE 20 -#define NV4_PME_IMAGE_RM_STATE 0x200420 -#define NV4_PME_IMAGE_RM_STATE_BUFF0_INTR_NOTIFY 0 -#define NV4_PME_IMAGE_RM_STATE_BUFF1_INTR_NOTIFY 4 -#define NV4_PME_IMAGE_BUFF0_SCALE_INCR 0x200424 -#define NV4_PME_IMAGE_BUFF0_SCALE_INCR_Y 16 -#define NV4_PME_IMAGE_BUFF0_SCALE_INCR_X 0 -#define NV4_PME_IMAGE_BUFF1_SCALE_INCR 0x200428 -#define NV4_PME_IMAGE_BUFF1_SCALE_INCR_Y 16 -#define NV4_PME_IMAGE_BUFF1_SCALE_INCR_X 0 -#define NV4_PME_IMAGE_Y_CROP 0x20042c -#define NV4_PME_IMAGE_Y_CROP_STARTLINE 0 -#define NV4_PME_FIFO_LINE_START 0x200480 -#define NV4_PME_FIFO_LINE_START_ADDRESS 4 -#define NV4_PME_FIFO_CURRENT 0x200484 -#define NV4_PME_FIFO_CURRENT_ADDRESS 2 -#define NV4_PME_VMI_POLL 0x200488 -#define NV4_PME_VMI_POLL_UNCD 0 -#define NV4_PME_VMI_POLL_UNCD_NOT_PENDING 0x0 -#define NV4_PME_VMI_POLL_UNCD_PENDING 0x1 -#define NV4_PME_VMI_POLL_VIDCD 1 -#define NV4_PME_VMI_POLL_VIDCD_NOT_PENDING 0x0 -#define NV4_PME_VMI_POLL_VIDCD_PENDING 0x1 -#define NV4_PME_VMI_POLL_AUDCD 2 -#define NV4_PME_VMI_POLL_AUDCD_NOT_PENDING 0x0 -#define NV4_PME_VMI_POLL_AUDCD_PENDING 0x1 -#define NV4_PME_VMI_POLL_INT 3 -#define NV4_PME_VMI_POLL_INT_NOT_PENDING 0x0 -#define NV4_PME_VMI_POLL_INT_PENDING 0x1 -#define NV4_PME_VMI_POLL_CPURDREC 4 -#define NV4_PME_VMI_POLL_CPURDREC_NOT_PENDING 0x0 -#define NV4_PME_VMI_POLL_CPURDREC_PENDING 0x1 -#define NV4_PME_EXTERNAL(i) (0x200600+(i)*4) -#define NV4_PME_EXTERNAL_SIZE_1 256 -#define NV4_PME_EXTERNAL_DATA 0 - -#define NV4_PFB_START 0x100000 -#define NV4_PFB_END 0x100FFF - -#define NV4_PFB_BOOT_0 0x100000 -#define NV4_PFB_BOOT_0_RAM_AMOUNT 0 -#define NV4_PFB_BOOT_0_RAM_AMOUNT_2MB 0x0 -#define NV4_PFB_BOOT_0_RAM_AMOUNT_4MB 0x1 -#define NV4_PFB_BOOT_0_RAM_AMOUNT_8MB 0x2 -#define NV4_PFB_BOOT_0_RAM_AMOUNT_16MB 0x3 -#define NV4_PFB_BOOT_0_RAM_WIDTH_128 2 -#define NV4_PFB_BOOT_0_RAM_WIDTH_128_OFF 0x0 -#define NV4_PFB_BOOT_0_RAM_WIDTH_128_ON 0x1 -#define NV4_PFB_BOOT_0_RAM_TYPE 3 -#define NV4_PFB_BOOT_0_RAM_TYPE_256K 0x0 -#define NV4_PFB_BOOT_0_RAM_TYPE_512K_2BANK 0x1 -#define NV4_PFB_BOOT_0_RAM_TYPE_512K_4BANK 0x2 -#define NV4_PFB_BOOT_0_RAM_TYPE_1024K_2BANK 0x3 -#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x0 -#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x1 -#define NV4_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x2 -#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x3 -#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x4 -#define NV4_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x5 -#define NV4_PFB_BOOT_0_UMA 8 -#define NV4_PFB_BOOT_0_UMA_DISABLE 0x0 -#define NV4_PFB_BOOT_0_UMA_ENABLE 0x1 -#define NV4_PFB_BOOT_0_UMA_SIZE 12 -#define NV4_PFB_BOOT_0_UMA_SIZE_DEFAULT 0x7 -#define NV4_PFB_BOOT_0_UMA_SIZE_2M 0x0 -#define NV4_PFB_BOOT_0_UMA_SIZE_4M 0x1 -#define NV4_PFB_BOOT_0_UMA_SIZE_6M 0x2 -#define NV4_PFB_BOOT_0_UMA_SIZE_8M 0x3 -#define NV4_PFB_BOOT_0_UMA_SIZE_10M 0x4 -#define NV4_PFB_BOOT_0_UMA_SIZE_12M 0x5 -#define NV4_PFB_BOOT_0_UMA_SIZE_14M 0x6 -#define NV4_PFB_BOOT_0_UMA_SIZE_16M 0x7 -#define NV4_PFB_BOOT_0_UMA_SIZE_18M 0x8 -#define NV4_PFB_BOOT_0_UMA_SIZE_20M 0x9 -#define NV4_PFB_BOOT_0_UMA_SIZE_22M 0xA -#define NV4_PFB_BOOT_0_UMA_SIZE_24M 0xB -#define NV4_PFB_BOOT_0_UMA_SIZE_26M 0xC -#define NV4_PFB_BOOT_0_UMA_SIZE_28M 0xD -#define NV4_PFB_BOOT_0_UMA_SIZE_30M 0xE -#define NV4_PFB_BOOT_0_UMA_SIZE_32M 0xF -#define NV4_PFB_DEBUG_0 0x100080 -#define NV4_PFB_DEBUG_0_PAGE_MODE 0 -#define NV4_PFB_DEBUG_0_PAGE_MODE_ENABLED 0x0 -#define NV4_PFB_DEBUG_0_PAGE_MODE_DISABLED 0x1 -#define NV4_PFB_DEBUG_0_REFRESH 4 -#define NV4_PFB_DEBUG_0_REFRESH_ENABLED 0x0 -#define NV4_PFB_DEBUG_0_REFRESH_DISABLED 0x1 -#define NV4_PFB_DEBUG_0_REFRESH_COUNTX64 8 -#define NV4_PFB_DEBUG_0_REFRESH_COUNTX64_DEFAULT 0x10 -#define NV4_PFB_DEBUG_0_REFRESH_SLOW_CLK 14 -#define NV4_PFB_DEBUG_0_REFRESH_SLOW_CLK_ENABLED 0x1 -#define NV4_PFB_DEBUG_0_CASOE 20 -#define NV4_PFB_DEBUG_0_CASOE_ENABLED 0x0 -#define NV4_PFB_DEBUG_0_CASOE_DISABLED 0x1 -#define NV4_PFB_DEBUG_0_CKE_INVERT 28 -#define NV4_PFB_DEBUG_0_CKE_INVERT_OFF 0x0 -#define NV4_PFB_DEBUG_0_CKE_INVERT_ON 0x1 -#define NV4_PFB_DEBUG_0_REFINC 29 -#define NV4_PFB_DEBUG_0_REFINC_DISABLED 0x0 -#define NV4_PFB_DEBUG_0_REFINC_ENABLED 0x1 -#define NV4_PFB_DEBUG_0_SAVE_POWER 30 -#define NV4_PFB_DEBUG_0_SAVE_POWER_ON 0x0 -#define NV4_PFB_DEBUG_0_SAVE_POWER_OFF 0x1 -#define NV4_PFB_GREEN_0 0x1000C0 -#define NV4_PFB_GREEN_0_LEVEL 0 -#define NV4_PFB_GREEN_0_LEVEL_VIDEO_ENABLED 0x0 -#define NV4_PFB_GREEN_0_LEVEL_VIDEO_DISABLED 0x1 -#define NV4_PFB_GREEN_0_LEVEL_TIMING_DISABLED 0x2 -#define NV4_PFB_GREEN_0_LEVEL_MEMORY_DISABLED 0x3 -#define NV4_PFB_CONFIG_0 0x100200 -#define NV4_PFB_CONFIG_0_TYPE 0 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_8BPP 0x120 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_16BPP 0x220 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_FIXED_32BPP 0x320 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_8BPP 0x4120 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_16BPP 0x4220 -#define NV4_PFB_CONFIG_0_TYPE_OLD1024_VAR_32BPP 0x4320 -#define NV4_PFB_CONFIG_0_TYPE_TETRIS 0x2000 -#define NV4_PFB_CONFIG_0_TYPE_NOTILING 0x1114 -#define NV4_PFB_CONFIG_0_TETRIS_MODE 15 -// 18:15 = tetris mode # -#define NV4_PFB_CONFIG_0_TETRIS_MODE_PASS 0x0 -// 18 = shift # -#define NV4_PFB_CONFIG_0_TETRIS_SHIFT 18 -#define NV4_PFB_CONFIG_0_BANK_SWAP 20 -#define NV4_PFB_CONFIG_0_BANK_SWAP_OFF 0x0 -#define NV4_PFB_CONFIG_0_BANK_SWAP_1M 0x1 -#define NV4_PFB_CONFIG_0_BANK_SWAP_2M 0x5 -#define NV4_PFB_CONFIG_0_BANK_SWAP_4M 0x7 -#define NV4_PFB_CONFIG_0_UNUSED 23 -#define NV4_PFB_CONFIG_0_SCRAMBLE_EN 29 -#define NV4_PFB_CONFIG_0_SCRAMBLE_EN_INIT 0x0 -#define NV4_PFB_CONFIG_0_SCRAMBLE_ACTIVE 0x1 -#define NV4_PFB_CONFIG_0_PRAMIN_WR 28 -#define NV4_PFB_CONFIG_0_PRAMIN_WR_INIT 0x0 -#define NV4_PFB_CONFIG_0_PRAMIN_WR_DISABLED 0x1 -#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK 24 -#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK_INIT 0x0 -#define NV4_PFB_CONFIG_0_PRAMIN_WR_MASK_CLEAR 0xF -#define NV4_PFB_CONFIG_1 0x100204 -#define NV4_PFB_CONFIG_1_CAS_LATENCY 0 -#define NV4_PFB_CONFIG_1_CAS_LATENCY_3 0x3 -#define NV4_PFB_CONFIG_1_CAS_LATENCY_2 0x2 -#define NV4_PFB_CONFIG_1_CAS_LATENCY_4 0x4 -#define NV4_PFB_CONFIG_1_RAS_RAS 4 -#define NV4_PFB_CONFIG_1_RAS_RAS_DEFAULT 0x9 -#define NV4_PFB_CONFIG_1_RAS_RAS_9CYCLES 0x8 -#define NV4_PFB_CONFIG_1_RAS_RAS_8CYCLES 0x7 -#define NV4_PFB_CONFIG_1_RAS_RAS_7CYCLES 0x6 -#define NV4_PFB_CONFIG_1_RAS_PCHG 8 -#define NV4_PFB_CONFIG_1_RAS_PCHG_DEFAULT 0x2 -#define NV4_PFB_CONFIG_1_RAS_PCHG_2CYCLES 0x1 -#define NV4_PFB_CONFIG_1_RAS_LOW 12 -#define NV4_PFB_CONFIG_1_RAS_LOW_DEFAULT 0x6 -#define NV4_PFB_CONFIG_1_RAS_LOW_7CYCLES 0x7 -#define NV4_PFB_CONFIG_1_RAS_LOW_5CYCLES 0x5 -#define NV4_PFB_CONFIG_1_RAS_LOW_4CYCLES 0x4 -#define NV4_PFB_CONFIG_1_MRS_TO_RAS 16 -#define NV4_PFB_CONFIG_1_MRS_TO_RAS_DEFAULT 0x1 -#define NV4_PFB_CONFIG_1_MRS_TO_RAS_2CYCLES 0x2 -#define NV4_PFB_CONFIG_1_MRS_TO_RAS_0CYCLES 0x0 -#define NV4_PFB_CONFIG_1_WRITE_TO_READ 20 -#define NV4_PFB_CONFIG_1_WRITE_TO_READ_DEFAULT 0x0 -#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1 24 -#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_DEFAULT 0x1 -#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_2CYCLES 0x2 -#define NV4_PFB_CONFIG_1_RAS_TO_CAS_M1_0CYCLES 0x0 -#define NV4_PFB_CONFIG_1_READ_TO_WRITE 28 -#define NV4_PFB_CONFIG_1_READ_TO_WRITE_DEFAULT 0x4 -#define NV4_PFB_CONFIG_1_READ_TO_WRITE_5CYCLES 0x5 -#define NV4_PFB_CONFIG_1_READ_TO_WRITE_3CYCLES 0x3 -#define NV4_PFB_CONFIG_1_READ_TO_WRITE_2CYCLES 0x2 -#define NV4_PFB_CONFIG_1_READ_TO_PCHG 31 -#define NV4_PFB_CONFIG_1_READ_TO_PCHG_ON 0x1 -#define NV4_PFB_CONFIG_1_READ_TO_PCHG_OFF 0x0 -#define NV4_PFB_RTL 0x100300 -#define NV4_PFB_RTL_H 0 -#define NV4_PFB_RTL_H_DEFAULT 0x0 -#define NV4_PFB_RTL_MC 1 -#define NV4_PFB_RTL_MC_DEFAULT 0x0 -#define NV4_PFB_RTL_V 2 -#define NV4_PFB_RTL_V_DEFAULT 0x0 -#define NV4_PFB_RTL_G 3 -#define NV4_PFB_RTL_G_DEFAULT 0x0 -#define NV4_PFB_RTL_GB 4 -#define NV4_PFB_RTL_GB_DEFAULT 0x0 -#define NV4_PFB_SCRAMBLE(i) (0x100400+((i)*4)) -#define NV4_PFB_SCRAMBLE_SIZE_1 8 -#define NV4_PFB_SCRAMBLE_w0 0 -#define NV4_PFB_SCRAMBLE_w1 8 -#define NV4_PFB_SCRAMBLE_w2 16 -#define NV4_PFB_SCRAMBLE_w3 24 -#define NV4_PFB_SCRAMBLE_EN 0x100420 -#define NV4_PFB_SCRAMBLE_VALUE_0 0x03020100 -#define NV4_PFB_SCRAMBLE_VALUE_1 0x07060504 -#define NV4_PFB_SCRAMBLE_VALUE_2 0x0b0a0908 -#define NV4_PFB_SCRAMBLE_VALUE_3 0x0f0e0d0c -#define NV4_PFB_SCRAMBLE_VALUE_4 0x13121110 -#define NV4_PFB_SCRAMBLE_VALUE_5 0x17161514 -#define NV4_PFB_SCRAMBLE_VALUE_6 0x1b1a1918 -#define NV4_PFB_SCRAMBLE_VALUE_7 0x1f1e1d1c -#define NV4_PFB_CONFIG_0_RESOLUTION 0 -#define NV4_PFB_CONFIG_0_RESOLUTION_320_PIXELS 0xA -#define NV4_PFB_CONFIG_0_RESOLUTION_400_PIXELS 0xD -#define NV4_PFB_CONFIG_0_RESOLUTION_480_PIXELS 0xF -#define NV4_PFB_CONFIG_0_RESOLUTION_512_PIXELS 0x10 -#define NV4_PFB_CONFIG_0_RESOLUTION_640_PIXELS 0x14 -#define NV4_PFB_CONFIG_0_RESOLUTION_800_PIXELS 0x19 -#define NV4_PFB_CONFIG_0_RESOLUTION_960_PIXELS 0x1e -#define NV4_PFB_CONFIG_0_RESOLUTION_1024_PIXELS 0x20 -#define NV4_PFB_CONFIG_0_RESOLUTION_1152_PIXELS 0x24 -#define NV4_PFB_CONFIG_0_RESOLUTION_1280_PIXELS 0x28 -#define NV4_PFB_CONFIG_0_RESOLUTION_1600_PIXELS 0x32 -#define NV4_PFB_CONFIG_0_RESOLUTION_DEFAULT 0x14 -#define NV4_PFB_CONFIG_0_PIXEL_DEPTH 8 -#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_8_BITS 0x1 -#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_16_BITS 0x2 -#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_32_BITS 0x3 -#define NV4_PFB_CONFIG_0_PIXEL_DEPTH_DEFAULT 0x1 -#define NV4_PFB_CONFIG_0_TILING 12 -#define NV4_PFB_CONFIG_0_TILING_ENABLED 0x0 -#define NV4_PFB_CONFIG_0_TILING_DISABLED 0x1 -#define NV4_PFB_CONFIG_0_TILING_DEBUG 13 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_DISABLED 0x0 -#define NV4_PFB_CONFIG_0_TILE 12 -#define NV4_PFB_CONFIG_0_TILE_OLD1024_FIXED 0x0 -#define NV4_PFB_CONFIG_0_TILE_OLD1024_VARIABLE 0x4 -#define NV4_PFB_CONFIG_0_TILE_TETRIS_ALLOW 0x1 -#define NV4_PFB_CONFIG_0_TILE_TETRIS_REDUNDANT 0x2 -#define NV4_PFB_CONFIG_0_TILE_TETRIS_REDUNDANT2 0x3 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON 13 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON_ENABLED 0x0 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_ON_DISABLED 0x1 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE 14 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE_FIXED 0x0 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TILESIZE_VARIABLE 0x1 -// tetris mode # = 17:15 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_MODE 15 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_MODE_PASS 0x0 -// tetris shift # = 18 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_TETRIS_SHIFT 18 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP 20 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_OFF 0x0 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_ON 0x1 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB 21 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_1M 0x0 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_2M 0x2 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_BANK_SWAP_MSB_4M 0x3 -#define NV4_PFB_CONFIG_0_TILING_DEBUG_UNUSED 23 -#define NV4_PFB_CONFIG_1_SGRAM100 3 -#define NV4_PFB_CONFIG_1_SGRAM100_ENABLED 0x0 -#define NV4_PFB_CONFIG_1_SGRAM100_DISABLED 0x1 -#define NV4_PFB_DEBUG_0_CKE_ALWAYSON 29 -#define NV4_PFB_DEBUG_0_CKE_ALWAYSON_OFF 0x0 -#define NV4_PFB_DEBUG_0_CKE_ALWAYSON_ON 0x1 - -// WARNING! WARNING! WARNING! -// STB V4400 has shown PROM at 0x700000 instead of 0x300000 (errata), clashing with first 0x10000 of instance memory -// Nvidia never ran into this issue because they never used that area -// But this part may not work -#define NV4_PRAMIN_START 0x700000 -#define NV4_PRAMIN_END 0x7FFFFF -#define NV4_PRAMIN_SIZE 0xFFFFF - -#define NV4_PRAMIN_CONTEXT_0 ( 0*32+31):( 0*32+ 0) -#define NV4_PRAMIN_CONTEXT_1 ( 1*32+31):( 1*32+ 0) -#define NV4_PRAMIN_CONTEXT_2 ( 2*32+31):( 2*32+ 0) -#define NV4_PRAMIN_CONTEXT_3 ( 3*32+31):( 3*32+ 0) -#define NV4_PRAMIN_RAMHT_START 0x710000 -#define NV4_PRAMIN_RAMHT_END 0x710FFF -#define NV4_PRAMIN_RAMFC_START 0x711000 -#define NV4_PRAMIN_RAMFC_END 0x7111FF -#define NV4_PRAMIN_RAMRO_START 0x711200 -#define NV4_PRAMIN_RAMRO_END 0x7113FF -#define NV4_PRAMIN_CTX_0(i) (0x700000 + (i)*16) -// This is the same format as PGRAPH_CTX_SWITCH -#define NV4_PRAMIN_CTX_0_SIZE_1 0x10000 -#define NV4_PRAMIN_CTX_0_NVCLASS 0 -#define NV4_PRAMIN_CTX_0_CHROMA_KEY 12 -#define NV4_PRAMIN_CTX_0_CHROMA_KEY_ENABLE 0x1 -#define NV4_PRAMIN_CTX_0_USER_CLIP 13 -#define NV4_PRAMIN_CTX_0_USER_CLIP_ENABLE 0x1 -#define NV4_PRAMIN_CTX_0_SWIZZLE 14 -#define NV4_PRAMIN_CTX_0_SWIZZLE_ENABLE 0x1 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG 17:15 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY_AND 0x0 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_ROP_AND 0x1 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_BLEND_AND 0x2 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY 0x3 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_SRCCOPY_PRE 0x4 -#define NV4_PRAMIN_CTX_0_PATCH_CONFIG_BLEND_PRE 0x5 -#define NV4_PRAMIN_CTX_0_PATCH_STATUS 24 -#define NV4_PRAMIN_CTX_0_PATCH_STATUS_INVALID 0x0 -#define NV4_PRAMIN_CTX_0_PATCH_STATUS_VALID 0x1 -#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE 25 -#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE_INVALID 0x0 -#define NV4_PRAMIN_CTX_0_CONTEXT_SURFACE_VALID 0x1 -#define NV4_PRAMIN_CTX_1(i) (0x700004 + (i)*16) -#define NV4_PRAMIN_CTX_1_SIZE_1 0x10000 -#define NV4_PRAMIN_CTX_1_MONO_FORMAT 0 -#define NV4_PRAMIN_CTX_1_MONO_FORMAT_INVALID 0x00 -#define NV4_PRAMIN_CTX_1_MONO_FORMAT_CGA6_M1 0x01 -#define NV4_PRAMIN_CTX_1_MONO_FORMAT_LE_M1 0x02 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT 8 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_INVALID 0x00 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y8 0x01 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16A8Y8 0x02 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X24Y8 0x03 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A1R5G5B5 0x06 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X1R5G5B5 0x07 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16A1R5G5B5 0x08 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X17R5G5B5 0x09 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_R5G6B5 0x0A -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A16R5G6B5 0x0B -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16R5G6B5 0x0C -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A8R8G8B8 0x0D -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X8R8G8B8 0x0E -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y16 0x0F -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_A16Y16 0x10 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_X16Y16 0x11 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_V8YB8U8YA8 0x12 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_YB8V8YA8U8 0x13 -#define NV4_PRAMIN_CTX_1_COLOR_FORMAT_LE_Y32 0x14 -#define NV4_PRAMIN_CTX_1_NOTIFY_INSTANCE 16 -#define NV4_PRAMIN_CTX_1_NOTIFY_INSTANCE_INVALID 0x00 -#define NV4_PRAMIN_CTX_2(i) (0x700008 + (i)*16) -#define NV4_PRAMIN_CTX_2_SIZE_1 0x10000 -#define NV4_PRAMIN_CTX_2_DMA_0_INSTANCE 0 -#define NV4_PRAMIN_CTX_2_DMA_0_INSTANCE_INVALID 0x00 -#define NV4_PRAMIN_CTX_2_DMA_1_INSTANCE 16 -#define NV4_PRAMIN_CTX_2_DMA_1_INSTANCE_INVALID 0x00 - -#define NV4_FIFO_DMA_OPCODE ( 0*32+31):( 0*32+29) -#define NV4_FIFO_DMA_OPCODE_METHOD 0x0 -#define NV4_FIFO_DMA_OPCODE_JUMP 0x1 -#define NV4_FIFO_DMA_METHOD_COUNT ( 0*32+28):( 0*32+18) -#define NV4_FIFO_DMA_METHOD_SUBCHANNEL ( 0*32+15):( 0*32+13) -#define NV4_FIFO_DMA_METHOD_ADDRESS ( 0*32+12):( 0*32+ 2) -#define NV4_FIFO_DMA_DATA ( 1*32+31):( 1*32+ 0) -#define NV4_FIFO_DMA_JUMP_OFFSET 2 - -// DFB is in BAR1. Access it as VRAM - -#define NV4_PEXTDEV_BOOT_0 0x101000 -#define NV4_STRAP_BUS_SPEED 0 -#define NV4_STRAP_BUS_SPEED_33MHZ 0x0 -#define NV4_STRAP_BUS_SPEED_66MHZ 0x1 -#define NV4_STRAP_SUB_VENDOR 1 -#define NV4_STRAP_SUB_VENDOR_NO_BIOS 0x0 -#define NV4_STRAP_SUB_VENDOR_BIOS 0x1 -#define NV4_STRAP_RAM_TYPE 2 -#define NV4_STRAP_RAM_TYPE_SGRAM_256K 0x0 -#define NV4_STRAP_RAM_TYPE_SGRAM_512K_2BANK 0x1 -#define NV4_STRAP_RAM_TYPE_SGRAM_512K_4BANK 0x2 -#define NV4_STRAP_RAM_TYPE_1024K_2BANK 0x3 -#define NV4_STRAP_RAM_WIDTH 4 -#define NV4_STRAP_RAM_WIDTH_64 0x0 -#define NV4_STRAP_RAM_WIDTH_128 0x1 -#define NV4_STRAP_BUS_TYPE 5 -#define NV4_STRAP_BUS_TYPE_PCI 0x0 -#define NV4_STRAP_BUS_TYPE_AGP 0x1 -#define NV4_STRAP_CRYSTAL 6 -#define NV4_STRAP_CRYSTAL_13500K 0x0 -#define NV4_STRAP_CRYSTAL_14318180 0x1 -#define NV4_STRAP_TVMODE 7 -#define NV4_STRAP_TVMODE_SECAM 0x0 -#define NV4_STRAP_TVMODE_NTSC 0x1 -#define NV4_STRAP_TVMODE_PAL 0x2 -#define NV4_STRAP_TVMODE_DISABLED 0x3 -#define NV4_STRAP_OVERWRITE 11 -#define NV4_STRAP_OVERWRITE_DISABLED 0x0 -#define NV4_STRAP_OVERWRITE_ENABLED 0x1 - -#define NV4_PDAC_START 0x680000 -#define NV4_PDAC_END 0x680FFF - -#define NV4_PDAC_DATA(i) (0x680000+(i)*4) -#define NV4_PDAC_DATA_SIZE_1 16 -#define NV4_PDAC_DATA_VALUE 0 - -// WARNING! -// This has shown up at 0x700000 on real NV4 -#define NV4_PROM_START 0x300000 -#define NV4_PROM_END 0x30FFFF -#define NV4_PROM_DATA(i) (NV4_PROM_START+(i)) -#define NV4_PROM_SIZE 65536 - -#define NV4_PROM_ERRATA_START 0x700000 -#define NV4_PROM_ERRATA_END 0x70FFFF -#define NV4_PROM_ERRATA_DATA(i) (NV4_PROM_ERRATA_START+(i)) - -#define NV4_PRM 0x5FFF:0x4000 -#define NV4_PRM_INTR_0 0x4100 -#define NV4_PRM_INTR_0_TRACE_MPU401 0 -#define NV4_PRM_INTR_0_TRACE_MPU401_NOT_PENDING 0x0 -#define NV4_PRM_INTR_0_TRACE_MPU401_PENDING 0x1 -#define NV4_PRM_INTR_0_TRACE_MPU401_RESET 0x1 -#define NV4_PRM_INTR_0_TRACE_FM 4 -#define NV4_PRM_INTR_0_TRACE_FM_NOT_PENDING 0x0 -#define NV4_PRM_INTR_0_TRACE_FM_PENDING 0x1 -#define NV4_PRM_INTR_0_TRACE_FM_RESET 0x1 -#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL 8 -#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_NOT_PENDING 0x0 -#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_PENDING 0x1 -#define NV4_PRM_INTR_0_TRACE_SB_DIGITAL_RESET 0x1 -#define NV4_PRM_INTR_0_TRACE_SB_MIXER 12 -#define NV4_PRM_INTR_0_TRACE_SB_MIXER_NOT_PENDING 0x0 -#define NV4_PRM_INTR_0_TRACE_SB_MIXER_PENDING 0x1 -#define NV4_PRM_INTR_0_TRACE_SB_MIXER_RESET 0x1 -#define NV4_PRM_INTR_0_TRACE_OVERFLOW 16 -#define NV4_PRM_INTR_0_TRACE_OVERFLOW_NOT_PENDING 0x0 -#define NV4_PRM_INTR_0_TRACE_OVERFLOW_PENDING 0x1 -#define NV4_PRM_INTR_0_TRACE_OVERFLOW_RESET 0x1 -#define NV4_PRM_INTR_EN_0 0x4140 -#define NV4_PRM_INTR_EN_0_TRACE_MPU401 0 -#define NV4_PRM_INTR_EN_0_TRACE_MPU401_ENABLED 0x1 -#define NV4_PRM_INTR_EN_0_TRACE_FM 4 -#define NV4_PRM_INTR_EN_0_TRACE_FM_ENABLED 0x1 -#define NV4_PRM_INTR_EN_0_TRACE_SB_DIGITAL 8 -#define NV4_PRM_INTR_EN_0_TRACE_SB_DIGITAL_ENABLED 0x1 -#define NV4_PRM_INTR_EN_0_TRACE_SB_MIXER 12 -#define NV4_PRM_INTR_EN_0_TRACE_SB_MIXER_ENABLED 0x1 -#define NV4_PRM_INTR_EN_0_TRACE_OVERFLOW 16 -#define NV4_PRM_INTR_EN_0_TRACE_OVERFLOW_ENABLED 0x1 -#define NV4_PRM_RAMRM 0x4200 -#define NV4_PRM_RAMRM_BASE_ADDRESS 12 -#define NV4_PRM_RAMRM_BASE_ADDRESS_2000 0x2000 -#define NV4_PRM_TRACE 0x4300 -#define NV4_PRM_TRACE_IO_CAPTURE 0 -#define NV4_PRM_TRACE_IO_CAPTURE_DISABLED 0x0 -#define NV4_PRM_TRACE_IO_CAPTURE_WRITES 0x1 -#define NV4_PRM_TRACE_IO_CAPTURE_READS 0x2 -#define NV4_PRM_TRACE_IO_CAPTURE_READS_WRITES 0x3 -#define NV4_PRM_TRACE_IO_WRITE 4 -#define NV4_PRM_TRACE_IO_WRITE_NONE 0x0 -#define NV4_PRM_TRACE_IO_WRITE_OCCURED 0x1 -#define NV4_PRM_TRACE_IO_WRITE_RESET 0x1 -#define NV4_PRM_TRACE_IO_READ 5 -#define NV4_PRM_TRACE_IO_READ_NONE 0x0 -#define NV4_PRM_TRACE_IO_READ_OCCURED 0x1 -#define NV4_PRM_TRACE_IO_READ_RESET 0x1 -#define NV4_PRM_TRACE_INDEX 0x4310 -#define NV4_PRM_TRACE_INDEX_ADDRESS 0 -#define NV4_PRM_TRACE_INDEX_ADDRESS_0 0x0 -#define NV4_PRM_IGNORE_0 0x4320 -#define NV4_PRM_IGNORE_0_MPU401 0 -#define NV4_PRM_IGNORE_0_MPU401_DISABLED 0x0 -#define NV4_PRM_IGNORE_0_MPU401_WRITES 0x1 -#define NV4_PRM_IGNORE_0_MPU401_READS 0x2 -#define NV4_PRM_IGNORE_0_MPU401_READS_WRITES 0x3 -#define NV4_PRM_IGNORE_0_FM 4 -#define NV4_PRM_IGNORE_0_FM_DISABLED 0x0 -#define NV4_PRM_IGNORE_0_FM_WRITES 0x1 -#define NV4_PRM_IGNORE_0_FM_READS 0x2 -#define NV4_PRM_IGNORE_0_FM_READS_WRITES 0x3 -#define NV4_PRM_IGNORE_0_SB_DIGITAL 8 -#define NV4_PRM_IGNORE_0_SB_DIGITAL_DISABLED 0x0 -#define NV4_PRM_IGNORE_0_SB_DIGITAL_WRITES 0x1 -#define NV4_PRM_IGNORE_0_SB_DIGITAL_READS 0x2 -#define NV4_PRM_IGNORE_0_SB_DIGITAL_READS_WRITES 0x3 -#define NV4_PRM_IGNORE_0_SB_MIXER 12 -#define NV4_PRM_IGNORE_0_SB_MIXER_DISABLED 0x0 -#define NV4_PRM_IGNORE_0_SB_MIXER_WRITES 0x1 -#define NV4_PRM_IGNORE_0_SB_MIXER_READS 0x2 -#define NV4_PRM_IGNORE_0_SB_MIXER_READS_WRITES 0x3 - -// -// PIO submission -// - -#define NV4_USER_START 0x800000 -#define NV4_USER_END 0xFFFFFF - -#define NV4_USER_OBJECT(i,j) (0x800000+(i)*0x10000+(j)*0x2000) -#define NV4_USER_OBJECT_SIZE_1 16 -#define NV4_USER_OBJECT_SIZE_2 8 -#define NV4_USER_OBJECT_HANDLE 0 -#define NV4_USER_FREE016(i,j) (0x800010+(i)*65536+(j)*8192) -#define NV4_USER_FREE016_SIZE_1 16 -#define NV4_USER_FREE016_SIZE_2 8 -#define NV4_USER_FREE016_COUNT_LO 0 -#define NV4_USER_FREE016_COUNT_LO_0 0x0 -#define NV4_USER_FREE016_COUNT 2 -#define NV4_USER_FREE016_COUNT_HI 10 -#define NV4_USER_FREE016_COUNT_HI_0 0x0 -#define NV4_USER_FREE032(i,j) (0x800010+(i)*65536+(j)*8192) -#define NV4_USER_FREE032_SIZE_1 16 -#define NV4_USER_FREE032_SIZE_2 8 -#define NV4_USER_FREE032_COUNT_LO 0 -#define NV4_USER_FREE032_COUNT_LO_0 0x0 -#define NV4_USER_FREE032_COUNT 2 -#define NV4_USER_FREE032_COUNT_HI 10 -#define NV4_USER_FREE032_COUNT_HI_0 0x0 -#define NV4_USER_ZERO016(i,j,k) (0x0800012+(i)*65536+(j)*8192+(k)*2) -#define NV4_USER_ZERO016_SIZE_1 16 -#define NV4_USER_ZERO016_SIZE_2 8 -#define NV4_USER_ZERO016_SIZE_3 7 -#define NV4_USER_ZERO016_COUNT 0 -#define NV4_USER_ZERO016_COUNT_0 0x0 -#define NV4_USER_ZERO032(i,j,k) (0x0800014+(i)*65536+(j)*8192+(k)*4) -#define NV4_USER_ZERO032_SIZE_1 16 -#define NV4_USER_ZERO032_SIZE_2 8 -#define NV4_USER_ZERO032_SIZE_3 3 -#define NV4_USER_ZERO032_COUNT 0 -#define NV4_USER_ZERO032_COUNT_0 0x0 -#define NV4_USER_DMA_PUT(i,j) (0x800040+(i)*0x10000+(j)*0x2000) -#define NV4_USER_DMA_PUT_OFFSET 2 -#define NV4_USER_DMA_GET(i,j) (0x800044+(i)*0x10000+(j)*0x2000) -#define NV4_USER_DMA_GET_OFFSET 2 - -#define NV4_USER_ADR_CHID 16 -#define NV4_USER_ADR_SUBCHID 13 -#define NV4_USER_ADR_METHOD 0 -#define NV4_USER_DEVICE 16 - -#define NV4_PTIMER_START 0x9000 -#define NV4_PTIMER_END 0x9FFF - -#define NV4_PTIMER_INTR 0x9100 -#define NV4_PTIMER_INTR_ALARM 0 -#define NV4_PTIMER_INTR_ALARM_NOT_PENDING 0x0 -#define NV4_PTIMER_INTR_ALARM_PENDING 0x1 -#define NV4_PTIMER_INTR_ALARM_RESET 0x1 -#define NV4_PTIMER_INTR_EN 0x9140 -#define NV4_PTIMER_INTR_EN_ALARM 0 -#define NV4_PTIMER_INTR_EN_ALARM_ENABLED 0x1 // 0 = disabled -#define NV4_PTIMER_NUMERATOR 0x9200 -#define NV4_PTIMER_NUMERATOR_VALUE 0 -#define NV4_PTIMER_NUMERATOR_VALUE_0 0x0 -#define NV4_PTIMER_DENOMINATOR 0x9210 -#define NV4_PTIMER_DENOMINATOR_VALUE 0 -#define NV4_PTIMER_DENOMINATOR_VALUE_0 0x0 -#define NV4_PTIMER_TIME_0 0x9400 -#define NV4_PTIMER_TIME_0_NSEC 5 -#define NV4_PTIMER_TIME_1 0x9410 -#define NV4_PTIMER_TIME_1_NSEC 0 -#define NV4_PTIMER_ALARM 0x9420 -#define NV4_PTIMER_ALARM_NSEC 5 - -#define NV4_TRACE 0xFFFF: 0x0 -#define NV4_TRACE_DATA ( 0*32+ 7):( 0*32+ 0) -#define NV4_TRACE_ACCESS ( 0*32+14):( 0*32+14) -#define NV4_TRACE_ACCESS_WRITE 0x0 -#define NV4_TRACE_ACCESS_READ 0x1 -#define NV4_TRACE_TYPE ( 0*32+15):( 0*32+15) -#define NV4_TRACE_TYPE_IO 0x0 -#define NV4_TRACE_TYPE_MEMORY 0x1 -#define NV4_TRACE_ADDRESS ( 0*32+31):( 0*32+16) - -#define NV4_RAMHT_SIZE_0 0xFFF -#define NV4_RAMHT_SIZE_1 0x1FFF -#define NV4_RAMHT_SIZE_2 0x3FFF -#define NV4_RAMHT_SIZE_3 0x7FFF -#define NV4_RAMHT_HANDLE ( 0*32+31):( 0*32+ 0) -#define NV4_RAMHT_INSTANCE ( 1*32+15):( 1*32+ 0) -#define NV4_RAMHT_ENGINE ( 1*32+17):( 1*32+16) -#define NV4_RAMHT_ENGINE_SW 0x0 -#define NV4_RAMHT_ENGINE_GRAPHICS 0x1 -#define NV4_RAMHT_ENGINE_DVD 0x2 -#define NV4_RAMHT_CHID ( 1*32+27):( 1*32+24) -#define NV4_RAMHT_STATUS ( 1*32+31):( 1*32+31) -#define NV4_RAMHT_STATUS_INVALID 0x0 -#define NV4_RAMHT_STATUS_VALID 0x1 - -#define NV4_RAMRO_SIZE_0 0x1FF -#define NV4_RAMRO_SIZE_1 0x1FFF -#define NV4_RAMRO_METHOD ( 0*32+12):( 0*32+ 0) -#define NV4_RAMRO_SUBCHANNEL ( 0*32+15):( 0*32+13) -#define NV4_RAMRO_CHID ( 0*32+22):( 0*32+16) -#define NV4_RAMRO_TYPE ( 0*32+23):( 0*32+23) -#define NV4_RAMRO_TYPE_WRITE 0x0 -#define NV4_RAMRO_TYPE_READ 0x1 -#define NV4_RAMRO_BYTE_ENABLES ( 0*32+27):( 0*32+24) -#define NV4_RAMRO_REASON ( 0*32+31):( 0*32+28) -#define NV4_RAMRO_REASON_ILLEGAL_ACCESS 0x0 -#define NV4_RAMRO_REASON_NO_CACHE_AVAILABLE 0x1 -#define NV4_RAMRO_REASON_CACHE_RAN_OUT 0x2 -#define NV4_RAMRO_REASON_FREE_COUNT_OVERRUN 0x3 -#define NV4_RAMRO_REASON_CAUGHT_LYING 0x4 -#define NV4_RAMRO_REASON_RESERVED_ACCESS 0x5 -#define NV4_RAMRO_DATA ( 1*32+31):( 1*32+ 0) - -#define NV4_RAMFC_SIZE_0 0x1FF -#define NV4_RAMFC_DMA_PUT ( 0*32+28):( 0*32+ 2) -#define NV4_RAMFC_DMA_GET ( 1*32+28):( 1*32+ 2) -#define NV4_RAMFC_DMA_INST ( 2*32+15):( 2*32+ 0) -#define NV4_RAMFC_DMA_METHOD ( 3*32+12):( 3*32+ 2) -#define NV4_RAMFC_DMA_SUBCHANNEL ( 3*32+15):( 3*32+13) -#define NV4_RAMFC_DMA_METHOD_COUNT ( 3*32+28):( 3*32+18) -#define NV4_RAMFC_DMA_FETCH_TRIG ( 4*32+ 7):( 4*32+ 3) -#define NV4_RAMFC_DMA_FETCH_SIZE ( 4*32+15):( 4*32+13) -#define NV4_RAMFC_DMA_FETCH_MAX_REQS ( 4*32+19):( 4*32+16) -#define NV4_RAMFC_ENGINE_SUB_0 ( 5*32+ 1):( 5*32+ 0) -#define NV4_RAMFC_ENGINE_SUB_1 ( 5*32+ 5):( 5*32+ 4) -#define NV4_RAMFC_ENGINE_SUB_2 ( 5*32+ 9):( 5*32+ 8) -#define NV4_RAMFC_ENGINE_SUB_3 ( 5*32+13):( 5*32+12) -#define NV4_RAMFC_ENGINE_SUB_4 ( 5*32+17):( 5*32+16) -#define NV4_RAMFC_ENGINE_SUB_5 ( 5*32+21):( 5*32+20) -#define NV4_RAMFC_ENGINE_SUB_6 ( 5*32+25):( 5*32+24) -#define NV4_RAMFC_ENGINE_SUB_7 ( 5*32+29):( 5*32+28) -#define NV4_RAMFC_ENGINE_SW 0x0 -#define NV4_RAMFC_ENGINE_GRAPHICS 0x1 -#define NV4_RAMFC_ENGINE_DVD 0x2 -#define NV4_RAMFC_PULL1_ENGINE ( 6*32+ 1):( 6*32+ 0) -#define NV4_RAMFC_PULL1_ENGINE_SW 0x0 -#define NV4_RAMFC_PULL1_ENGINE_GRAPHICS 0x1 -#define NV4_RAMFC_PULL1_ENGINE_DVD 0x2 - - -#define NV4_RAMDVD_CTX_TABLE (63*32+31):( 0*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT(c,s) (((c)*4+((s)/2))*32+((s)%2)*16+15):(((c)*4+((s)/2))*32+((s)%2)*16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_0 ( 0*32+15):( 0*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_1 ( 0*32+31):( 0*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_2 ( 1*32+15):( 1*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_3 ( 1*32+31):( 1*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_4 ( 2*32+15):( 2*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_5 ( 2*32+31):( 2*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_6 ( 3*32+15):( 3*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_0_7 ( 3*32+31):( 3*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_0 (60*32+15):(60*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_1 (60*32+31):(60*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_2 (61*32+15):(61*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_3 (61*32+31):(61*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_4 (62*32+15):(62*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_5 (62*32+31):(62*32+16) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_6 (63*32+15):(63*32+ 0) -#define NV4_RAMDVD_CTX_TABLE_OBJECT_15_7 (63*32+31):(63*32+16) - -#define NV4_DMA_CLASS ( 0*32+11):( 0*32+ 0) -#define NV4_DMA_PAGE_TABLE ( 0*32+12):( 0*32+12) -#define NV4_DMA_PAGE_TABLE_NOT_PRESENT 0x0 -#define NV4_DMA_PAGE_TABLE_PRESENT 0x1 -#define NV4_DMA_PAGE_ENTRY ( 0*32+13):( 0*32+13) -#define NV4_DMA_PAGE_ENTRY_NOT_LINEAR 0x0 -#define NV4_DMA_PAGE_ENTRY_LINEAR 0x1 -#define NV4_DMA_TARGET_NODE ( 0*32+17):( 0*32+16) -#define NV4_DMA_TARGET_NODE_NVM 0x0 -#define NV4_DMA_TARGET_NODE_PCI 0x2 -#define NV4_DMA_TARGET_NODE_AGP 0x3 -#define NV4_DMA_ADJUST ( 0*32+31):( 0*32+20) -#define NV4_DMA_LIMIT ( 1*32+31):( 1*32+ 0) -#define NV4_DMA_ACCESS ( 2*32+ 1):( 2*32+ 1) -#define NV4_DMA_ACCESS_READ_ONLY 0x0 -#define NV4_DMA_ACCESS_READ_AND_WRITE 0x1 -#define NV4_DMA_FRAME_ADDRESS ( 2*32+31):( 2*32+12) - -#define NV4_SUBCHAN_CTX_SWITCH ( 0*32+31):( 0*32+ 0) -#define NV4_SUBCHAN_DMA_INSTANCE ( 1*32+15):( 1*32+ 0) -#define NV4_SUBCHAN_NOTIFY_INSTANCE ( 1*32+31):( 1*32+16) -#define NV4_SUBCHAN_MEMFMT_INSTANCE ( 2*32+15):( 2*32+ 0) -#define NV4_SUBCHAN_MEMFMT_LINEAR ( 2*32+16):( 2*32+16) -#define NV4_SUBCHAN_MEMFMT_LINEAR_OUT 0x0 -#define NV4_SUBCHAN_MEMFMT_LINEAR_IN 0x1 \ No newline at end of file diff --git a/src/include/86box/utils/video_stdlib.h b/src/include/86box/utils/video_stdlib.h deleted file mode 100644 index 0d4b148d6..000000000 --- a/src/include/86box/utils/video_stdlib.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - * - * Standard library for implementation of video functionality that is duplicated across multiple cards. - * - * - * - * Authors: Connor Hyde - * - * Copyright 2025 Connor Hyde - */ - - -/* ROP */ - -#define VIDEO_ROP_SRC_COPY 0xCC - -int32_t video_rop_gdi_ternary(int32_t rop, int32_t src, int32_t dst, int32_t pattern); \ No newline at end of file diff --git a/src/include/86box/video.h b/src/include/86box/video.h index c87ef4f77..81424dbcb 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -630,13 +630,6 @@ 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 nv1_device_edge2k; -extern const device_t nv1_device_edge3k; -extern const device_t nv3_device_pci; -extern const device_t nv3_device_agp; -extern const device_t nv3t_device_pci; -extern const device_t nv3t_device_agp; -extern const device_t nv4_device_agp; /* Wyse 700 */ extern const device_t wy700_device; diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 87ff7293a..1a004d958 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -171,15 +171,7 @@ add_library(ui STATIC qt_newfloppydialog.ui qt_harddiskdialog.cpp qt_harddiskdialog.hpp - qt_harddiskdialog.ui - - qt_gpudebug_vram.cpp - qt_gpudebug_vram.hpp - qt_gpudebug_vram.ui - - qt_gpudebug_visualnv.cpp - qt_gpudebug_visualnv.hpp - qt_gpudebug_visualnv.ui + qt_harddiskdialog.ui qt_harddrive_common.cpp qt_harddrive_common.hpp diff --git a/src/qt/qt_gpudebug_visualnv.cpp b/src/qt/qt_gpudebug_visualnv.cpp deleted file mode 100644 index 3b2ba8472..000000000 --- a/src/qt/qt_gpudebug_visualnv.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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. - * - * GPU Debugging Tools - Visual NV Debugger implementation - * - * - * - * Authors: starfrost - * - * Copyright 2025 starfrost - */ - -/* C++ includes */ -#include -#include - - -/* Qt includes*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ui_qt_gpudebug_visualnv.h" - -/* 86Box core includes */ -extern "C" -{ - /* NOTE: DO NOT REMOVE */ - #include <86box/86box.h> - #include <86box/device.h> - #include <86box/mem.h> - #include <86box/pci.h> - #include <86box/rom.h> - #include <86box/video.h> - #include <86box/nv/vid_nv.h> - #include <86box/nv/vid_nv3.h> -} - -VisualNVDialog::VisualNVDialog(QWidget *parent) - : QDialog(parent) - , ui(new Ui::VisualNVDialog) -{ - ui->setupUi(this); - - connect(ui->btnLoadSavestate, &QPushButton::clicked, this, &VisualNVDialog::on_btnLoadSavestate_clicked); - connect(ui->fbStartAddress, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_fbStartAddress_changed); - connect(ui->bPitch0Value, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_bPitch0Value_changed); - connect(ui->bPitch1Value, &QPlainTextEdit::textChanged, this, &VisualNVDialog::on_bPitch1Value_changed); -} - -// VisualNV dialog destructor -VisualNVDialog::~VisualNVDialog() -{ - -} - -void VisualNVDialog::on_btnLoadSavestate_clicked() -{ - if (!nv3) - return; - - QString bar0_file_name = QFileDialog::getOpenFileName - ( - this, - tr("Please provide NVPlay 0.3.0.7+ NV3BAR0.BIN file"), - ".", - tr("NVPlay MMIO Dump Files (*.bin)") - ); - - QString bar1_file_name = QFileDialog::getOpenFileName - ( - this, - tr("Please provide NVPlay 0.3.0.7+ NV3BAR1.BIN file"), - ".", - tr("NVPlay VRAM/RAMIN Dump Files (*.bin)") - ); - - // - // Open both dump files - // - - QFile bar0(bar0_file_name); - QFile bar1(bar1_file_name); - - if (!bar0.open(QIODevice::ReadOnly)) - { - warning("Failed to open NV3BAR0.bin!"); - return; - } - - if (!bar1.open(QIODevice::ReadOnly)) - { - warning("Failed to open NV3BAR1.bin!"); - return; - } - - if (bar0.size() != NV3_MMIO_SIZE - || bar1.size() != NV3_MMIO_SIZE) - { - warning("NV3BAR0.bin and NV3BAR1.bin must be 16MB!"); - bar0.close(); - bar1.close(); - return; - } - - // Load VRAM contents only for now. Todo: MMIO+RAMIN - QString oldTitle = this->windowTitle(); - - this->setWindowTitle(tr("RIVA 128 Realtime Debugger: Savestate Loading...")); - - bar1.read((char*)nv3->nvbase.svga.vram, nv3->nvbase.vram_amount); - - this->setWindowTitle(oldTitle); - -} - -void VisualNVDialog::on_fbStartAddress_changed() -{ - if (nv3) - { - nv3->nvbase.debug_dba_enabled = true; - - bool ok = true; - - nv3->nvbase.debug_dba = ui->fbStartAddress->toPlainText().toInt(&ok); - - if (!ok) - nv3->nvbase.debug_dba_enabled = false; - } -} - -void VisualNVDialog::on_bPitch0Value_changed() -{ - if (nv3) - { - bool ok = true; - - uint32_t old_bpitch = nv3->pgraph.bpitch[0]; - - nv3->pgraph.bpitch[0] = ui->bPitch0Value->toPlainText().toInt(&ok); - - if (!ok) - nv3->pgraph.bpitch[0] = old_bpitch; - } - -} - -void VisualNVDialog::on_bPitch1Value_changed() -{ - if (nv3) - { - bool ok = true; - - uint32_t old_bpitch = nv3->pgraph.bpitch[1]; - - nv3->pgraph.bpitch[1] = ui->bPitch0Value->toPlainText().toInt(&ok); - - if (!ok) - nv3->pgraph.bpitch[1] = old_bpitch; - } - -} \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.hpp b/src/qt/qt_gpudebug_visualnv.hpp deleted file mode 100644 index ee87dc0b3..000000000 --- a/src/qt/qt_gpudebug_visualnv.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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. - * - * GPU Debugging Tools - VRAM Viewer headers - * - * - * - * Authors: starfrost - * - * Copyright 2025 starfrost - */ - - -#pragma once - -#include - -namespace Ui -{ - class VisualNVDialog; -} - -class VisualNVDialog : public QDialog -{ - Q_OBJECT - - public: - explicit VisualNVDialog(QWidget *parent = nullptr); - ~VisualNVDialog(); - - void on_btnLoadSavestate_clicked(); - void on_fbStartAddress_changed(); - void on_bPitch0Value_changed(); - void on_bPitch1Value_changed(); - - protected: - private: - Ui::VisualNVDialog* ui; - - -}; \ No newline at end of file diff --git a/src/qt/qt_gpudebug_visualnv.ui b/src/qt/qt_gpudebug_visualnv.ui deleted file mode 100644 index ce45c4e44..000000000 --- a/src/qt/qt_gpudebug_visualnv.ui +++ /dev/null @@ -1,213 +0,0 @@ - - - VisualNVDialog - - - - 0 - 0 - 620 - 350 - - - - - 0 - 0 - - - - - 620 - 350 - - - - - 620 - 350 - - - - RIVA 128 Realtime Debugger - - - - - - - - 10 - 0 - 201 - 171 - - - - PFIFO State - - - - - - 220 - 0 - 361 - 171 - - - - PGRAPH State - - - - - - 10 - 180 - 571 - 141 - - - - VRAM Control - - - - - 160 - 30 - 104 - 21 - - - - - - - 10 - 30 - 151 - 21 - - - - <html><head/><body><p>VRAM Visual Aperture Start</p></body></html> - - - - - - 390 - 30 - 91 - 16 - - - - <html><head/><body><p>FB Start Address:</p></body></html> - - - - - - 480 - 30 - 81 - 16 - - - - <html><head/><body><p><span style=" font-weight:700;">PLACEHOLDER</span></p></body></html> - - - - - - 160 - 60 - 104 - 21 - - - - - - - 10 - 60 - 151 - 21 - - - - <html><head/><body><p>Pixel Depth</p></body></html> - - - - - - 10 - 110 - 231 - 24 - - - - Load nvplay savestate from real hardware - - - - - - 390 - 60 - 61 - 16 - - - - <html><head/><body><p>BPITCH[0]:</p></body></html> - - - - - - 460 - 60 - 104 - 21 - - - - - - - 460 - 90 - 104 - 21 - - - - - - - 390 - 90 - 61 - 16 - - - - <html><head/><body><p>BPITCH[1]:</p></body></html> - - - - - - - - - - diff --git a/src/qt/qt_gpudebug_vram.cpp b/src/qt/qt_gpudebug_vram.cpp deleted file mode 100644 index 000f41263..000000000 --- a/src/qt/qt_gpudebug_vram.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - * - * GPU Debugging Tools - VRAM Viewer implementation - * - * - * - * Authors: starfrost - * - * Copyright 2025 starfrost - */ - -/* C++ includes */ -#include -#include - - -/* Qt includes*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ui_qt_gpudebug_vram.h" - -/* 86Box core includes */ -extern "C" -{ - -} - -GPUDebugVRAMDialog::GPUDebugVRAMDialog(QWidget *parent) - : QDialog(parent) - , ui(new Ui::GPUDebugVRAMDialog) -{ - ui->setupUi(this); -} - -GPUDebugVRAMDialog::~GPUDebugVRAMDialog() -{ - -} \ No newline at end of file diff --git a/src/qt/qt_gpudebug_vram.hpp b/src/qt/qt_gpudebug_vram.hpp deleted file mode 100644 index 18b2e8c96..000000000 --- a/src/qt/qt_gpudebug_vram.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - * - * GPU Debugging Tools - VRAM Viewer headers - * - * - * - * Authors: starfrost - * - * Copyright 2025 starfrost - */ - - -#pragma once - -#include - -namespace Ui -{ - class GPUDebugVRAMDialog; -} - -class GPUDebugVRAMDialog : public QDialog -{ - Q_OBJECT - - public: - explicit GPUDebugVRAMDialog(QWidget *parent = nullptr); - ~GPUDebugVRAMDialog(); - protected: - private: - Ui::GPUDebugVRAMDialog* ui; - - -}; \ No newline at end of file diff --git a/src/qt/qt_gpudebug_vram.ui b/src/qt/qt_gpudebug_vram.ui deleted file mode 100644 index a92a60306..000000000 --- a/src/qt/qt_gpudebug_vram.ui +++ /dev/null @@ -1,42 +0,0 @@ - - - GPUDebugVRAMDialog - - - - 0 - 0 - 600 - 400 - - - - - 0 - 0 - - - - - 600 - 400 - - - - - 600 - 400 - - - - VRAM Viewer - - - - - - - - - - diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index e0900d1ef..954547078 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -24,8 +24,6 @@ #include "qt_mainwindow.hpp" #include "ui_qt_mainwindow.h" -#include "ui_qt_gpudebug_vram.h" -#include "ui_qt_gpudebug_visualnv.h" #include "qt_specifydimensions.h" #include "qt_soundgain.hpp" @@ -108,8 +106,6 @@ void qt_set_sequence_auto_mnemonic(bool b); #include "qt_mediamenu.hpp" #include "qt_util.hpp" -#include "qt_gpudebug_vram.hpp" - #if defined __unix__ && !defined __HAIKU__ # ifndef Q_OS_MACOS # include "evdev_keyboard.hpp" @@ -289,7 +285,29 @@ MainWindow::MainWindow(QWidget *parent) this->setWindowTitle(QString("%1 - %2 %3").arg(vmname, EMU_NAME, EMU_VERSION_FULL)); connect(this, &MainWindow::hardResetCompleted, this, [this]() { - onHardResetCompleted(); + ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA)); + num_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + scroll_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + caps_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); + int ext_ax_kbd = machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD) && + (keyboard_type == KEYBOARD_TYPE_AX); + int int_ax_kbd = machine_has_flags(machine, MACHINE_KEYBOARD_JIS) && + !machine_has_bus(machine, MACHINE_BUS_PS2_PORTS); + kana_label->setVisible(ext_ax_kbd || int_ax_kbd); + while (QApplication::overrideCursor()) + QApplication::restoreOverrideCursor(); +#ifdef USE_WACOM + ui->menuTablet_tool->menuAction()->setVisible(mouse_input_mode >= 1); +#else + ui->menuTablet_tool->menuAction()->setVisible(false); +#endif + + bool enable_comp_option = false; + for (int i = 0; i < MONITORS_NUM; i++) { + if (monitors[i].mon_composite) { enable_comp_option = true; break; } + } + + ui->actionCGA_composite_settings->setEnabled(enable_comp_option); }); connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::QueuedConnection); @@ -890,50 +908,6 @@ MainWindow::MainWindow(QWidget *parent) updateShortcuts(); } -void MainWindow::onHardResetCompleted() -{ - ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA)); - num_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - scroll_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - caps_label->setVisible(machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD)); - int ext_ax_kbd = machine_has_bus(machine, MACHINE_BUS_PS2_PORTS | MACHINE_BUS_AT_KBD) && - (keyboard_type == KEYBOARD_TYPE_AX); - int int_ax_kbd = machine_has_flags(machine, MACHINE_KEYBOARD_JIS) && - !machine_has_bus(machine, MACHINE_BUS_PS2_PORTS); - kana_label->setVisible(ext_ax_kbd || int_ax_kbd); - while (QApplication::overrideCursor()) - QApplication::restoreOverrideCursor(); -#ifdef USE_WACOM - ui->menuTablet_tool->menuAction()->setVisible(mouse_input_mode >= 1); -#else - ui->menuTablet_tool->menuAction()->setVisible(false); -#endif - - bool enable_comp_option = false; - for (int i = 0; i < MONITORS_NUM; i++) { - if (monitors[i].mon_composite) { enable_comp_option = true; break; } - } - - ui->actionCGA_composite_settings->setEnabled(enable_comp_option); - -#ifdef ENABLE_NV_LOG - /* - THIS CODE SUCKS AND THIS DESIGN IS TERRIBLE - EVERYTHING ABOUT IT IS BAD AND WRONG. - ENTIRE DEVICE SUBSYSTEM IDEALLY WOULD BE DECOUPLED FROM UI BUT MEH - */ - - const device_t* vid_device = video_card_getdevice(gfxcard[0]); - - bool is_nv3 = (vid_device == &nv3_device_agp - || vid_device == &nv3_device_pci - || vid_device == &nv3t_device_agp - || vid_device == &nv3t_device_pci); - - ui->actionDebug_GPUDebug_VisualNv->setVisible(is_nv3); -#endif -} - - void MainWindow::closeEvent(QCloseEvent *event) { @@ -2551,29 +2525,3 @@ void MainWindow::on_actionCGA_composite_settings_triggered() config_save(); } - -void MainWindow::on_actionDebug_GPUDebug_VRAM_triggered() -{ - debugVramDialog = new GPUDebugVRAMDialog(this); - debugVramDialog->setWindowFlag(Qt::CustomizeWindowHint, true); - debugVramDialog->setWindowFlag(Qt::WindowTitleHint, true); - debugVramDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); - // If I have this as a NON-MODAL dialog, input is just eaten without doing anything - // WTF?!?!?!?!? - //debugVramDialog->show(); - debugVramDialog->exec(); - -} - - -void MainWindow::on_actionDebug_GPUDebug_VisualNv_triggered() -{ - visualNvDialog = new VisualNVDialog(this); - visualNvDialog->setWindowFlag(Qt::CustomizeWindowHint, true); - visualNvDialog->setWindowFlag(Qt::WindowTitleHint, true); - visualNvDialog->setWindowFlag(Qt::WindowSystemMenuHint, false); - // If I have this as a NON-MODAL dialog, input is just eaten without doing anything - // WTF?!?!?!?!? - //visualNvDialog->show(); - visualNvDialog->exec(); -} \ No newline at end of file diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 6ae838896..f562c2ea9 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -17,11 +17,6 @@ extern QTimer discordupdate; -// NON-modal dialogs -#include "qt_gpudebug_vram.hpp" -#include "qt_gpudebug_visualnv.hpp" - - class MediaMenu; class RendererStack; @@ -79,8 +74,6 @@ signals: public slots: void showSettings(); void hardReset(); - void onHardResetCompleted(); - void togglePause(); void initRendererMonitorSlot(int monitor_index); void destroyRendererMonitorSlot(int monitor_index); @@ -143,9 +136,7 @@ private slots: void on_actionPreferences_triggered(); void on_actionEnable_Discord_integration_triggered(bool checked); void on_actionRenderer_options_triggered(); - void on_actionDebug_GPUDebug_VRAM_triggered(); - void on_actionDebug_GPUDebug_VisualNv_triggered(); - + void refreshMediaMenu(); void showMessage_(int flags, const QString &header, const QString &message, bool richText, std::atomic_bool* done = nullptr); void getTitle_(wchar_t *title); @@ -184,10 +175,6 @@ private slots: private: Ui::MainWindow *ui; - - // NON-modal dialogs - these use ::show() and therefore have to be maintained as objects - GPUDebugVRAMDialog *debugVramDialog; - VisualNVDialog *visualNvDialog; std::unique_ptr status; std::shared_ptr mm; diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index bebef8c88..ca7aca2ca 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -251,19 +251,11 @@ - - - &Debugging Tools - - - - - @@ -927,27 +919,7 @@ &CGA composite settings... - - - - - GPU Debug - VRAM Viewer - - - - - RIVA 128 Realtime Debugger - - - - - GPU Debug - VRAM Viewer - - - - - RIVA 128 Realtime Debugger - + @@ -1077,54 +1049,6 @@ &8x - - - true - - - &Full screen stretch - - - - - true - - - &4:3 - - - - - true - - - &Square pixels (Keep ratio) - - - - - true - - - &Integer scale - - - - - true - - - 4:&3 Integer scale - - - - - true - - - &1x - - diff --git a/src/sound/snd_emu8k.c b/src/sound/snd_emu8k.c index b806f3ccb..c3efdaaff 100644 --- a/src/sound/snd_emu8k.c +++ b/src/sound/snd_emu8k.c @@ -1525,7 +1525,7 @@ emu8k_outw(uint16_t addr, uint16_t val, void *priv) default: break; } - emu8k_log("EMU8K WRITE: : Unknown register write: %04X-%02X(%d/%d): %04X \n", addr, (emu8k->cur_reg) << 5 | emu8k->cur_voice, + emu8k_log("EMU8K WRITE: Unknown register write: %04X-%02X(%d/%d): %04X \n", addr, (emu8k->cur_reg) << 5 | emu8k->cur_voice, emu8k->cur_reg, emu8k->cur_voice, val); } diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index fe9be9c2d..f041ebb8c 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -798,7 +798,7 @@ sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv) break; default: - sb_log("sb_ct1335: : Unknown register write: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("sb_ct1335: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } @@ -899,7 +899,7 @@ sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *priv) break; default: - sb_log("sb_ct1345: : Unknown register write: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); + sb_log("sb_ct1345: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } } diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index fab669d28..bcbc7aafd 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -24,7 +24,4 @@ add_library(utils OBJECT ini.c log.c random.c - - # VIDEO - video/video_rop.c ) diff --git a/src/utils/log.c b/src/utils/log.c index b8c28afab..f0eb6aa3c 100644 --- a/src/utils/log.c +++ b/src/utils/log.c @@ -44,7 +44,6 @@ typedef struct log_t { char **cyclic_buff; int32_t cyclic_last_line; int32_t log_cycles; - int32_t last_repeat_order; } log_t; /* File to log output to. */ @@ -220,18 +219,9 @@ log_out_cyclic(void* priv, const char* fmt, va_list ap) } - if (is_cycle) - { - if (log->cyclic_last_line % repeat_order == 0) - { - log->log_cycles++; - - // If the order of the log repeat changes - if (log->last_repeat_order != repeat_order - && log->last_repeat_order > 0) - { - log->log_cycles = 1; - } + if (is_cycle) { + if (log->cyclic_last_line % repeat_order == 0) { + log->log_cycles++; if (log->log_cycles == 1) { /* @@ -275,13 +265,10 @@ log_out_cyclic(void* priv, const char* fmt, va_list ap) } log->cyclic_last_line++; - - log->last_repeat_order = repeat_order; } } #endif - void log_fatal(void *priv, const char *fmt, ...) { diff --git a/src/utils/video/video_rop.c b/src/utils/video/video_rop.c deleted file mode 100644 index 146822201..000000000 --- a/src/utils/video/video_rop.c +++ /dev/null @@ -1,790 +0,0 @@ -#include -#include <86box/utils/video_stdlib.h> - -/* - Implements a standard GDI ternary rop for e.g. bitblit acceleration. - For further information on this function, refer to the documentation on Win32 GDI: - - https://learn.microsoft.com/en-us/windows/win32/gdi/binary-raster-operations - - This is currently used in the following graphics cards: Tseng Labs ET4000/32p, Cirrus Logic CL-GD54xx, 3dfx Voodoo Banshee/Voodoo 3, Trident TGUI9440, - S3 ViRGE, C&T 69000, ATI Mach64, and NVidia RIVA 128 -*/ -int32_t video_rop_gdi_ternary(int32_t rop, int32_t src, int32_t dst, int32_t pattern) -{ - uint32_t out = 0x00; - - switch (rop) - { - case 0x00: - out = 0; - break; - case 0x01: - out = ~(dst | (pattern | src)); - break; - case 0x02: - out = dst & ~(pattern | src); - break; - case 0x03: - out = ~(pattern | src); - break; - case 0x04: - out = src & ~(dst | pattern); - break; - case 0x05: - out = ~(dst | pattern); - break; - case 0x06: - out = ~(pattern | ~(dst ^ src)); - break; - case 0x07: - out = ~(pattern | (dst & src)); - break; - case 0x08: - out = src & (dst & ~pattern); - break; - case 0x09: - out = ~(pattern | (dst ^ src)); - break; - case 0x0a: - out = dst & ~pattern; - break; - case 0x0b: - out = ~(pattern | (src & ~dst)); - break; - case 0x0c: - out = src & ~pattern; - break; - case 0x0d: - out = ~(pattern | (dst & ~src)); - break; - case 0x0e: - out = ~(pattern | ~(dst | src)); - break; - case 0x0f: - out = ~pattern; - break; - case 0x10: - out = pattern & ~(dst | src); - break; - case 0x11: - out = ~(dst | src); - break; - case 0x12: - out = ~(src | ~(dst ^ pattern)); - break; - case 0x13: - out = ~(src | (dst & pattern)); - break; - case 0x14: - out = ~(dst | ~(pattern ^ src)); - break; - case 0x15: - out = ~(dst | (pattern & src)); - break; - case 0x16: - out = pattern ^ (src ^ (dst & ~(pattern & src))); - break; - case 0x17: - out = ~(src ^ ((src ^ pattern) & (dst ^ src))); - break; - case 0x18: - out = (src ^ pattern) & (pattern ^ dst); - break; - case 0x19: - out = ~(src ^ (dst & ~(pattern & src))); - break; - case 0x1a: - out = pattern ^ (dst | (src & pattern)); - break; - case 0x1b: - out = ~(src ^ (dst & (pattern ^ src))); - break; - case 0x1c: - out = pattern ^ (src | (dst & pattern)); - break; - case 0x1d: - out = ~(dst ^ (src & (pattern ^ dst))); - break; - case 0x1e: - out = pattern ^ (dst | src); - break; - case 0x1f: - out = ~(pattern & (dst | src)); - break; - case 0x20: - out = dst & (pattern & ~src); - break; - case 0x21: - out = ~(src | (dst ^ pattern)); - break; - case 0x22: - out = dst & ~src; - break; - case 0x23: - out = ~(src | (pattern & ~dst)); - break; - case 0x24: - out = (src ^ pattern) & (dst ^ src); - break; - case 0x25: - out = ~(pattern ^ (dst & ~(src & pattern))); - break; - case 0x26: - out = src ^ (dst | (pattern & src)); - break; - case 0x27: - out = src ^ (dst | ~(pattern ^ src)); - break; - case 0x28: - out = dst & (pattern ^ src); - break; - case 0x29: - out = ~(pattern ^ (src ^ (dst | (pattern & src)))); - break; - case 0x2a: - out = dst & ~(pattern & src); - break; - case 0x2b: - out = ~(src ^ ((src ^ pattern) & (pattern ^ dst))); - break; - case 0x2c: - out = src ^ (pattern & (dst | src)); - break; - case 0x2d: - out = pattern ^ (src | ~dst); - break; - case 0x2e: - out = pattern ^ (src | (dst ^ pattern)); - break; - case 0x2f: - out = ~(pattern & (src | ~dst)); - break; - case 0x30: - out = pattern & ~src; - break; - case 0x31: - out = ~(src | (dst & ~pattern)); - break; - case 0x32: - out = src ^ (dst | (pattern | src)); - break; - case 0x33: - out = ~src; - break; - case 0x34: - out = src ^ (pattern | (dst & src)); - break; - case 0x35: - out = src ^ (pattern | ~(dst ^ src)); - break; - case 0x36: - out = src ^ (dst | pattern); - break; - case 0x37: - out = ~(src & (dst | pattern)); - break; - case 0x38: - out = pattern ^ (src & (dst | pattern)); - break; - case 0x39: - out = src ^ (pattern | ~dst); - break; - case 0x3a: - out = src ^ (pattern | (dst ^ src)); - break; - case 0x3b: - out = ~(src & (pattern | ~dst)); - break; - case 0x3c: - out = pattern ^ src; - break; - case 0x3d: - out = src ^ (pattern | ~(dst | src)); - break; - case 0x3e: - out = src ^ (pattern | (dst & ~src)); - break; - case 0x3f: - out = ~(pattern & src); - break; - case 0x40: - out = pattern & (src & ~dst); - break; - case 0x41: - out = ~(dst | (pattern ^ src)); - break; - case 0x42: - out = (src ^ dst) & (pattern ^ dst); - break; - case 0x43: - out = ~(src ^ (pattern & ~(dst & src))); - break; - case 0x44: - out = src & ~dst; - break; - case 0x45: - out = ~(dst | (pattern & ~src)); - break; - case 0x46: - out = dst ^ (src | (pattern & dst)); - break; - case 0x47: - out = ~(pattern ^ (src & (dst ^ pattern))); - break; - case 0x48: - out = src & (dst ^ pattern); - break; - case 0x49: - out = ~(pattern ^ (dst ^ (src | (pattern & dst)))); - break; - case 0x4a: - out = dst ^ (pattern & (src | dst)); - break; - case 0x4b: - out = pattern ^ (dst | ~src); - break; - case 0x4c: - out = src & ~(dst & pattern); - break; - case 0x4d: - out = ~(src ^ ((src ^ pattern) | (dst ^ src))); - break; - case 0x4e: - out = pattern ^ (dst | (src ^ pattern)); - break; - case 0x4f: - out = ~(pattern & (dst | ~src)); - break; - case 0x50: - out = pattern & ~dst; - break; - case 0x51: - out = ~(dst | (src & ~pattern)); - break; - case 0x52: - out = dst ^ (pattern | (src & dst)); - break; - case 0x53: - out = ~(src ^ (pattern & (dst ^ src))); - break; - case 0x54: - out = ~(dst | ~(pattern | src)); - break; - case 0x55: - out = ~dst; - break; - case 0x56: - out = dst ^ (pattern | src); - break; - case 0x57: - out = ~(dst & (pattern | src)); - break; - case 0x58: - out = pattern ^ (dst & (src | pattern)); - break; - case 0x59: - out = dst ^ (pattern | ~src); - break; - case 0x5a: - out = dst ^ pattern; - break; - case 0x5b: - out = dst ^ (pattern | ~(src | dst)); - break; - case 0x5c: - out = dst ^ (pattern | (src ^ dst)); - break; - case 0x5d: - out = ~(dst & (pattern | ~src)); - break; - case 0x5e: - out = dst ^ (pattern | (src & ~dst)); - break; - case 0x5f: - out = ~(dst & pattern); - break; - case 0x60: - out = pattern & (dst ^ src); - break; - case 0x61: - out = ~(dst ^ (src ^ (pattern | (dst & src)))); - break; - case 0x62: - out = dst ^ (src & (pattern | dst)); - break; - case 0x63: - out = src ^ (dst | ~pattern); - break; - case 0x64: - out = src ^ (dst & (pattern | src)); - break; - case 0x65: - out = dst ^ (src | ~pattern); - break; - case 0x66: - out = dst ^ src; - break; - case 0x67: - out = src ^ (dst | ~(pattern | src)); - break; - case 0x68: - out = ~(dst ^ (src ^ (pattern | ~(dst | src)))); - break; - case 0x69: - out = ~(pattern ^ (dst ^ src)); - break; - case 0x6a: - out = dst ^ (pattern & src); - break; - case 0x6b: - out = ~(pattern ^ (src ^ (dst & (pattern | src)))); - break; - case 0x6c: - out = src ^ (dst & pattern); - break; - case 0x6d: - out = ~(pattern ^ (dst ^ (src & (pattern | dst)))); - break; - case 0x6e: - out = src ^ (dst & (pattern | ~src)); - break; - case 0x6f: - out = ~(pattern & ~(dst ^ src)); - break; - case 0x70: - out = pattern & ~(dst & src); - break; - case 0x71: - out = ~(src ^ ((src ^ dst) & (pattern ^ dst))); - break; - case 0x72: - out = src ^ (dst | (pattern ^ src)); - break; - case 0x73: - out = ~(src & (dst | ~pattern)); - break; - case 0x74: - out = dst ^ (src | (pattern ^ dst)); - break; - case 0x75: - out = ~(dst & (src | ~pattern)); - break; - case 0x76: - out = src ^ (dst | (pattern & ~src)); - break; - case 0x77: - out = ~(dst & src); - break; - case 0x78: - out = pattern ^ (dst & src); - break; - case 0x79: - out = ~(dst ^ (src ^ (pattern & (dst | src)))); - break; - case 0x7a: - out = dst ^ (pattern & (src | ~dst)); - break; - case 0x7b: - out = ~(src & ~(dst ^ pattern)); - break; - case 0x7c: - out = src ^ (pattern & (dst | ~src)); - break; - case 0x7d: - out = ~(dst & ~(pattern ^ src)); - break; - case 0x7e: - out = (src ^ pattern) | (dst ^ src); - break; - case 0x7f: - out = ~(dst & (pattern & src)); - break; - case 0x80: - out = dst & (pattern & src); - break; - case 0x81: - out = ~((src ^ pattern) | (dst ^ src)); - break; - case 0x82: - out = dst & ~(pattern ^ src); - break; - case 0x83: - out = ~(src ^ (pattern & (dst | ~src))); - break; - case 0x84: - out = src & ~(dst ^ pattern); - break; - case 0x85: - out = ~(pattern ^ (dst & (src | ~pattern))); - break; - case 0x86: - out = dst ^ (src ^ (pattern & (dst | src))); - break; - case 0x87: - out = ~(pattern ^ (dst & src)); - break; - case 0x88: - out = dst & src; - break; - case 0x89: - out = ~(src ^ (dst | (pattern & ~src))); - break; - case 0x8a: - out = dst & (src | ~pattern); - break; - case 0x8b: - out = ~(dst ^ (src | (pattern ^ dst))); - break; - case 0x8c: - out = src & (dst | ~pattern); - break; - case 0x8d: - out = ~(src ^ (dst | (pattern ^ src))); - break; - case 0x8e: - out = src ^ ((src ^ dst) & (pattern ^ dst)); - break; - case 0x8f: - out = ~(pattern & ~(dst & src)); - break; - case 0x90: - out = pattern & ~(dst ^ src); - break; - case 0x91: - out = ~(src ^ (dst & (pattern | ~src))); - break; - case 0x92: - out = dst ^ (pattern ^ (src & (dst | pattern))); - break; - case 0x93: - out = ~(src ^ (pattern & dst)); - break; - case 0x94: - out = pattern ^ (src ^ (dst & (pattern | src))); - break; - case 0x95: - out = ~(dst ^ (pattern & src)); - break; - case 0x96: - out = dst ^ (pattern ^ src); - break; - case 0x97: - out = pattern ^ (src ^ (dst | ~(pattern | src))); - break; - case 0x98: - out = ~(src ^ (dst | ~(pattern | src))); - break; - case 0x99: - out = ~(dst ^ src); - break; - case 0x9a: - out = dst ^ (pattern & ~src); - break; - case 0x9b: - out = ~(src ^ (dst & (pattern | src))); - break; - case 0x9c: - out = src ^ (pattern & ~dst); - break; - case 0x9d: - out = ~(dst ^ (src & (pattern | dst))); - break; - case 0x9e: - out = dst ^ (src ^ (pattern | (dst & src))); - break; - case 0x9f: - out = ~(pattern & (dst ^ src)); - break; - case 0xa0: - out = dst & pattern; - break; - case 0xa1: - out = ~(pattern ^ (dst | (src & ~pattern))); - break; - case 0xa2: - out = dst & (pattern | ~src); - break; - case 0xa3: - out = ~(dst ^ (pattern | (src ^ dst))); - break; - case 0xa4: - out = ~(pattern ^ (dst | ~(src | pattern))); - break; - case 0xa5: - out = ~(pattern ^ dst); - break; - case 0xa6: - out = dst ^ (src & ~pattern); - break; - case 0xa7: - out = ~(pattern ^ (dst & (src | pattern))); - break; - case 0xa8: - out = dst & (pattern | src); - break; - case 0xa9: - out = ~(dst ^ (pattern | src)); - break; - case 0xaa: - out = dst; - break; - case 0xab: - out = dst | ~(pattern | src); - break; - case 0xac: - out = src ^ (pattern & (dst ^ src)); - break; - case 0xad: - out = ~(dst ^ (pattern | (src & dst))); - break; - case 0xae: - out = dst | (src & ~pattern); - break; - case 0xaf: - out = dst | ~pattern; - break; - case 0xb0: - out = pattern & (dst | ~src); - break; - case 0xb1: - out = ~(pattern ^ (dst | (src ^ pattern))); - break; - case 0xb2: - out = src ^ ((src ^ pattern) | (dst ^ src)); - break; - case 0xb3: - out = ~(src & ~(dst & pattern)); - break; - case 0xb4: - out = pattern ^ (src & ~dst); - break; - case 0xb5: - out = ~(dst ^ (pattern & (src | dst))); - break; - case 0xb6: - out = dst ^ (pattern ^ (src | (dst & pattern))); - break; - case 0xb7: - out = ~(src & (dst ^ pattern)); - break; - case 0xb8: - out = pattern ^ (src & (dst ^ pattern)); - break; - case 0xb9: - out = ~(dst ^ (src | (pattern & dst))); - break; - case 0xba: - out = dst | (pattern & ~src); - break; - case 0xbb: - out = dst | ~src; - break; - case 0xbc: - out = src ^ (pattern & ~(dst & src)); - break; - case 0xbd: - out = ~((src ^ dst) & (pattern ^ dst)); - break; - case 0xbe: - out = dst | (pattern ^ src); - break; - case 0xbf: - out = dst | ~(pattern & src); - break; - case 0xc0: - out = pattern & src; - break; - case 0xc1: - out = ~(src ^ (pattern | (dst & ~src))); - break; - case 0xc2: - out = ~(src ^ (pattern | ~(dst | src))); - break; - case 0xc3: - out = ~(pattern ^ src); - break; - case 0xc4: - out = src & (pattern | ~dst); - break; - case 0xc5: - out = ~(src ^ (pattern | (dst ^ src))); - break; - case 0xc6: - out = src ^ (dst & ~pattern); - break; - case 0xc7: - out = ~(pattern ^ (src & (dst | pattern))); - break; - case 0xc8: - out = src & (dst | pattern); - break; - case 0xc9: - out = ~(src ^ (pattern | dst)); - break; - case 0xca: - out = dst ^ (pattern & (src ^ dst)); - break; - case 0xcb: - out = ~(src ^ (pattern | (dst & src))); - break; - case 0xcc: - out = src; - break; - case 0xcd: - out = src | ~(dst | pattern); - break; - case 0xce: - out = src | (dst & ~pattern); - break; - case 0xcf: - out = src | ~pattern; - break; - case 0xd0: - out = pattern & (src | ~dst); - break; - case 0xd1: - out = ~(pattern ^ (src | (dst ^ pattern))); - break; - case 0xd2: - out = pattern ^ (dst & ~src); - break; - case 0xd3: - out = ~(src ^ (pattern & (dst | src))); - break; - case 0xd4: - out = src ^ ((src ^ pattern) & (pattern ^ dst)); - break; - case 0xd5: - out = ~(dst & ~(pattern & src)); - break; - case 0xd6: - out = pattern ^ (src ^ (dst | (pattern & src))); - break; - case 0xd7: - out = ~(dst & (pattern ^ src)); - break; - case 0xd8: - out = pattern ^ (dst & (src ^ pattern)); - break; - case 0xd9: - out = ~(src ^ (dst | (pattern & src))); - break; - case 0xda: - out = dst ^ (pattern & ~(src & dst)); - break; - case 0xdb: - out = ~((src ^ pattern) & (dst ^ src)); - break; - case 0xdc: - out = src | (pattern & ~dst); - break; - case 0xdd: - out = src | ~dst; - break; - case 0xde: - out = src | (dst ^ pattern); - break; - case 0xdf: - out = src | ~(dst & pattern); - break; - case 0xe0: - out = pattern & (dst | src); - break; - case 0xe1: - out = ~(pattern ^ (dst | src)); - break; - case 0xe2: - out = dst ^ (src & (pattern ^ dst)); - break; - case 0xe3: - out = ~(pattern ^ (src | (dst & pattern))); - break; - case 0xe4: - out = src ^ (dst & (pattern ^ src)); - break; - case 0xe5: - out = ~(pattern ^ (dst | (src & pattern))); - break; - case 0xe6: - out = src ^ (dst & ~(pattern & src)); - break; - case 0xe7: - out = ~((src ^ pattern) & (pattern ^ dst)); - break; - case 0xe8: - out = src ^ ((src ^ pattern) & (dst ^ src)); - break; - case 0xe9: - out = ~(dst ^ (src ^ (pattern & ~(dst & src)))); - break; - case 0xea: - out = dst | (pattern & src); - break; - case 0xeb: - out = dst | ~(pattern ^ src); - break; - case 0xec: - out = src | (dst & pattern); - break; - case 0xed: - out = src | ~(dst ^ pattern); - break; - case 0xee: - out = dst | src; - break; - case 0xef: - out = src | (dst | ~pattern); - break; - case 0xf0: - out = pattern; - break; - case 0xf1: - out = pattern | ~(dst | src); - break; - case 0xf2: - out = pattern | (dst & ~src); - break; - case 0xf3: - out = pattern | ~src; - break; - case 0xf4: - out = pattern | (src & ~dst); - break; - case 0xf5: - out = pattern | ~dst; - break; - case 0xf6: - out = pattern | (dst ^ src); - break; - case 0xf7: - out = pattern | ~(dst & src); - break; - case 0xf8: - out = pattern | (dst & src); - break; - case 0xf9: - out = pattern | ~(dst ^ src); - break; - case 0xfa: - out = dst | pattern; - break; - case 0xfb: - out = dst | (pattern | ~src); - break; - case 0xfc: - out = pattern | src; - break; - case 0xfd: - out = pattern | (src | ~dst); - break; - case 0xfe: - out = dst | (pattern | src); - break; - case 0xff: - out = ~0; - break; - } - - return out; -} \ No newline at end of file diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index f1d755dba..98a9cb385 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -11,7 +11,7 @@ # Authors: David Hrdlička, # # Copyright 2020-2021 David Hrdlička. -# Copyright 2025 Connor Hyde / starfrost +# Copyright 2025 starfrost # add_library(vid OBJECT @@ -136,77 +136,12 @@ add_library(vid OBJECT # Matrox vid_mga.c - # NVidia - Core - nv/nv_base.c + # NVidia (pending) nv/nv_rivatimer.c - # NVidia NV1 - nv/nv1/nv1_core.c - nv/nv1/nv1_core_config.c - - # NVidia RIVA 128 - Subsystems - nv/nv3/nv3_core.c - nv/nv3/nv3_core_config.c - nv/nv3/nv3_core_arbiter.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 - nv/nv3/subsystems/nv3_user.c - - # NVidia RIVA 128 - Object Classes - nv/nv3/classes/nv3_class_names.c - nv/nv3/classes/nv3_class_shared_methods.c - nv/nv3/classes/nv3_class_001_beta_factor.c - nv/nv3/classes/nv3_class_002_rop.c - nv/nv3/classes/nv3_class_003_chroma_key.c - nv/nv3/classes/nv3_class_004_plane_mask.c - nv/nv3/classes/nv3_class_005_clipping_rectangle.c - nv/nv3/classes/nv3_class_006_pattern.c - nv/nv3/classes/nv3_class_007_rectangle.c - nv/nv3/classes/nv3_class_008_point.c - nv/nv3/classes/nv3_class_009_line.c - nv/nv3/classes/nv3_class_00a_lin.c - nv/nv3/classes/nv3_class_00b_triangle.c - nv/nv3/classes/nv3_class_00c_win95_gdi_text.c - nv/nv3/classes/nv3_class_00d_m2mf.c - nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c - nv/nv3/classes/nv3_class_010_blit.c - nv/nv3/classes/nv3_class_011_image.c - nv/nv3/classes/nv3_class_012_bitmap.c - nv/nv3/classes/nv3_class_014_transfer2memory.c - nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c - nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c - nv/nv3/classes/nv3_class_018_point_zeta_buffer.c - nv/nv3/classes/nv3_class_01c_image_in_memory.c - - # NVidia RIVA 128 - Render - nv/nv3/render/nv3_render_core.c - nv/nv3/render/nv3_render_primitives.c - nv/nv3/render/nv3_render_blit.c - - # NVidia RIVA TNT/TNT2 - Core - nv/nv4/nv4_core.c - nv/nv4/nv4_core_io.c - nv/nv4/nv4_core_config.c - nv/nv4/nv4_debug_register_list.c - nv/nv4/subsystems/nv4_pramdac.c - nv/nv4/subsystems/nv4_ptimer.c - # Generic vid_bochs_vbe.c + ) if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") diff --git a/src/video/nv/nv1/nv1_core.c b/src/video/nv/nv1/nv1_core.c deleted file mode 100644 index 3de528b26..000000000 --- a/src/video/nv/nv1/nv1_core.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv1.h> - - -void nv1_init() -{ - -} - -void* nv1_init_edge2k(const device_t *info) -{ - -} - -void* nv1_init_edge3k(const device_t *info) -{ - -} - -void nv1_close(void* priv) -{ - -} - -void nv1_speed_changed(void *priv) -{ - -} - -void nv1_draw_cursor(svga_t* svga, int32_t drawline) -{ - -} - -void nv1_recalc_timings(svga_t* svga) -{ - -} - -void nv1_force_redraw(void* priv) -{ - -} - -// See if the bios rom is available. -int32_t nv1_available(void) -{ - return (rom_present(NV1_VBIOS_E3D_2X00) - || rom_present(NV1_VBIOS_E3D_3X00)); -} - -// NV3 (RIVA 128) -// PCI -// 2MB or 4MB VRAM -const device_t nv1_device_edge2k = -{ - .name = "nVIDIA NV1 [Diamond Edge 3D 2x00] [Not Direct3D Compatible]", - .internal_name = "nv1_edge2k", - .flags = DEVICE_PCI, - .local = 0, - .init = nv1_init_edge2k, - .close = nv1_close, - .speed_changed = nv1_speed_changed, - .force_redraw = nv1_force_redraw, - .available = nv1_available, - .config = nv1_config, -}; - -// NV3 (RIVA 128) -// AGP -// 2MB or 4MB VRAM -const device_t nv1_device_edge3k = -{ - .name = "nVIDIA NV1 [Diamond Edge 3D 3x00] [Not Direct3D Compatible]", - .internal_name = "nv1_edge3k", - .flags = DEVICE_PCI, - .local = 0, - .init = nv1_init_edge3k, - .close = nv1_close, - .speed_changed = nv1_speed_changed, - .force_redraw = nv1_force_redraw, - .available = nv1_available, - .config = nv1_config, -}; diff --git a/src/video/nv/nv1/nv1_core_config.c b/src/video/nv/nv1/nv1_core_config.c deleted file mode 100644 index 460067f6f..000000000 --- a/src/video/nv/nv1/nv1_core_config.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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. - * - * Provides NV4 configuration - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv1.h> - -const device_config_t nv1_config[] = -{ - // Memory configuration - { - .name = "vram_size", - .description = "VRAM Size", - .type = CONFIG_SELECTION, - .default_int = NV1_VRAM_SIZE_4MB, - .selection = - { - // I thought this was never released, but it seems that at least one was released: - // The card was called the "NEC G7AGK" - { - .description = "1 MB", - .value = NV1_VRAM_SIZE_1MB, - }, - { - .description = "2 MB", - .value = NV1_VRAM_SIZE_2MB, - }, - { - .description = "4 MB", - .value = NV1_VRAM_SIZE_4MB, - }, - } - - }, - // Multithreading configuration - { - - .name = "pgraph_threads", -#ifndef RELEASE_BUILD - .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", -#else - .description = "Render threads", -#endif - .type = CONFIG_SELECTION, - .default_int = 1, // todo: change later - .selection = - { - { - .description = "1 thread (Only use if issues appear with more threads)", - .value = 1, - }, - { - .description = "2 threads", - .value = 2, - }, - { - .description = "4 threads", - .value = 4, - }, - { - .description = "8 threads", - .value = 8, - }, - }, - }, - { - .name = "RAMDAC Type", - .description = "SGS-Thomson RAMDAC type", - .default_int = 0x1764, - .type = CONFIG_SELECTION, - .selection = - { - { - .description = "SGS-Thomson STG-1732X", - .value = 0x1732, - }, - { - .description = "SGS-Thomson STG-1764X/NVDAC64", - .value = 0x1764, - }, - } - }, - { - .name = "Chip type", - .description = "Chip type", - .default_int = 0x1, - .type = CONFIG_SELECTION, - .selection = - { - { - .description = "SGS-Thomson STG-2000", - .value = 0x2000, - }, - { - .description = "Nvidia NV1", - .value = 0x1, - }, - } - }, -#ifndef RELEASE_BUILD - { - .name = "nv_debug_fulllog", - .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", - .type = CONFIG_BINARY, - .default_int = 0, - }, -#endif - { - .type = CONFIG_END - } -}; \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c b/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c deleted file mode 100644 index 4520a941f..000000000 --- a/src/video/nv/nv3/classes/nv3_class_001_beta_factor.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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: Methods for class 0x01 (Beta factor) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_001_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - /* even if we don't do anything with this yet... */ - case NV3_BETA_FACTOR: - if (param & 0x80000000) /* bit0 */ - nv3->pgraph.beta_factor = 0; - else - nv3->pgraph.beta_factor = param & 0x7F800000; - - nv_log("Method Execution: Beta Factor = %02x", nv3->pgraph.beta_factor); - - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_002_rop.c b/src/video/nv/nv3/classes/nv3_class_002_rop.c deleted file mode 100644 index 56b135888..000000000 --- a/src/video/nv/nv3/classes/nv3_class_002_rop.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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: Methods for class 0x02 (Render operation) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_002_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_ROP_SET_ROP: - nv3->pgraph.rop = param & 0xFF; - nv_log("Method Execution: ROP = %02x\n", nv3->pgraph.rop); - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c b/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c deleted file mode 100644 index f19a06932..000000000 --- a/src/video/nv/nv3/classes/nv3_class_003_chroma_key.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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: Methods for class 0x02 (Chroma/color key) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_003_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_CHROMA_UNKNOWN_0200: - nv_log("Method Execution: Chroma Unknown 0x0200 0x%08x", param); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - - break; - case NV3_CHROMA_KEY: - { - nv3_color_expanded_t expanded_color = nv3_render_expand_color(param, grobj); - - nv3->pgraph.chroma_key = nv3_render_to_chroma(expanded_color); - - nv_log("Method Execution: Chroma = 0x%08x", nv3->pgraph.chroma_key); - break; - } - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c b/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c deleted file mode 100644 index d93de9bc1..000000000 --- a/src/video/nv/nv3/classes/nv3_class_004_plane_mask.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x02 (Plane mask) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_004_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c b/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c deleted file mode 100644 index c7ec3ae98..000000000 --- a/src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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: Methods for class 0x05 (Clipping rectangle) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_005_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_CLIP_POSITION: - nv3->pgraph.clip_start.x = (param >> 16) & 0xFFFF; - nv3->pgraph.clip_start.y = (param) & 0xFFFF; - nv_log("Method Execution: Clip Position: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); - break; - case NV3_CLIP_SIZE: - nv3->pgraph.clip_size.x = (param >> 16) & 0xFFFF; - nv3->pgraph.clip_size.y = (param) & 0xFFFF; - nv_log("Method Execution: Clip Size: %d,%d\n", nv3->pgraph.clip_start.x, nv3->pgraph.clip_start.y); - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_006_pattern.c b/src/video/nv/nv3/classes/nv3_class_006_pattern.c deleted file mode 100644 index 5aa5bf0fc..000000000 --- a/src/video/nv/nv3/classes/nv3_class_006_pattern.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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: Methods for class 0x06 (Pattern) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_006_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - /* Valid software method, suppress logging */ - case NV3_PATTERN_FORMAT: - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - case NV3_PATTERN_SHAPE: - /* If the shape is not valid, tell the software that it's invalid */ - - /* - Technically you are meant to do this: - - But in practice, I don't know, because it always submits 0x20 or 0x40, which are valid when param & 0x03, - and appear to be deliberate behaviour in the drivers rather than bugs. What - if (param > NV3_PATTERN_SHAPE_LAST_VALID) - { - warning("NV3 class 0x06 (Pattern) invalid shape %d (This is a bug)", param); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_INVALID_DATA); - return; - } - - */ - nv3->pgraph.pattern_shape = param & 0x03; - - break; - /* Seems to be "SetPatternSelect" on Riva TNT and later, but possibly called by accident on Riva 128. There is no hardware equivalent for this. So let's just suppress - the warnings. */ - case NV3_PATTERN_UNUSED_DRIVER_BUG: - break; - case NV3_PATTERN_COLOR0: - { - nv3_color_expanded_t expanded_colour0 = nv3_render_expand_color(param, grobj); - nv3_render_set_pattern_color(expanded_colour0, false); - break; - } - case NV3_PATTERN_COLOR1: - { - nv3_color_expanded_t expanded_colour1 = nv3_render_expand_color(param, grobj); - nv3_render_set_pattern_color(expanded_colour1, true); - break; - } - case NV3_PATTERN_BITMAP_HIGH: - nv3->pgraph.pattern_bitmap = 0; //reset - nv3->pgraph.pattern_bitmap |= ((uint64_t)param << 32); - break; - case NV3_PATTERN_BITMAP_LOW: - nv3->pgraph.pattern_bitmap |= param; - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c b/src/video/nv/nv3/classes/nv3_class_007_rectangle.c deleted file mode 100644 index d1273a84a..000000000 --- a/src/video/nv/nv3/classes/nv3_class_007_rectangle.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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: Methods for class 0x07 (Rectangle) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_007_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_RECTANGLE_COLOR: - nv3->pgraph.rectangle.color = param; - break; - default: - /* Check for any rectangle point or size method. */ - if (method_id >= NV3_RECTANGLE_START && method_id <= NV3_RECTANGLE_END) - { - uint32_t index = (method_id - NV3_RECTANGLE_START) >> 3; - - // If the size is submitted, render it. - if (method_id & 0x04) - { - nv3->pgraph.rectangle.size[index].x = param & 0xFFFF; - nv3->pgraph.rectangle.size[index].y = (param >> 16) & 0xFFFF; - - nv_log("Method Execution: Rect%d Size=%d,%d Color=0x%08x\n", index, nv3->pgraph.rectangle.size[index].x, nv3->pgraph.rectangle.size[index].y, nv3->pgraph.rectangle.color); - - nv3_render_rect(nv3->pgraph.rectangle.position[index], nv3->pgraph.rectangle.size[index], nv3->pgraph.rectangle.color, grobj); - } - else // position - { - nv3->pgraph.rectangle.position[index].x = param & 0xFFFF; - nv3->pgraph.rectangle.position[index].y = (param >> 16) & 0xFFFF; - - nv_log("Method Execution: Rect%d Position=%d,%d\n", index, nv3->pgraph.rectangle.position[index].x, nv3->pgraph.rectangle.position[index].y); - } - - return; - } - - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_008_point.c b/src/video/nv/nv3/classes/nv3_class_008_point.c deleted file mode 100644 index 41002f745..000000000 --- a/src/video/nv/nv3/classes/nv3_class_008_point.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x08 (Point) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_008_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_009_line.c b/src/video/nv/nv3/classes/nv3_class_009_line.c deleted file mode 100644 index f149bb8b3..000000000 --- a/src/video/nv/nv3/classes/nv3_class_009_line.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x09 (Line) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_009_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00a_lin.c b/src/video/nv/nv3/classes/nv3_class_00a_lin.c deleted file mode 100644 index 473aaa214..000000000 --- a/src/video/nv/nv3/classes/nv3_class_00a_lin.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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: Methods for class 0x0A (Lin - a line without starting or ending pixels) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_00a_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c b/src/video/nv/nv3/classes/nv3_class_00b_triangle.c deleted file mode 100644 index d08b7a72e..000000000 --- a/src/video/nv/nv3/classes/nv3_class_00b_triangle.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x0B (Basic triangle) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_00b_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c b/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c deleted file mode 100644 index 64d76c278..000000000 --- a/src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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: Methods for class 0x0C (Windows 95 GDI text acceleration) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_00c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - /* Type A: Unclipped Rectangle */ - - /* NOTE: This method is used by the GDI driver as part of the notification engine. */ - case NV3_W95TXT_A_COLOR: - nv3->pgraph.win95_gdi_text.color_a = param; - nv_log("Method Execution: GDI-A Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); - break; - /* Type B: Clipped Rectangle */ - case NV3_W95TXT_B_COLOR: - nv3->pgraph.win95_gdi_text.color_b = param; - nv_log("Method Execution: GDI-B Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_b); - break; - case NV3_W95TXT_B_CLIP_TOPLEFT: - nv3->pgraph.win95_gdi_text.clip_b.left = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_b.top = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-B Clip Left,Top %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.left, nv3->pgraph.win95_gdi_text.clip_b.top); - break; - case NV3_W95TXT_B_CLIP_BOTTOMRIGHT: - nv3->pgraph.win95_gdi_text.clip_b.bottom = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_b.right = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-B Clip Bottom,Right %04x,%04x", nv3->pgraph.win95_gdi_text.clip_b.right, nv3->pgraph.win95_gdi_text.clip_b.bottom); - break; - /* Type C: Unclipped Bitmap */ - case NV3_W95TXT_C_CLIP_COLOR: - nv3->pgraph.win95_gdi_text.color1_c = param; - nv_log("Method Execution: GDI-C Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color1_c); - break; - case NV3_W95TXT_C_CLIP_SIZE: - nv3->pgraph.win95_gdi_text.size_c.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_c.y = ((param >> 16) & 0xFFFF); - - nv3->pgraph.win95_gdi_text_bit_count = 0; - nv_log("Method Execution: GDI-C Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_c.x, nv3->pgraph.win95_gdi_text.size_c.y); - break; - case NV3_W95TXT_C_CLIP_POSITION: - nv3->pgraph.win95_gdi_text.point_c.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.point_c.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-C Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_c.x, nv3->pgraph.win95_gdi_text.point_c.y); - - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_c.x ; - nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_c.y; - - break; - case NV3_W95TXT_C_CLIP_TOPLEFT: - nv3->pgraph.win95_gdi_text.clip_c.left = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_c.top = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-C Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.top); - break; - case NV3_W95TXT_C_CLIP_BOTTOMRIGHT: - nv3->pgraph.win95_gdi_text.clip_c.right = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_c.bottom = ((param >> 16) & 0xFFFF); - /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ - nv_log("Method Execution: GDI-C Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.top); - break; - /* Type B and C not implemented YET, as they are not used by NT GDI driver */ - case NV3_W95TXT_D_CLIP_TOPLEFT: - nv3->pgraph.win95_gdi_text.clip_d.left = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_d.top = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Clip Left,Top %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); - break; - case NV3_W95TXT_D_CLIP_BOTTOMRIGHT: - nv3->pgraph.win95_gdi_text.clip_d.right = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_d.bottom = ((param >> 16) & 0xFFFF); - /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ - nv_log("Method Execution: GDI-D Clip Right,Bottom %04x,%04x\n", nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.top); - break; - - case NV3_W95TXT_D_CLIP_COLOR: - nv3->pgraph.win95_gdi_text.color1_d = param; - nv_log("Method Execution: GDI-D Color 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); - break; - case NV3_W95TXT_D_CLIP_SIZE_IN: - nv3->pgraph.win95_gdi_text.size_in_d.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_in_d.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_d.x, nv3->pgraph.win95_gdi_text.size_in_d.y); - break; - case NV3_W95TXT_D_CLIP_SIZE_OUT: - nv3->pgraph.win95_gdi_text.size_out_d.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_out_d.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_d.x, nv3->pgraph.win95_gdi_text.size_out_d.y); - break; - case NV3_W95TXT_D_CLIP_POSITION: - nv3->pgraph.win95_gdi_text.point_d.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.point_d.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-D Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y); - - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; - nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_d.y; - nv3->pgraph.win95_gdi_text_bit_count = 0; - - break; - /* Type E: Two-colour 1bpp */ - case NV3_W95TXT_E_CLIP_TOPLEFT: - nv3->pgraph.win95_gdi_text.clip_e.left = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_e.top = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Clip Left,Top 0x%08x\n", nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.top); - break; - case NV3_W95TXT_E_CLIP_BOTTOMRIGHT: - nv3->pgraph.win95_gdi_text.clip_e.right = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clip_e.bottom = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Clip Bottom,Right 0x%08x\n", nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.bottom); - /* is it "only if we are out of the top left or the bottom right or is it "all of them"*/ - break; - case NV3_W95TXT_E_CLIP_COLOR_0: - nv3->pgraph.win95_gdi_text.color0_e = param; - nv_log("Method Execution: GDI-E Color0 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); - break; - case NV3_W95TXT_E_CLIP_COLOR_1: - nv3->pgraph.win95_gdi_text.color1_e = param; - nv_log("Method Execution: GDI-E Color1 0x%08x\n", nv3->pgraph.win95_gdi_text.color_a); - break; - case NV3_W95TXT_E_CLIP_SIZE_IN: - nv3->pgraph.win95_gdi_text.size_in_e.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_in_e.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Size In %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_in_e.x, nv3->pgraph.win95_gdi_text.size_in_e.y); - break; - case NV3_W95TXT_E_CLIP_SIZE_OUT: - nv3->pgraph.win95_gdi_text.size_out_e.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.size_out_e.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: GDI-E Size Out %04x,%04x\n", nv3->pgraph.win95_gdi_text.size_out_e.x, nv3->pgraph.win95_gdi_text.size_out_e.y); - break; - case NV3_W95TXT_E_CLIP_POSITION: - nv3->pgraph.win95_gdi_text.point_e.x = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.point_e.y = ((param >> 16) & 0xFFFF); - - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; - nv3->pgraph.win95_gdi_text_current_position.y = nv3->pgraph.win95_gdi_text.point_e.y; - nv3->pgraph.win95_gdi_text_bit_count = 0; - - nv_log("Method Execution: GDI-E Point %04x,%04x\n", nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y); - break; - default: - /* Type A submission: these are the same things as rectangles */ - if (method_id >= NV3_W95TXT_A_RECT_START && method_id <= NV3_W95TXT_A_RECT_END) - { - uint32_t index = (method_id - NV3_W95TXT_A_RECT_START) >> 3; - - // IN THIS ONE SPECIFIC PLACE, ****AND ONLY THIS ONE SPECIFIC PLACE****, X AND Y ARE SWAPPED???? */ - // If the size is submitted, render it. - if (method_id & 0x04) - { - nv3->pgraph.win95_gdi_text.rect_a_size[index].x = (param >> 16) & 0xFFFF; - nv3->pgraph.win95_gdi_text.rect_a_size[index].y = param & 0xFFFF; - - nv_log("Method Execution: Rect GDI-A%d Size=%d,%d", index, nv3->pgraph.win95_gdi_text.rect_a_size[index].x, - nv3->pgraph.win95_gdi_text.rect_a_size[index].y); - - nv3_render_rect(nv3->pgraph.win95_gdi_text.rect_a_position[index], - nv3->pgraph.win95_gdi_text.rect_a_size[index], nv3->pgraph.win95_gdi_text.color_a, grobj); - } - else // position - { - nv3->pgraph.win95_gdi_text.rect_a_position[index].x = (param >> 16) & 0xFFFF; - nv3->pgraph.win95_gdi_text.rect_a_position[index].y = param & 0xFFFF; - - nv_log("Method Execution: Rect GDI-A%d Position=%d,%d\n", index, - nv3->pgraph.win95_gdi_text.rect_a_position[index].x, nv3->pgraph.win95_gdi_text.rect_a_position[index].y); - } - - return; - } - /* Type B: Clipped Rectangle */ - else if (method_id >= NV3_W95TXT_B_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_B_CLIP_CLIPRECT_END) - { - uint32_t index = (method_id - NV3_W95TXT_B_CLIP_CLIPRECT_START) >> 3; - - /* Works slightly differently, we define the bounds of the rectangle instead. */ - if (method_id & 0x04) - { - nv3->pgraph.win95_gdi_text.clipped_rect[index].right = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clipped_rect[index].bottom = ((param >> 16) & 0xFFFF); - - nv_log("Method Execution: Rect GDI-B%d Right,Bottom=%d,%d\n", index, nv3->pgraph.win95_gdi_text.clipped_rect[index].right, - nv3->pgraph.win95_gdi_text.clipped_rect[index].bottom); - - nv3_render_rect_clipped(nv3->pgraph.win95_gdi_text.clipped_rect[index], - nv3->pgraph.win95_gdi_text.color_b, grobj); - } - else // left,top - { - nv3->pgraph.win95_gdi_text.clipped_rect[index].left = (param & 0xFFFF); - nv3->pgraph.win95_gdi_text.clipped_rect[index].top = ((param >> 16) & 0xFFFF); - - nv_log("Method Execution: Rect GDI-B%d Left,Top=%d,%d\n", index, - nv3->pgraph.win95_gdi_text.clipped_rect[index].left, nv3->pgraph.win95_gdi_text.clipped_rect[index].top); - } - - return; - } - /* Type C */ - else if (method_id >= NV3_W95TXT_C_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_C_CLIP_CLIPRECT_END) - { - /* lol */ - uint32_t index = (method_id - NV3_W95TXT_C_CLIP_CLIPRECT_START) >> 3; - - nv3->pgraph.win95_gdi_text.bitmap_c[index] = param; - - /* Mammoth logger! */ - nv_log("Method Execution: Rect GDI-C%d Data=%08x Size%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_c.x, nv3->pgraph.win95_gdi_text.size_c.y, - nv3->pgraph.win95_gdi_text.point_c.x, nv3->pgraph.win95_gdi_text.point_c.y, - nv3->pgraph.win95_gdi_text.color1_c, - nv3->pgraph.win95_gdi_text.clip_c.left, nv3->pgraph.win95_gdi_text.clip_c.right, - nv3->pgraph.win95_gdi_text.clip_c.top, nv3->pgraph.win95_gdi_text.clip_c.bottom); - - nv3_render_gdi_transparent_bitmap(false, nv3->pgraph.win95_gdi_text.color1_c, nv3->pgraph.win95_gdi_text.bitmap_c[index], grobj); - return; - } - else if (method_id >= NV3_W95TXT_D_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_D_CLIP_CLIPRECT_END) - { - /* lol */ - uint32_t index = (method_id - NV3_W95TXT_D_CLIP_CLIPRECT_START) >> 3; - - nv3->pgraph.win95_gdi_text.bitmap_d[index] = param; - - /* Mammoth logger! */ - nv_log("Method Execution: Rect GDI-D%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_in_d.x, nv3->pgraph.win95_gdi_text.size_in_d.y, - nv3->pgraph.win95_gdi_text.size_out_d.x, nv3->pgraph.win95_gdi_text.size_out_d.y, - nv3->pgraph.win95_gdi_text.point_d.x, nv3->pgraph.win95_gdi_text.point_d.y, - nv3->pgraph.win95_gdi_text.color1_d, - nv3->pgraph.win95_gdi_text.clip_d.left, nv3->pgraph.win95_gdi_text.clip_d.right, - nv3->pgraph.win95_gdi_text.clip_d.top, nv3->pgraph.win95_gdi_text.clip_d.bottom); - - nv3_render_gdi_transparent_bitmap(true, nv3->pgraph.win95_gdi_text.color1_d, nv3->pgraph.win95_gdi_text.bitmap_d[index], grobj); - return; - } - else if (method_id >= NV3_W95TXT_E_CLIP_CLIPRECT_START && method_id <= NV3_W95TXT_E_CLIP_CLIPRECT_END) - { - /* lol */ - uint32_t index = (method_id - NV3_W95TXT_E_CLIP_CLIPRECT_START) >> 3; - - nv3->pgraph.win95_gdi_text.bitmap_e[index] = param; - - /* Mammoth logger! */ - nv_log("Method Execution: Rect GDI-E%d Data=%08x SizeIn%04x,%04x SizeOut%04x,%04x Point%04x,%04x Color=%08x Clip Left=0x%04x Right=0x%04x Top=0x%04x Bottom=0x%04x", - index, param, nv3->pgraph.win95_gdi_text.size_in_e.x, nv3->pgraph.win95_gdi_text.size_in_e.y, - nv3->pgraph.win95_gdi_text.size_out_e.x, nv3->pgraph.win95_gdi_text.size_out_e.y, - nv3->pgraph.win95_gdi_text.point_e.x, nv3->pgraph.win95_gdi_text.point_e.y, - nv3->pgraph.win95_gdi_text.color1_e, - nv3->pgraph.win95_gdi_text.clip_e.left, nv3->pgraph.win95_gdi_text.clip_e.right, nv3->pgraph.win95_gdi_text.clip_e.top, nv3->pgraph.win95_gdi_text.clip_e.bottom); - - nv3_render_gdi_1bpp_bitmap(nv3->pgraph.win95_gdi_text.color0_e, nv3->pgraph.win95_gdi_text.color1_e, nv3->pgraph.win95_gdi_text.bitmap_e[index], grobj); - return; - } - - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c b/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c deleted file mode 100644 index f07aab737..000000000 --- a/src/video/nv/nv3/classes/nv3_class_00d_m2mf.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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: Methods for class 0x0D (Reformat image in memory) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_M2MF_IN_CTXDMA_OFFSET: - nv3->pgraph.m2mf.offset_in = param; - nv_log("Method Execution: M2MF Offset In = 0x%08x", param); - break; - case NV3_M2MF_OUT_CTXDMA_OFFSET: - nv3->pgraph.m2mf.offset_out = param; - nv_log("Method Execution: M2MF Offset Out = 0x%08x", param); - break; - case NV3_M2MF_IN_PITCH: - nv3->pgraph.m2mf.pitch_in = param; - nv_log("Method Execution: M2MF Pitch In = 0x%08x", param); - break; - case NV3_M2MF_OUT_PITCH: - nv3->pgraph.m2mf.pitch_out = param; - nv_log("Method Execution: M2MF Pitch Out = 0x%08x", param); - break; - case NV3_M2MF_SCANLINE_LENGTH_IN_BYTES: - nv3->pgraph.m2mf.scanline_length = param; - nv_log("Method Execution: M2MF Scanline Length in Bytes = 0x%08x", param); - break; - case NV3_M2MF_NUM_SCANLINES: - nv3->pgraph.m2mf.num_scanlines = param; - nv_log("Method Execution: M2MF Num Scanlines = 0x%08x", param); - break; - case NV3_M2MF_FORMAT: - nv3->pgraph.m2mf.format = param; - nv_log("Method Execution: M2MF Format = 0x%08x", param); - - // Format Done - start m2mf - - nv3_perform_dma_m2mf(grobj); - - break; - case NV3_M2MF_NOTIFY: - /* This is technically its own thing, but I don't know if it's ever a problem with how we've designed it */ - if (nv3->pgraph.notify_pending) - { - nv_log("WARNING: M2MF notification with notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); - nv_log("IF THIS BUILD WAS COMPILED WITH NV_LOG_ENABLE_ULTRA, YOU SHOULD SEE A CONTEXT BELOW"); - nv3_debug_ramin_print_context_info(param, context); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - - // disable - nv3->pgraph.notify_pending = false; - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - /* may need to disable fifo in this state */ - return; - } - - nv_log("Method Execution: TODO: ACTUALLY IMPLEMENT M2MF!!!!"); - // set a notify as pending. - nv3->pgraph.notifier = param; - nv3->pgraph.notify_pending = true; - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break;; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c b/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c deleted file mode 100644 index 457c8ade1..000000000 --- a/src/video/nv/nv3/classes/nv3_class_00e_scaled_image_from_mem.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x0E (Get image from vram and scale it) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_00e_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_010_blit.c b/src/video/nv/nv3/classes/nv3_class_010_blit.c deleted file mode 100644 index ed1ac465b..000000000 --- a/src/video/nv/nv3/classes/nv3_class_010_blit.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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: Methods for class 0x10 (Blit something) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_010_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_BLIT_POSITION_IN: - nv3->pgraph.blit.point_in.x = (param & 0xFFFF); - nv3->pgraph.blit.point_in.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB POINT_IN %d,%d\n", nv3->pgraph.blit.point_in.x, nv3->pgraph.blit.point_in.y); - break; - case NV3_BLIT_POSITION_OUT: - nv3->pgraph.blit.point_out.x = (param & 0xFFFF); - nv3->pgraph.blit.point_out.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB POINT_OUT %d,%d\n", nv3->pgraph.blit.point_out.x, nv3->pgraph.blit.point_out.y); - - break; - case NV3_BLIT_SIZE: - /* This is the last one*/ - nv3->pgraph.blit.size.x = (param & 0xFFFF); - nv3->pgraph.blit.size.y = ((param >> 16) & 0xFFFF); - nv_log("Method Execution: S2SB Size %d,%d grobj_0=0x%08x\n", nv3->pgraph.blit.size.x, nv3->pgraph.blit.size.y, grobj.grobj_0); - - nv3_render_blit_screen2screen(grobj); - - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_011_image.c b/src/video/nv/nv3/classes/nv3_class_011_image.c deleted file mode 100644 index 1eda15df7..000000000 --- a/src/video/nv/nv3/classes/nv3_class_011_image.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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: Methods for class 0x11 (Color image) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_011_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - case NV3_IMAGE_START_POSITION: - nv3->pgraph.image.point.x = (param & 0xFFFF); - nv3->pgraph.image.point.y = (param >> 16); - nv_log("Method Execution: Image Point=%d,%d\n", nv3->pgraph.image.point.x, nv3->pgraph.image.point.y); - break; - /* Seems to allow scaling of the bitblt. */ - case NV3_IMAGE_SIZE: - nv3->pgraph.image.size.x = (param & 0xFFFF); - nv3->pgraph.image.size.y = (param >> 16); - nv_log("Method Execution: Image Size (Clip)=%d,%d\n", nv3->pgraph.image.size.x, nv3->pgraph.image.size.y); - break; - case NV3_IMAGE_SIZE_IN: - nv3->pgraph.image.size_in.x = (param & 0xFFFF); - nv3->pgraph.image.size_in.y = (param >> 16); - nv3->pgraph.image_current_position = nv3->pgraph.image.point; - nv_log("Method Execution: Image SizeIn=%d,%d\n", nv3->pgraph.image.size_in.x, nv3->pgraph.image.size_in.y); - break; - default: - if (method_id >= NV3_IMAGE_COLOR_START && method_id <= NV3_IMAGE_COLOR_END) - { - uint32_t pixel_slot = (method_id - NV3_IMAGE_COLOR_START) >> 2; - nv_log("Method Execution: Image Pixel%d Colour%08x Format%x\n", pixel_slot, param, (grobj.grobj_0) & 0x07); - nv3_render_blit_image(param, grobj); - } - else - { - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - - } - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c b/src/video/nv/nv3/classes/nv3_class_012_bitmap.c deleted file mode 100644 index 2ac47f0de..000000000 --- a/src/video/nv/nv3/classes/nv3_class_012_bitmap.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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: Methods for class 0x12 (Monochrome bitmap) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_012_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c b/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c deleted file mode 100644 index b49b1fc89..000000000 --- a/src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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: Methods for class 0x14 (Transfer to Memory) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_014_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c b/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c deleted file mode 100644 index 3fa88ba39..000000000 --- a/src/video/nv/nv3/classes/nv3_class_015_stretched_image_from_cpu.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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: Methods for class 0x15 (stretched image from cpu to memory) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_015_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c deleted file mode 100644 index 3bb8f66d7..000000000 --- a/src/video/nv/nv3/classes/nv3_class_017_d3d5_tri_zeta_buffer.c +++ /dev/null @@ -1,40 +0,0 @@ -/* -* 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: Methods for class 0x17 (Direct3D 5.0 accelerated triangle with zeta buffer) -* -* -* -* Authors: Connor Hyde, I need a better email address ;^) -* -* Copyright 2024-2025 Connor Hyde -*/ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_017_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c b/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c deleted file mode 100644 index 87111b70c..000000000 --- a/src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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: Methods for class 0x18 (Point with zeta buffer) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -struct nv3_object_class_018 nv3_d3d5_point_zeta_buffer; - -void nv3_class_018_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c b/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c deleted file mode 100644 index 12ac3fe6b..000000000 --- a/src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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: Methods for class 0x1C (Image in memory) - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_class_01c_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - /* We need this for a lot of methods, so may as well store it here. */ - uint32_t src_buffer_id = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; - - - switch (method_id) - { - /* Color format of the image */ - case NV3_IMAGE_IN_MEMORY_COLOR_FORMAT: - { - // convert to how the bpixel registers represent surface - uint32_t real_format = 1; - - /* TODO: THIS CODE MIGHT BE NONSENSE - Convert between different internal representations of the pixel format, because Nvidia says: I WANT TO MAKE YOUR LIFE PAIN. - */ - switch (param) - { - case nv3_image_in_memory_pixel_format_x8g8b8r8: - real_format = 3; //32bit - // no change - break; - case nv3_image_in_memory_pixel_format_x1r5g5b5_p2: - real_format = 2; - break; - case nv3_image_in_memory_pixel_format_le_y16_p2: - real_format = 0; - break; - } - - /* Set the format */ - - nv3->pgraph.bpixel[src_buffer_id] = (real_format | NV3_BPIXEL_FORMAT_IS_VALID); - - nv_log("Method Execution: Image in Memory BUF%d COLOR_FORMAT=0x%04x\n", src_buffer_id, param); - - break; - } - /* DOn't log invalid */ - case NV3_IMAGE_IN_MEMORY_IN_MEMORY_DMA_CTX_TYPE: - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - /* Pitch - length between scanlines */ - case NV3_IMAGE_IN_MEMORY_PITCH: - nv3->pgraph.image_in_memory.pitch = param & 0x1FF0; - nv3->pgraph.bpitch[src_buffer_id] = param & 0x1FF0; // 12:0 - - nv_log("Method Execution: Image in Memory BUF%d PITCH=0x%04x\n", src_buffer_id, nv3->pgraph.bpitch[src_buffer_id]); - break; - /* Byte offset in GPU VRAM of top left pixel (22:0) */ - case NV3_IMAGE_IN_MEMORY_TOP_LEFT_OFFSET: - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX - nv3->pgraph.boffset[src_buffer_id] = param & 0x7FFFFF; - else - nv3->pgraph.boffset[src_buffer_id] = param & 0x3FFFFF; - - nv_log("Method Execution: Image in Memory BUF%d TOP_LEFT_OFFSET=0x%08x\n", src_buffer_id, nv3->pgraph.boffset[src_buffer_id]); - break; - case NV3_NVCLASS_CRAP_START ... NV3_NVCLASS_CRAP_END: - /* Suppress but don't do anything */ - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - default: - warning("%s: Invalid or unimplemented method 0x%04x\n", nv3_class_names[context.class_id & 0x1F], method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_names.c b/src/video/nv/nv3/classes/nv3_class_names.c deleted file mode 100644 index 64e35d9b3..000000000 --- a/src/video/nv/nv3/classes/nv3_class_names.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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: Defines core class names for debugging purposes - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ -#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> - -/* These are the object classes AS RECOGNISED BY THE GRAPHICS HARDWARE. */ -/* The drivers implement a COMPLETELY DIFFERENT SET OF CLASSES. */ - -/* THERE CAN ONLY BE 32 CLASSES IN NV3 BECAUSE THE CLASS ID PART OF THE CONTEXT OF A GRAPHICS OBJECT IN PFIFO RAM HASH TABLE IS ONLY 5 BITS LONG! */ - -const char* nv3_class_names[] = -{ - "NV3 INVALID class 0x00", - "NV3 class 0x01: Beta factor", - "NV3 class 0x02: Render operation", - "NV3 class 0x03: Chroma key", - "NV3 class 0x04: Plane mask", - "NV3 class 0x05: Clipping rectangle", - "NV3 class 0x06: Pattern", - "NV3 class 0x07: Rectangle", - "NV3 class 0x08: Point", - "NV3 class 0x09: Line", - "NV3 class 0x0A: Lin (line without starting or ending pixel)", - "NV3 class 0x0B: Triangle", - "NV3 class 0x0C: Windows 95 GDI text acceleration", - "NV3 class 0x0D: Memory to memory format", - "NV3 class 0x0E: Scaled image from memory", - "NV3 INVALID class 0x0F", - "NV3 class 0x10: Blit", - "NV3 class 0x11: Image", - "NV3 class 0x12: Bitmap", - "NV3 INVALID class 0x13", - "NV3 class 0x14: Transfer to Memory", - "NV3 class 0x15: Stretched image from CPU", - "NV3 INVALID class 0x16", - "NV3 class 0x17: Direct3D 5.0 accelerated textured triangle w/zeta buffer", - "NV3 class 0x18: Point with zeta buffer", - "NV3 INVALID class 0x19", - "NV3 INVALID class 0x1A", - "NV3 INVALID class 0x1B", - "NV3 class 0x1C: Image in Memory", - "NV3 INVALID class 0x1D", - "NV3 INVALID class 0x1E", - "NV3 INVALID class 0x1F", -}; \ No newline at end of file diff --git a/src/video/nv/nv3/classes/nv3_class_shared_methods.c b/src/video/nv/nv3/classes/nv3_class_shared_methods.c deleted file mode 100644 index 89473d829..000000000 --- a/src/video/nv/nv3/classes/nv3_class_shared_methods.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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: Methods shared across multiple classes - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 Connor Hyde - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/dma.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_generic_method(uint32_t param, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - switch (method_id) - { - /* mthdCreate in software(?)*/ - case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE: - //nv_log("mthdCreate obj_name=0x%08x\n", param); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - break; - // set up the current notification request/object - // and check for double notifiers. - case NV3_SET_NOTIFY: - if (nv3->pgraph.notify_pending) - { - nv_log("Executed method NV3_SET_NOTIFY with nv3->pgraph.notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n"); - nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW"); - nv3_debug_ramin_print_context_info(param, context); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - - // disable - nv3->pgraph.notify_pending = false; - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY); - /* may need to disable fifo in this state */ - return; - } - - // set a notify as pending. - nv3->pgraph.notifier = param; - nv3->pgraph.notify_pending = true; - break; - default: - nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id); - nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING); - return; - } -} diff --git a/src/video/nv/nv3/nv3_core.c b/src/video/nv/nv3/nv3_core.c deleted file mode 100644 index 01af9100d..000000000 --- a/src/video/nv/nv3/nv3_core.c +++ /dev/null @@ -1,1567 +0,0 @@ -/* - * 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-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -/* Main device object pointer */ -nv3_t* nv3; - -/* These are a ****PLACEHOLDER**** and are copied from 3dfx VoodooBanshee/Voodoo3*/ -static video_timings_t timing_nv3_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; -static video_timings_t timing_nv3_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; -// Revision C -static video_timings_t timing_nv3t_pci = { .type = VIDEO_PCI, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; -static video_timings_t timing_nv3t_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; - -// Prototypes for functions only used in this translation unit -void nv3_init_mappings_mmio(void); -void nv3_init_mappings_svga(void); -bool nv3_is_svga_redirect_address(uint32_t addr); - -uint8_t nv3_svga_read(uint16_t addr, void* priv); -void nv3_svga_write(uint16_t addr, uint8_t val, void* priv); - -// Determine if this address needs to be redirected to the SVGA subsystem. - -bool nv3_is_svga_redirect_address(uint32_t addr) -{ - return (addr >= NV3_PRMVIO_START && addr <= NV3_PRMVIO_END) // VGA - || (addr >= NV3_PRMCIO_START && addr <= NV3_PRMCIO_END) // CRTC - || (addr >= NV3_USER_DAC_START && addr <= NV3_USER_DAC_END); // Note: 6813c6-6813c9 are ignored somewhere else -} - -// 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) -{ - uint32_t ret = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - // We need to specifically exclude this particular set of registers - // so we can write the 4/8bpp CLUT - if (addr >= NV3_USER_DAC_PALETTE_START && addr <= NV3_USER_DAC_PALETTE_END) - { - // Throw directly into PRAMDAC - return nv3_mmio_arbitrate_read(addr); - } - - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv3_svga_read(real_address, nv3); - - nv_log_verbose_only("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - - // see if unaligned reads are a problem - 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 = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv3_svga_read(real_address, nv3) - | (nv3_svga_read(real_address + 1, nv3) << 8); - - nv_log_verbose_only("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - - 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) -{ - uint32_t ret = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv3_svga_read(real_address, nv3) - | (nv3_svga_read(real_address + 1, nv3) << 8) - | (nv3_svga_read(real_address + 2, nv3) << 16) - | (nv3_svga_read(real_address + 3, nv3) << 24); - - nv_log_verbose_only("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - ret = nv3_mmio_arbitrate_read(addr); - return ret; - -} - -// Write 8-bit MMIO -void nv3_mmio_write8(uint32_t addr, uint8_t val, void* priv) -{ - addr &= 0xFFFFFF; - - // We need to specifically exclude this particular set of registers - // so we can write the 4/8bpp CLUT - if (addr >= NV3_USER_DAC_PALETTE_START && addr <= NV3_USER_DAC_PALETTE_END) - { - // Throw directly into PRAMDAC - nv3_mmio_arbitrate_write(addr, val); - return; - } - - // This is weitek vga stuff - // If we need to add more of these we can convert these to a switch statement - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv3_svga_write(real_address, val & 0xFF, nv3); - - return; - } - - // 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) -{ - addr &= 0xFFFFFF; - - // This is weitek vga stuff - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv3_svga_write(real_address, val & 0xFF, nv3); - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); - - return; - } - - // 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) -{ - addr &= 0xFFFFFF; - - // This is weitek vga stuff - if (nv3_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv3_svga_write(real_address, val & 0xFF, nv3); - nv3_svga_write(real_address + 1, (val >> 8) & 0xFF, nv3); - nv3_svga_write(real_address + 2, (val >> 16) & 0xFF, nv3); - nv3_svga_write(real_address + 3, (val >> 24) & 0xFF, nv3); - - return; - } - - nv3_mmio_arbitrate_write(addr, val); -} - -// AGP read function -uint8_t nv3_agp_read(int32_t func, int32_t addr) -{ - uint8_t ret = 0x00; - - switch (addr) - { - case NV3_AGP_CAPABILITIES_CAP_ID: - ret = NV3_AGP_CAPABILITIES_CAP_ID_AGP; // AGP capable device - break; - case NV3_AGP_CAPABILITIES_NEXT_PTR: // Always off - ret = 0x00; - case NV3_AGP_CAPABILITIES_AGP_VERSION: - ret = (0x1 << NV3_AGP_CAPABILITIES_AGP_VERSION_MAJOR) | NV3_AGP_CAPABILITIES_AGP_VERSION_MINOR; - break; - case NV3_AGP_STATUS_RATE: - // NV3T = AGP 2X, NV3 = AGP 1X - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - ret = NV3_AGP_STATUS_RATE_1X_SUPPORTED | NV3_AGP_STATUS_RATE_2X_SUPPORTED; - else - ret = NV3_AGP_STATUS_RATE_1X_SUPPORTED; - break; - case NV3_AGP_STATUS_BYTE1: - ret = 0x00; // SBA not supported - break; - case NV3_AGP_STATUS_MAX_REQUESTS: - ret = NV3_AGP_STATUS_MAX_REQUESTS_AMOUNT; - break; - // This is also used for SBA but SBA is always off so we can use a bool - case NV3_AGP_COMMAND_BYTE1: - ret = nv3->nvbase.agp_enabled; - break; - default: - ret = nv3->nvbase.pci_config.pci_regs[addr]; - break; - } - - return ret; -} - -// 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; - - // sanity check - if (!nv3) - return ret; - - // 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 = (NV_PCI_DEVICE_NV3 & 0xFF); - break; - - case NV3_PCI_CFG_DEVICE_ID + 1: - ret = (NV_PCI_DEVICE_NV3 >> 8); - break; - - // various capabilities enabled by default - // 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->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L]; - break; - - case PCI_REG_COMMAND_H: - ret = nv3->nvbase.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->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] | NV3_PCI_STATUS_L_66MHZ_CAPABLE); - else - ret = nv3->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L]; - - break; - - case PCI_REG_STATUS_H: - ret = (nv3->nvbase.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->nvbase.gpu_revision; // 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->nvbase.pci_config.vbios_enabled; - break; - - case NV3_AGP_CAPABILITIES_POINTER: - if (nv3->nvbase.bus_generation >= nv_bus_agp_1x) - ret = NV3_AGP_CAPABILITIES_START; - else - ret = 0x00; - break; - - case NV3_PCI_CFG_INT_LINE: - ret = nv3->nvbase.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->nvbase.pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)]; - break; - - case NV3_AGP_START ... NV3_AGP_END: - if (nv3->nvbase.bus_generation < nv_bus_agp_1x) - break; - - ret = nv3_agp_read(func, addr); - - break; - - - default: // by default just return pci_config.pci_regs - ret = nv3->nvbase.pci_config.pci_regs[addr]; - break; - - } - - nv_log("nv3_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr, ret); - return ret; -} - -void nv3_agp_write(int32_t func, int32_t addr, uint8_t val) -{ - nv3->nvbase.pci_config.pci_regs[addr] = val; - - switch (addr) - { - case NV3_AGP_COMMAND_BYTE1: - nv3->nvbase.agp_enabled = val; - break; - default: - break; - } -} - -void nv3_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) -{ - // sanity check - if (!nv3) - return; - - // some addresses are not writable so can't have any effect and can't be allowed to be modified using this code - // as an example, 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_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr, val); - - nv3->nvbase.pci_config.pci_regs[addr] = val; - - switch (addr) - { - // standard pci command stuff - case PCI_REG_COMMAND_L: - nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L] = val; - // actually update the mappings - nv3_update_mappings(); - break; - case PCI_REG_COMMAND_H: - nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_H] = val; - // actually update the mappings - nv3_update_mappings(); - break; - // pci status register - case PCI_REG_STATUS_L: - nv3->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] = val | (NV3_PCI_STATUS_L_66MHZ_CAPABLE); - break; - case PCI_REG_STATUS_H: - nv3->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H] = val | (NV3_PCI_STATUS_H_FAST_DEVSEL_TIMING << NV3_PCI_STATUS_H_DEVSEL_TIMING); - break; - 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->nvbase.pci_config.vbios_enabled = (val & 0x01); - - if (nv3->nvbase.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->nvbase.pci_config.pci_regs[NV3_PCI_CFG_VBIOS_BASE_H] << 24 | - nv3->nvbase.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->nvbase.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->nvbase.pci_config.pci_regs[NV3_PCI_CFG_SUBSYSTEM_ID + (addr & 0x03)] = val; - break; - - case NV3_AGP_START ... NV3_AGP_END: - if (nv3->nvbase.bus_generation < nv_bus_agp_1x) - break; - - nv3_agp_write(func, addr, val); - - break; - - default: - break; - } -} - - -// -// SVGA functions -// -void nv3_recalc_timings(svga_t* svga) -{ - // sanity check - if (!nv3) - return; - - nv3_t* nv3 = (nv3_t*)svga->priv; - uint32_t pixel_mode = svga->crtc[NV3_CRTC_REGISTER_PIXELMODE] & 0x03; - - svga->memaddr_latch += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16; - - /* Turn off override if we are in VGA mode */ - svga->override = !(pixel_mode == NV3_CRTC_REGISTER_PIXELMODE_VGA); - - /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. - - Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. - - This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. - In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. - - Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick - to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from - the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. - */ - - // Set the pixel mode - switch (pixel_mode) - { - case NV3_CRTC_REGISTER_PIXELMODE_8BPP: - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 1; // ????? - svga->bpp = 8; - svga->lowres = 0; - svga->map8 = svga->pallook; - break; - case NV3_CRTC_REGISTER_PIXELMODE_16BPP: - /* This is some sketchy shit that is an attempt at an educated guess - at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, - this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ - if ((svga->crtc[NV3_CRTC_REGISTER_VRETRACESTART] >> 1) & 0x01) - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 2; - else - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; - - /* sometimes it really renders in 15bpp, so you need to do this */ - if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) - { - svga->bpp = 16; - svga->lowres = 0; - } - else - { - svga->bpp = 15; - svga->lowres = 0; - - } - - break; - case NV3_CRTC_REGISTER_PIXELMODE_32BPP: - svga->rowoffset += (svga->crtc[NV3_CRTC_REGISTER_RPC0] & 0xE0) << 3; - - svga->bpp = 32; - svga->lowres = 0; - //svga->render = nv3_render_32bpp; - 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) -{ - // sanity check - if (!nv3) - return; - - nv3_recalc_timings(&nv3->nvbase.svga); -} - -// Force Redraw -// Reset etc. -void nv3_force_redraw(void* priv) -{ - // sanity check - if (!nv3) - return; - - nv3->nvbase.svga.fullchange = changeframecount; -} - -// Read from SVGA core memory -uint8_t nv3_svga_read(uint16_t addr, void* priv) -{ - - nv3_t* nv3 = (nv3_t*)priv; - - uint8_t ret = 0x00; - - // sanity check - if (!nv3) - return ret; - - // 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 NV3_CRTC_REGISTER_INDEX: - ret = nv3->nvbase.svga.crtcreg; - break; - case NV3_CRTC_REGISTER_WTF: - ret = 0x08; // Required to not freeze in certain situations on v3.xx drivers. Even though this register doesn't actually exist lol - break; - case NV3_CRTC_REGISTER_CURRENT: - // Support the extended NVIDIA CRTC register range - switch (nv3->nvbase.svga.crtcreg) - { - case NV3_CRTC_REGISTER_RL0: - ret = nv3->nvbase.svga.displine & 0xFF; - break; - /* Is rl1?*/ - case NV3_CRTC_REGISTER_RL1: - ret = (nv3->nvbase.svga.displine >> 8) & 7; - break; - case NV3_CRTC_REGISTER_I2C: - ret = i2c_gpio_get_sda(nv3->nvbase.i2c) << 3 - | i2c_gpio_get_scl(nv3->nvbase.i2c) << 2; - - break; - 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_write(uint16_t addr, uint8_t val, void* priv) -{ - // sanity check - if (!nv3) - return; - - // 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 = 0x00; - - // todo: - // Pixel formats (8bit vs 555 vs 565) - // VBE 3.0? - - switch (addr) - { - case NV3_CRTC_REGISTER_INDEX: - // real mode access to GPU MMIO space... - nv3->nvbase.svga.crtcreg = val; - break; - // support the extended crtc regs and debug this out - case NV3_CRTC_REGISTER_CURRENT: - - // 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... - old_value = nv3->nvbase.svga.crtc[crtcreg]; - - 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; - /* Handle some large screen stuff */ - case NV3_CRTC_REGISTER_PIXELMODE: - if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VDT10)) - nv3->nvbase.svga.vtotal += 0x400; - if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VRS10)) - nv3->nvbase.svga.vblankstart += 0x400; - if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_VBS10)) - nv3->nvbase.svga.vsyncstart += 0x400; - if (val & 1 << (NV3_CRTC_REGISTER_FORMAT_HBE6)) - nv3->nvbase.svga.hdisp += 0x400; - - /* Make sure dispend and vblankstart are right if we are displaying above 1024 vert */ - if (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_PIXELMODE] & 1 << (NV3_CRTC_REGISTER_FORMAT_VDE10)) - nv3->nvbase.svga.dispend += 0x400; - - break; - case NV3_CRTC_REGISTER_HEB: - if (val & 0x01) - nv3->nvbase.svga.hdisp += 0x100; - break; - case NV3_CRTC_REGISTER_I2C_GPIO: - { - uint8_t scl = !!(val & 0x20); - uint8_t sda = !!(val & 0x10); - // Set an I2C GPIO register - i2c_gpio_set(nv3->nvbase.i2c, scl, sda); - break; - } - /* [6:0] contains cursorAddr [22:16] */ - case NV3_CRTC_REGISTER_CURSOR_ADDR0: - nv3->pramdac.cursor_address |= ((val & 0x7F) << 12); //bit7 technically ignored, but nv don't care, so neither do we - break; - /* [7:2] contains cursorAddr [16:11] */ - case NV3_CRTC_REGISTER_CURSOR_ADDR1: - nv3->pramdac.cursor_address |= ((val & 0xF8) << 4); // bit0 and 1 aren't part of the address - break; - } - - /* Recalculate the timings if we actually changed them - Additionally only do it if the value actually changed*/ - if (old_value != val) - { - // Thx to Fuel who basically wrote most of the SVGA compatibility code already (although I fixed some issues), because VGA is boring - // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. - // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" - if (nv3->nvbase.svga.crtcreg < 0xE - || nv3->nvbase.svga.crtcreg > 0x10) - { - nv3->nvbase.svga.fullchange = changeframecount; - nv3_recalc_timings(&nv3->nvbase.svga); - } - } - - break; - default: - svga_out(addr, val, &nv3->nvbase.svga); - break; - } - -} - -/* DFB, sets up a dumb framebuffer */ -uint8_t nv3_dfb_read8(uint32_t addr, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - return nv3->nvbase.svga.vram[addr]; -} - -uint16_t nv3_dfb_read16(uint32_t addr, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - return (nv3->nvbase.svga.vram[addr + 1] << 8) | nv3->nvbase.svga.vram[addr]; -} - -uint32_t nv3_dfb_read32(uint32_t addr, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - return (nv3->nvbase.svga.vram[addr + 3] << 24) | (nv3->nvbase.svga.vram[addr + 2] << 16) + - (nv3->nvbase.svga.vram[addr + 1] << 8) | nv3->nvbase.svga.vram[addr]; -} - -void nv3_dfb_write8(uint32_t addr, uint8_t val, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - nv3->nvbase.svga.vram[addr] = val; - nv3->nvbase.svga.changedvram[addr >> 12] = val; - nv3_render_current_bpp_dfb_8(addr); -} - -void nv3_dfb_write16(uint32_t addr, uint16_t val, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; - nv3->nvbase.svga.vram[addr] = (val) & 0xFF; - nv3->nvbase.svga.changedvram[addr >> 12] = val; - nv3_render_current_bpp_dfb_16(addr); - -} - -void nv3_dfb_write32(uint32_t addr, uint32_t val, void* priv) -{ - addr &= (nv3->nvbase.svga.vram_mask); - nv3->nvbase.svga.vram[addr + 3] = (val >> 24) & 0xFF; - nv3->nvbase.svga.vram[addr + 2] = (val >> 16) & 0xFF; - nv3->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; - nv3->nvbase.svga.vram[addr] = (val) & 0xFF; - nv3->nvbase.svga.changedvram[addr >> 12] = val; - - nv3_render_current_bpp_dfb_32(addr); - -} - -/* Cursor shit */ -void nv3_draw_cursor(svga_t* svga, int32_t drawline) -{ - // sanity check - if (!nv3) - return; - - // if cursor disabled is set, return - if ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_CURSOR_START] >> NV3_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01) - return; - - // NT GDI drivers: Load cursor using NV_IMAGE_FROM_MEMORY ("NV3LCD") - // 9x GDI drivers: Use H/W cursor in RAMIN - - // Do we need to emulate it? - - // THIS IS CORRECT. BUT HOW DO WE FIND IT? - uint32_t ramin_cursor_position = NV3_RAMIN_OFFSET_CURSOR; - - /* let's just assume buffer 0 here...that code needs to be totally rewritten*/ - nv3_coord_16_t start_position = nv3->pramdac.cursor_start; - - /* refuse to draw if thge cursor is offscreen */ - if (start_position.x >= nv3->nvbase.svga.hdisp - || start_position.y >= nv3->nvbase.svga.dispend) - { - return; - } - - nv_log("nv3_draw_cursor start=0x%04x,0x%04x", start_position.x, start_position.y); - - uint32_t final_position = nv3_render_get_vram_address_for_buffer(start_position, 0); - - uint16_t* vram_16 = (uint16_t*)nv3->nvbase.svga.vram; - uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; - - /* - We have to get a 32x32, "A"1R5G5B5-format cursor - out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1 - - These are expanded to RGB10 only if they are XORed. We don't do this (we don't really need to + there is no grobj specified here so special casing - would be needed) so we just xor it with the current pixel format - */ - for (int32_t y = 0; y < NV3_PRAMDAC_CURSOR_SIZE_Y; y++) - { - for (int32_t x = 0; x < NV3_PRAMDAC_CURSOR_SIZE_X; x++) - { - uint16_t current_pixel = nv3_ramin_read16(ramin_cursor_position, nv3); - - // 0000 = transparent, so skip drawing - if (current_pixel) - { - bool replace_bit = (current_pixel & 0x8000); - - // use buffer 0 BPIXEL - uint32_t bpixel_format = (nv3->pgraph.bpixel[0]) & 0x03; - - switch (bpixel_format) - { - case bpixel_fmt_8bit: - if (replace_bit) - nv3->nvbase.svga.vram[final_position] = current_pixel; - else //xor - { - // not sure what to do here. we'd have to search through the palette to find the closest possible colour. - uint8_t final = current_pixel ^ nv3->nvbase.svga.vram[final_position]; - nv3->nvbase.svga.vram[final_position] = final; - } - case bpixel_fmt_16bit: // easy case (our cursor is 15bpp format) - uint32_t index_16 = final_position >> 1; - - if (replace_bit) // just replace - vram_16[index_16] = current_pixel; - else // xor - { - current_pixel &= ~0x8000; // mask off the xor bit - uint16_t final = current_pixel ^ vram_16[index_16]; - vram_16[index_16] = final; - } - case bpixel_fmt_32bit: - uint32_t index_32 = final_position >> 2; - - if (replace_bit) // just replace - vram_32[index_32] = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here - else //xor - { - current_pixel &= ~0x8000; // mask off the xor bit - uint32_t current_pixel_32 = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here - - uint32_t final = current_pixel_32 ^ vram_32[index_32]; - vram_32[index_32] = final; - } - break; - } - } - - // increment vram position - ramin_cursor_position += 2; - - // go - switch (nv3->nvbase.svga.bpp) - { - case 8: - final_position++; - case 15 ... 16: - final_position += 2; - break; - case 32: - final_position += 4; - break; - } - - start_position.x++; - } - - - start_position.y++; - start_position.x = nv3->pramdac.cursor_start.x; - - // reset at the end of each line so we "jump" to the start x - final_position = nv3_render_get_vram_address_for_buffer(start_position, 0); - } -} - -// MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. -// Note this area is 64kb and the vbios is only 32kb. See below.. - -uint8_t nv3_prom_read(uint32_t address) -{ - // prom area is 64k, so... - // first see if we even have a rom of 64kb in size - uint32_t max_rom_size = NV3_PROM_END - NV3_PROM_START; - uint32_t real_rom_size = max_rom_size; - - // set it - if (nv3->nvbase.vbios.sz < max_rom_size) - real_rom_size = nv3->nvbase.vbios.sz; - - //get our real address - uint8_t rom_address = address & max_rom_size; - - // Does this mirror on real hardware? - if (rom_address >= real_rom_size) - { - nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); - return 0xFF; - } - else - { - uint8_t val = nv3->nvbase.vbios.rom[rom_address]; - nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); - return val; - } -} - -void nv3_prom_write(uint32_t address, uint32_t value) -{ - uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", real_addr, value); -} - -// Initialise the MMIO mappings -void nv3_init_mappings_mmio(void) -{ - nv_log("Initialising MMIO mapping\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(void) -{ - nv_log("Initialising SVGA core memory mapping\n"); - // setup the svga mappings - mem_mapping_add(&nv3->nvbase.framebuffer_mapping, 0, 0, - nv3_dfb_read8, - nv3_dfb_read16, - nv3_dfb_read32, - nv3_dfb_write8, - nv3_dfb_write16, - nv3_dfb_write32, - nv3->nvbase.svga.vram, 0, &nv3->nvbase.svga); - - // the SVGA/LFB mapping is also mirrored - mem_mapping_add(&nv3->nvbase.framebuffer_mapping_mirror, 0, 0, - nv3_dfb_read8, - nv3_dfb_read16, - nv3_dfb_read32, - nv3_dfb_write8, - nv3_dfb_write16, - nv3_dfb_write32, - nv3->nvbase.svga.vram, 0, &nv3->nvbase.svga); - - io_sethandler(0x03c0, 0x0020, - nv3_svga_read, NULL, NULL, - nv3_svga_write, NULL, NULL, - nv3); -} - -void nv3_init_mappings(void) -{ - nv3_init_mappings_mmio(); - nv3_init_mappings_svga(); -} - -// Updates the mappings after initialisation. -void nv3_update_mappings(void) -{ - // sanity check - if (!nv3) - return; - - // setting this to 0 doesn't seem to disable it, based on the datasheet - - nv_log("\nMemory Mapping Config Change:\n"); - - (nv3->nvbase.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_read, NULL, NULL, - nv3_svga_write, NULL, NULL, - nv3); - - if (nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) - io_sethandler(0x03c0, 0x0020, - nv3_svga_read, NULL, NULL, - nv3_svga_write, NULL, NULL, - nv3); - - if (!(nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) - { - nv_log("The memory was turned off, not much is going to happen.\n"); - return; - } - - // 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); - - // Setup BAR0 (MMIO) - - nv_log("BAR0 (MMIO Base) = 0x%08x\n", nv3->nvbase.bar0_mmio_base); - - - if (nv3->nvbase.bar0_mmio_base) - mem_mapping_set_addr(&nv3->nvbase.mmio_mapping, nv3->nvbase.bar0_mmio_base, NV3_MMIO_SIZE); - - // if this breaks anything, remove it - nv_log("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 - - /* TODO: 2MB */ - - // 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 - - // 8MB VRAM memory map: - // LFB_BASE->LFB_BASE+VRAM_SIZE=LFB - // What is in 800000-c00000? - // LFB_BASE+0xC00000 = RAMIN - - if (nv3->nvbase.bar1_lfb_base) - { - if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_4MB) - { - mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping, nv3->nvbase.bar1_lfb_base, NV3_VRAM_SIZE_4MB); - mem_mapping_set_addr(&nv3->nvbase.ramin_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_MIRROR_START, NV3_LFB_MAPPING_SIZE); - mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_MIRROR_START, NV3_VRAM_SIZE_4MB); - mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); - } - else if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_8MB) - { - // we don't need this one in the case of 8mb, because regular mapping is 8mb - mem_mapping_disable(&nv3->nvbase.ramin_mapping_mirror); - mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping, nv3->nvbase.bar1_lfb_base, NV3_VRAM_SIZE_8MB); - mem_mapping_set_addr(&nv3->nvbase.framebuffer_mapping_mirror, nv3->nvbase.bar1_lfb_base + NV3_LFB_MIRROR_START, NV3_LFB_MAPPING_SIZE); - mem_mapping_set_addr(&nv3->nvbase.ramin_mapping, nv3->nvbase.bar1_lfb_base + NV3_LFB_RAMIN_START, NV3_LFB_MAPPING_SIZE); - } - else - { - fatal("NV3 2MB not implemented yet"); - } - } - - // Did we change the banked SVGA mode? - switch (nv3->nvbase.svga.gdcreg[0x06] & 0x0c) - { - case NV3_CRTC_BANKED_128K_A0000: - nv_log("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("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("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("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) -{ - // set the vram amount and gpu revision - - /* We don't bother looking these up if they are nonzero. On Riva 128 ZX, they are already set by the init function (always 8 MB VRAM + Revision C0) */ - if (!nv3->nvbase.vram_amount) - nv3->nvbase.vram_amount = device_get_config_int("vram_size"); - - if (!nv3->nvbase.gpu_revision) - nv3->nvbase.gpu_revision = device_get_config_int("chip_revision"); - - /* Set log device name based on card model */ - const char* log_device_name = (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) ? "NV3T" : "NV3"; - - if (device_get_config_int("nv_debug_fulllog")) - nv3->nvbase.log = log_open(log_device_name); - else - nv3->nvbase.log = log_open_cyclic(log_device_name); - -#ifdef ENABLE_NV_LOG - // Allows nv_log to be used for multiple nvidia devices - nv_log_set_device(nv3->nvbase.log); -#endif - nv_log("Initialising core\n"); - - // this will only be logged if ENABLE_NV_LOG_ULTRA is defined - nv_log_verbose_only("ULTRA LOGGING enabled"); - - // Figure out which vbios the user selected - // This depends on the bus we are using and if the gpu is rev a/b or rev c - const char* vbios_id = device_get_config_bios("vbios"); - const char* vbios_file = ""; - - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - { - if (nv3->nvbase.bus_generation == nv_bus_pci) - vbios_file = device_get_bios_file(&nv3t_device_pci, vbios_id, 0); - else - vbios_file = device_get_bios_file(&nv3t_device_agp, vbios_id, 0); - } - else - { - if (nv3->nvbase.bus_generation == nv_bus_pci) - vbios_file = device_get_bios_file(&nv3_device_pci, vbios_id, 0); - else - vbios_file = device_get_bios_file(&nv3_device_agp, vbios_id, 0); - } - - - int32_t err = rom_init(&nv3->nvbase.vbios, vbios_file, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); - - if (err) - { - nv_log("NV3 FATAL: failed to load VBIOS err=%d\n", err); - fatal("Nvidia NV3 init failed: Somehow selected a nonexistent VBIOS? err=%d\n", err); - return NULL; - } - else - nv_log("Successfully loaded VBIOS %s located at %s\n", vbios_id, vbios_file); - - // set up the bus and start setting up SVGA core - if (nv3->nvbase.bus_generation == nv_bus_pci) - { - nv_log("Using PCI bus\n"); - - pci_add_card(PCI_ADD_NORMAL, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); - - /* Initialise the right revision of the card */ - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - { - svga_init(&nv3t_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - - video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_pci); - } - else - { - svga_init(&nv3_device_pci, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - - video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_pci); - } - - } - else if (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) - { - nv_log("Using AGP 1X/2X bus\n"); - - pci_add_card(PCI_ADD_AGP, nv3_pci_read, nv3_pci_write, NULL, &nv3->nvbase.pci_slot); - - /* Initialise the right revision of the card */ - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - { - svga_init(&nv3t_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - - video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3t_agp); - } - else - { - svga_init(&nv3_device_agp, &nv3->nvbase.svga, nv3, nv3->nvbase.vram_amount, - nv3_recalc_timings, nv3_svga_read, nv3_svga_write, nv3_draw_cursor, NULL); - - video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv3_agp); - } - } - - // set vram - nv_log("VRAM=%d bytes\n", nv3->nvbase.svga.vram_max); - - // init memory mappings - nv3_init_mappings(); - - // make us actually exist - nv3->nvbase.pci_config.int_line = 0xFF; // per datasheet - nv3->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEM; - - // svga is done, so now initialise the real gpu - - nv_log("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 engine - - nv_log("Initialising I2C..."); - nv3->nvbase.i2c = i2c_gpio_init("nv3_i2c"); - nv3->nvbase.ddc = ddc_init(i2c_gpio_get_bus(nv3->nvbase.i2c)); - - return nv3; -} - -// RIVA 128 PCI initialisation function: This function simply allocates the device struct, 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); - return nv3; -} - -// RIVA 128 AGP initialisation function: This function simply allocates the device struct, 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); - return nv3; -} - -// RIVA 128 ZX PCI initialisation function: This function simply allocates the device struct, and sets the bus to PCI before initialising. -// It also sets the GPU revision to C0 because NV3T config doesn't let you configure the rev (there were multiple steppings, but it's basically irrelevant), -// and sets RAM to 8 MB (the only supported config on ZX cards) -void* nv3t_init_pci(const device_t* info) -{ - nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); - nv3->nvbase.bus_generation = nv_bus_pci; - nv3->nvbase.gpu_revision = NV3_PCI_CFG_REVISION_C00; - nv3->nvbase.vram_amount = NV3_VRAM_SIZE_8MB; - nv3_init(info); - return nv3; -} - -// RIVA 128 ZX AGP initialisation function: This function simply allocates the device struct, and sets the bus to AGP before initialising. -// It also sets the GPU revision to C0 because NV3T config doesn't let you configure the rev (there were multiple steppings, but it's basically irrelevant) -// and sets RAM to 8 MB (the only supported config on ZX cards) -void* nv3t_init_agp(const device_t* info) -{ - nv3 = (nv3_t*)calloc(1, sizeof(nv3_t)); - nv3->nvbase.bus_generation = nv_bus_agp_2x; // Riva 128 ZX is AGP2X - nv3->nvbase.gpu_revision = NV3_PCI_CFG_REVISION_C00; - nv3->nvbase.vram_amount = NV3_VRAM_SIZE_8MB; - nv3_init(info); - return nv3; -} - -void nv3_close(void* priv) -{ - // Shut down logging - log_close(nv3->nvbase.log); -#ifdef ENABLE_NV_LOG - nv_log_set_device(NULL); -#endif - - // Shut down I2C and the DDC - ddc_close(nv3->nvbase.ddc); - i2c_gpio_close(nv3->nvbase.i2c); - - // Destroy the Rivatimers. (It doesn't matter if they are running.) - rivatimer_destroy(nv3->nvbase.pixel_clock_timer); - rivatimer_destroy(nv3->nvbase.memory_clock_timer); - - // Shut down SVGA - svga_close(&nv3->nvbase.svga); - free(nv3); - nv3 = NULL; -} - -// See if the bios rom is available. -int32_t nv3_available(void) -{ - return rom_present(NV3_VBIOS_ASUS_V3000_V151) - || rom_present(NV3_VBIOS_DIAMOND_V330_V162) - || rom_present(NV3_VBIOS_ERAZOR_V14700) - || rom_present(NV3_VBIOS_ERAZOR_V15403) - || rom_present(NV3_VBIOS_ERAZOR_V15500) - || rom_present(NV3_VBIOS_STB_V128_V182) - || rom_present(NV3_VBIOS_STB_V128_V182) - || rom_present(NV3T_VBIOS_ASUS_V170) - || rom_present(NV3T_VBIOS_DIAMOND_V330_V182B) - || rom_present(NV3T_VBIOS_REFERENCE_CEK_V171) - || rom_present(NV3T_VBIOS_REFERENCE_CEK_V172); -} - -// NV3 (RIVA 128) -// PCI -// 2MB or 4MB VRAM -const device_t nv3_device_pci = -{ - .name = "nVIDIA RIVA 128 (NV3) PCI", - .internal_name = "nv3_pci", - .flags = DEVICE_PCI, - .local = 0, - .init = nv3_init_pci, - .close = nv3_close, - .speed_changed = nv3_speed_changed, - .force_redraw = nv3_force_redraw, - .available = nv3_available, - .config = nv3_config, -}; - -// 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, - .available = nv3_available, - .config = nv3_config, -}; - -// NV3T (RIVA 128 ZX) -// PCI -// 8MB VRAM -const device_t nv3t_device_pci = -{ - .name = "nVIDIA RIVA 128 ZX (NV3T) PCI", - .internal_name = "nv3t_pci", - .flags = DEVICE_PCI, - .local = 0, - .init = nv3t_init_pci, - .close = nv3_close, - .speed_changed = nv3_speed_changed, - .force_redraw = nv3_force_redraw, - .available = nv3_available, - .config = nv3t_config, -}; - -// NV3T (RIVA 128) -// AGP -// 2MB or 4MB VRAM -const device_t nv3t_device_agp = -{ - .name = "nVIDIA RIVA 128 ZX (NV3T) AGP", - .internal_name = "nv3t_agp", - .flags = DEVICE_AGP, - .local = 0, - .init = nv3t_init_agp, - .close = nv3_close, - .speed_changed = nv3_speed_changed, - .force_redraw = nv3_force_redraw, - .available = nv3_available, - .config = nv3t_config, -}; \ 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 deleted file mode 100644 index 3399ca9fe..000000000 --- a/src/video/nv/nv3/nv3_core_arbiter.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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-2025 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) -{ - // sanity check - if (!nv3) - return 0x00; - - uint32_t ret = 0x00; - - // Ensure the addresses are dword aligned. - // I don't know why this is needed because writepriv32 is always to dword align, but it crashes if you don't do this. - if (!(address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) - 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_PFIFO_START && address <= NV3_PFIFO_END) - ret = nv3_pfifo_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) - || (address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) //clut - ret = nv3_pramdac_read(address); - else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) - ret = nv3_dfb_read32(address & nv3->nvbase.svga.vram_mask, &nv3->nvbase.svga); - else if (address >= NV3_USER_START && address <= NV3_USER_END) - ret = nv3_user_read(address); - else - { - //nvplay stuff - //#ifdef ENABLE_NV_LOG_ULTRA - //warning("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); - //#else - nv_log("MMIO read arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning unmapped pattern]\n", address); - //#endif - - // The real hardware returns a garbage pattern - return 0x00; - } - - return ret; -} - -void nv3_mmio_arbitrate_write(uint32_t address, uint32_t value) -{ - // sanity check - if (!nv3) - return; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - address &= 0xFFFFFF; - - - // Ensure the addresses are dword aligned. - // I don't know why this is needed because writepriv32 is always dword aligned in Nvidia's drivers, but it crashes if you don't do this. - // Exclude the 4bpp/8bpp CLUT for this purpose - if (!(address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) - 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) - || (address >= NV3_USER_DAC_PALETTE_START && address <= NV3_USER_DAC_PALETTE_END)) //clut - nv3_pramdac_write(address, value); - else if (address >= NV3_VRAM_START && address <= NV3_VRAM_END) - nv3_dfb_write32(address, value, &nv3->nvbase.svga); - else if (address >= NV3_USER_START && address <= NV3_USER_END) - nv3_user_write(address, value); - //RAMIN is its own thing - else - { - //nvplay stuff - //#ifdef ENABLE_NV_LOG_ULTRA - //warning("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); - //#else - nv_log("MMIO write arbitration failed, INVALID address NOT mapped to any GPU subsystem 0x%08x [returning 0x00]\n", address); - //#endif - - return; - } -} - - -// // -// ******* 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_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) {}; diff --git a/src/video/nv/nv3/nv3_core_config.c b/src/video/nv/nv3/nv3_core_config.c deleted file mode 100644 index b039ff82b..000000000 --- a/src/video/nv/nv3/nv3_core_config.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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. - * - * Provides NV3 configuration - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -const device_config_t nv3_config[] = -{ - // VBIOS type configuration - { - .name = "vbios", - .description = "Model", - .type = CONFIG_BIOS, - .default_string = "NV3_VBIOS_ERAZOR_V15403", - .default_int = 0, - .bios = - { - - { - .name = "ELSA VICTORY Erazor - Version 1.47.00", .files_no = 1, - .internal_name = "NV3_VBIOS_ERAZOR_V14700", - .files = {NV3_VBIOS_ERAZOR_V14700, ""} - }, - { - .name = "ELSA VICTORY Erazor - Version 1.54.03", .files_no = 1, - .internal_name = "NV3_VBIOS_ERAZOR_V15403", - .files = {NV3_VBIOS_ERAZOR_V15403, ""} - }, - { - .name = "ELSA VICTORY Erazor - Version 1.55.00", .files_no = 1, - .internal_name = "NV3_VBIOS_ERAZOR_V15500", - .files = {NV3_VBIOS_ERAZOR_V15500, ""} - }, - { - .name = "Diamond Viper V330 - Version 1.62-CO", .files_no = 1, - .internal_name = "NV3_VBIOS_DIAMOND_V330_V162", - .files = {NV3_VBIOS_DIAMOND_V330_V162, ""}, - }, - { - .name = "ASUS AGP/3DP-V3000 - Version 1.51B", .files_no = 1, - .internal_name = "NV3_VBIOS_ASUS_V3000_V151", - .files = {NV3_VBIOS_ASUS_V3000_V151, ""}, - }, - { - .name = "STB Velocity 128 - Version 1.60 [BUGGY]", .files_no = 1, - .internal_name = "NV3_VBIOS_STB_V128_V160", - .files = {NV3_VBIOS_STB_V128_V160, ""}, - }, - { - .name = "STB Velocity 128 - Version 1.82", .files_no = 1, - .internal_name = "NV3_VBIOS_STB_V128_V182", - .files = {NV3_VBIOS_STB_V128_V182, ""}, - }, - } - }, - // Memory configuration - { - .name = "vram_size", - .description = "VRAM Size", - .type = CONFIG_SELECTION, - .default_int = NV3_VRAM_SIZE_4MB, - .selection = - { - // I thought this was never released, but it seems that at least one was released: - // The card was called the "NEC G7AGK" - { - .description = "2 MB", - .value = NV3_VRAM_SIZE_2MB, - }, - - { - .description = "4 MB", - .value = NV3_VRAM_SIZE_4MB, - }, - } - - }, - { - .name = "chip_revision", - .description = "Chip Revision", - .type = CONFIG_SELECTION, - .default_int = NV3_PCI_CFG_REVISION_B00, - .selection = - { - { - .description = "RIVA 128 Prototype (Revision A; January 1997)", - .value = NV3_PCI_CFG_REVISION_A00, - }, - { - .description = "RIVA 128 (Revision B)", - .value = NV3_PCI_CFG_REVISION_B00, - }, - } - }, - // Multithreading configuration - { - - .name = "pgraph_threads", -#ifndef RELEASE_BUILD - .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", -#else - .description = "Render threads", -#endif - .type = CONFIG_SELECTION, - .default_int = 1, // todo: change later - .selection = - { - { - .description = "1 thread (Only use if issues appear with more threads)", - .value = 1, - }, - { - .description = "2 threads", - .value = 2, - }, - { - .description = "4 threads", - .value = 4, - }, - { - .description = "8 threads", - .value = 8, - }, - }, - }, -#ifndef RELEASE_BUILD - { - .name = "nv_debug_fulllog", - .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", - .type = CONFIG_BINARY, - .default_int = 0, - }, -#endif - { - .type = CONFIG_END - } -}; - -const device_config_t nv3t_config[] = -{ - // VBIOS type configuration - { - .name = "vbios", - .description = "Model", - .type = CONFIG_BIOS, - .default_string = "NV3T_VBIOS_DIAMOND_V330_V182B", - .default_int = 0, - .bios = - { - { - - .name = "Diamond Multimedia Viper V330 8M BIOS - Version 1.82B", .files_no = 1, - .internal_name = "NV3T_VBIOS_DIAMOND_V330_V182B", - .files = {NV3T_VBIOS_DIAMOND_V330_V182B, ""}, - }, - { - .name = "ASUS AGP-V3000 ZXTV BIOS - V1.70D.03", .files_no = 1, - .internal_name = "NV3T_VBIOS_ASUS_V170", - .files = {NV3T_VBIOS_ASUS_V170, ""}, - }, - { - .name = "NVidia Reference BIOS - V1.71B-N", .files_no = 1, - - .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V171", - .files = {NV3T_VBIOS_REFERENCE_CEK_V171, ""}, - }, - - { - .name = "NVidia Reference BIOS - V1.72B", .files_no = 1, - .internal_name = "NV3T_VBIOS_REFERENCE_CEK_V172", - .files = {NV3T_VBIOS_REFERENCE_CEK_V172, ""}, - }, - } - }, - // Multithreading configuration - { - - .name = "pgraph_threads", -#ifndef RELEASE_BUILD - .description = "PFIFO/PGRAPH - Number of threads to split large object method execution into", -#else - .description = "Render threads", -#endif - .type = CONFIG_SELECTION, - .default_int = 1, // todo: change later - .selection = - { - { - .description = "1 thread (Only use if issues appear with more threads)", - .value = 1, - }, - { - .description = "2 threads", - .value = 2, - }, - { - .description = "4 threads", - .value = 4, - }, - { - .description = "8 threads", - .value = 8, - }, - }, - }, -#ifndef RELEASE_BUILD - { - .name = "nv_debug_fulllog", - .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", - .type = CONFIG_BINARY, - .default_int = 0, - }, -#endif - { - .type = CONFIG_END - } -}; \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_blit.c b/src/video/nv/nv3/render/nv3_render_blit.c deleted file mode 100644 index 92b657cd0..000000000 --- a/src/video/nv/nv3/render/nv3_render_blit.c +++ /dev/null @@ -1,229 +0,0 @@ -/* -* 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 Core rendering code (Software version) -* -* -* -* Authors: Connor Hyde, I need a better email address ;^) -* -* Copyright 2024-2025 Connor Hyde -*/ - -#include -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/plat.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -/* Check the line bounds */ -void nv3_class_011_check_line_bounds(void) -{ - uint32_t relative_x = nv3->pgraph.image_current_position.x - nv3->pgraph.image.point.x; - - /* In the case of class 0x11 there is no requirement to check for relative_y because we have exceeded the size of the image */ - if (relative_x >= nv3->pgraph.image.size_in.x) - { - nv3->pgraph.image_current_position.y++; - nv3->pgraph.image_current_position.x = nv3->pgraph.image.point.x; - } -} - -/* Renders an image from cpu */ -void nv3_render_blit_image(uint32_t color, nv3_grobj_t grobj) -{ - /* todo: a lot of stuff */ - - uint32_t pixel0 = 0, pixel1 = 0, pixel2 = 0, pixel3 = 0; - - /* Some extra data is sent as padding, we need to clip it off using size_out */ - - uint16_t clip_x = nv3->pgraph.image.point.x + nv3->pgraph.image.size.x; - /* we need to unpack them - IF THIS IS USED SOMEWHERE ELSE, DO SOMETHING ELSE WITH IT */ - /* the reverse order is due to the endianness */ - switch (nv3->nvbase.svga.bpp) - { - // 4 pixels packed into one color in 8bpp - case 8: - - //pixel3 - pixel3 = color & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel3, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel2 = (color >> 8) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel2, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel1 = (color >> 16) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel0 = (color >> 24) & 0xFF; - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - // 2 pixels packed into one color in 15/16bpp - case 15: - case 16: - pixel1 = (color) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < (clip_x)) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel1, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - pixel0 = (color >> 16) & 0xFFFF; - if (nv3->pgraph.image_current_position.x < (clip_x)) nv3_render_write_pixel(nv3->pgraph.image_current_position, pixel0, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - // just one pixel in 32bpp - case 32: - if (nv3->pgraph.image_current_position.x < clip_x) nv3_render_write_pixel(nv3->pgraph.image_current_position, color, grobj); - nv3->pgraph.image_current_position.x++; - nv3_class_011_check_line_bounds(); - - break; - } -} - - -#define NV3_MAX_HORIZONTAL_SIZE 1920 -#define NV3_MAX_VERTICAL_SIZE 1200 - -/* 1920 for margin. Holds a buffer of the old screen we want to hold so we don't overwrite things we already overwtote -We only need to clear it once per blit, because the blits are always the same size, and then only for the size of our new blit - -Extremely not crazy about this...Surely a better way to do it without buffering the ENTIRE SCREEN. I only update the parts that are needed, but still... - -This is LUDICROUSLY INEFFICIENT (2*O(n^2)) and COMPLETELY TERRIBLE code, but it's currently 2:48am so I can't think of a better approach... -*/ -uint32_t nv3_s2sb_line_buffer[NV3_MAX_HORIZONTAL_SIZE*NV3_MAX_VERTICAL_SIZE] = {0}; - -void nv3_render_blit_screen2screen_for_buffer(nv3_grobj_t grobj, uint32_t dst_buffer) -{ - if (nv3->pgraph.blit.size.x < NV3_MAX_HORIZONTAL_SIZE - && nv3->pgraph.blit.size.y < NV3_MAX_VERTICAL_SIZE) - memset(&nv3_s2sb_line_buffer, 0x00, (sizeof(uint32_t) * nv3->pgraph.blit.size.y) * (sizeof(uint32_t) * nv3->pgraph.blit.size.x)); - - /* First calculate our source and destination buffer */ - uint32_t src_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; - - nv3_coord_16_t in_position = nv3->pgraph.blit.point_in; - nv3_coord_16_t out_position = nv3->pgraph.blit.point_out; - - /* Coordinates for copying an entire line at a time */ - uint32_t buf_position = 0, vram_position = 0, size_x = nv3->pgraph.blit.size.x; - - /* - Read the old pixel into the line buffer - Assumption: All data is sent in an unpacked format. In the case of an NVIDIA GPU this means that all data is sent 32 bits at a time regardless of if - the actual source data is 32 bits in size or not. For pixel data, the upper bits are left as 0 in 8bpp/16bpp mode. For 86box purposes, the data is written - 8/16 bits at a time. - - TODO: CHECK FOR PACKED FORMAT!!!!! - */ - - if (nv3->nvbase.svga.bpp == 15 - || nv3->nvbase.svga.bpp == 16) - size_x <<= 1; - else if (nv3->nvbase.svga.bpp == 32) - size_x <<= 2; - - for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) - { - buf_position = (nv3->pgraph.blit.size.x * y); - /* shouldn't matter in non-wtf mode */ - vram_position = nv3_render_get_vram_address_for_buffer(in_position, src_buffer); - - memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x); - in_position.y++; - /* 32bit buffer */ - } - - /* simply write it all back to vram */ - for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++) - { - buf_position = (nv3->pgraph.blit.size.x * y); - vram_position = nv3_render_get_vram_address_for_buffer(out_position, dst_buffer); - - memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x); - out_position.y++; - } - - /* - //32bit only as a test - uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; - - if (nv3->pgraph.boffset[src_buffer] != nv3->pgraph.boffset[dst_buffer]) - { - // stretch out the position to the new one - - nv3_coord_16_t current_pos_in; - nv3_coord_16_t current_pos_out; - - current_pos_in.x = nv3->pgraph.blit.point_in.x; - current_pos_in.y = nv3->pgraph.blit.point_in.y; - current_pos_out.x = nv3->pgraph.blit.point_out.x; - current_pos_out.y = nv3->pgraph.blit.point_out.y; - - for (uint32_t y = 0; y < nv3->pgraph.blit.size.y; y++) - { - current_pos_in.y = nv3->pgraph.blit.point_in.y + y; - current_pos_out.y = nv3->pgraph.blit.point_out.y + y; - - for (uint32_t x = 0; x < nv3->pgraph.blit.size.x; x++) - { - current_pos_in.x = nv3->pgraph.blit.point_in.x + x; - current_pos_out.x = nv3->pgraph.blit.point_out.x + x; - - uint32_t index = nv3_render_get_vram_address_for_buffer(current_pos_in, dst_buffer) >> 2; - uint32_t index_dst = nv3_render_get_vram_address_for_buffer(current_pos_out, src_buffer) >> 2; - - vram_32[index_dst] = vram_32[index]; - - //nv3_render_write_pixel(current_pos, vram_32[index], grobj); - } - - current_pos_in.x = nv3->pgraph.blit.point_in.x; - current_pos_out.x = nv3->pgraph.blit.point_out.x; - - } - } - */ -} - -void nv3_render_blit_screen2screen(nv3_grobj_t grobj) -{ - uint32_t dst_buffer = (nv3_pgraph_destination_buffer)grobj.grobj_0; // 5 = just use the source buffer - - if (dst_buffer & pgraph_dest_buffer0) - nv3_render_blit_screen2screen_for_buffer(grobj, 0); - if (dst_buffer & pgraph_dest_buffer1) - nv3_render_blit_screen2screen_for_buffer(grobj, 1); - if (dst_buffer & pgraph_dest_buffer2) - nv3_render_blit_screen2screen_for_buffer(grobj, 2); - if (dst_buffer & pgraph_dest_buffer3) - nv3_render_blit_screen2screen_for_buffer(grobj, 3); - - -} \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_core.c b/src/video/nv/nv3/render/nv3_render_core.c deleted file mode 100644 index c40fa84a9..000000000 --- a/src/video/nv/nv3/render/nv3_render_core.c +++ /dev/null @@ -1,869 +0,0 @@ -/* -* 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 Core rendering code (Software version) -* -* -* -* Authors: Connor Hyde, I need a better email address ;^) -* -* Copyright 2024-2025 Connor Hyde -*/ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/plat.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> -#include <86box/utils/video_stdlib.h> - -/* Functions only used in this translation unit */ -void nv3_render_8bpp(uint32_t vram_start, nv3_coord_16_t screen_size); -void nv3_render_15bpp(uint32_t vram_start, nv3_coord_16_t screen_size); -void nv3_render_16bpp(uint32_t vram_start, nv3_coord_16_t screen_size); -void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size); - -/* Expand a colour. - NOTE: THE GPU INTERNALLY OPERATES ON RGB10!!!!!!!!!!! -*/ -nv3_color_expanded_t nv3_render_expand_color(uint32_t color, nv3_grobj_t grobj) -{ - // grobj0 = seems to share the format of PGRAPH_CONTEXT_SWITCH register. - - uint8_t format = (grobj.grobj_0 & 0x07); - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; - - nv3_color_expanded_t color_final; - // set the pixel format - color_final.pixel_format = format; - - nv_log_verbose_only("Expanding Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); - - - // default to fully opaque in case alpha is disabled - color_final.a = 0xFF; - - switch (format) - { - // ALL OF THESE TYPES ARE 32 BITS IN SIZE - - // 555 - case nv3_pgraph_pixel_format_r5g5b5: - // "stretch out" the colour - - color_final.a = (color >> 15) & 0x01; // will be ignored if alpha_enabled isn't used - color_final.r = ((color >> 10) & 0x1F) << 5; - color_final.g = ((color >> 5) & 0x1F) << 5; - color_final.b = (color & 0x1F) << 5; - - break; - // 888 (standard colour + 8-bit alpha) - case nv3_pgraph_pixel_format_r8g8b8: - if (alpha_enabled) - color_final.a = ((color >> 24) & 0xFF) * 4; - - color_final.r = ((color >> 16) & 0xFF) * 4; - color_final.g = ((color >> 8) & 0xFF) * 4; - color_final.b = (color & 0xFF) * 4; - - break; - case nv3_pgraph_pixel_format_r10g10b10: - color_final.a = (color >> 31) & 0x01; - color_final.r = (color >> 30) & 0x3FF; - color_final.g = (color >> 20) & 0x1FF; - color_final.b = (color >> 10); - - break; - case nv3_pgraph_pixel_format_y8: - /* Indexed mode */ - color_final.a = (color >> 8) & 0xFF; - - // yuv - color_final.r = color_final.g = color_final.b = (color & 0xFF) * 4; // convert to rgb10 - break; - case nv3_pgraph_pixel_format_y16: - color_final.a = (color >> 16) & 0xFFFF; - - // yuv - color_final.r = color_final.g = color_final.b = (color & 0xFFFF) * 4; // convert to rgb10 - break; - case nv3_pgraph_pixel_format_y420: - warning("nv3_render_expand_color: YUV420 not implemented\n"); - break; - default: - warning("nv3_render_expand_color unknown format %d", format); - break; - - } - - // i8 is a union under i16 - color_final.i16 = (color & 0xFFFF); - - return color_final; -} - -/* Used for chroma test */ -uint32_t nv3_render_downconvert_color(nv3_grobj_t grobj, nv3_color_expanded_t color) -{ - uint8_t format = (grobj.grobj_0 & 0x07); - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; - - nv_log_verbose_only("Downconverting Colour 0x%08x using pgraph_pixel_format 0x%x alpha enabled=%d\n", color, format, alpha_enabled); - - uint32_t packed_color = 0x00; - - switch (format) - { - case nv3_pgraph_pixel_format_r5g5b5: - packed_color = (color.r >> 5) << 10 | - (color.g >> 5) << 5 | - (color.b >> 5); - - break; - case nv3_pgraph_pixel_format_r8g8b8: - packed_color = (color.a) << 24 | // is this a thing? - (color.r >> 2) << 16 | - (color.g >> 2) << 8 | - color.b; - break; - case nv3_pgraph_pixel_format_r10g10b10: - /* sometimes alpha isn't used but we should incorporate it anyway */ - if (color.a > 0x00) packed_color |= (1 << 31); - - packed_color |= (color.r << 30); - packed_color |= (color.g << 20); - packed_color |= (color.b << 10); - break; - case nv3_pgraph_pixel_format_y8: /* i think this is just indexed mode. since r=g=b we can just take the indexed from the r */ - packed_color = nv3_render_get_palette_index((color.r >> 2) & 0xFF); - break; - case nv3_pgraph_pixel_format_y16: - warning("nv3_render_downconvert_color: Y16 not implemented"); - break; - case nv3_pgraph_pixel_format_y420: - warning("nv3_render_downconvert_color: YUV420 not implemented\n"); - break; - default: - warning("nv3_render_downconvert_color unknown format %d", format); - break; - - } - - return packed_color; -} - -/* Runs the chroma key/color key test */ -bool nv3_render_chroma_test(uint32_t color, nv3_grobj_t grobj) -{ - bool chroma_enabled = ((grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_CHROMA_KEY) & 0x01); - - if (!chroma_enabled) - return true; - - bool alpha = ((nv3->pgraph.chroma_key >> 31) & 0x01); - - if (!alpha) - return true; - - /* this is dumb but i'm lazy, if it kills perf, fix it later - we need to do some format shuffling */ - nv3_grobj_t grobj_fake = {0}; - grobj_fake.grobj_0 = 0x02; /* we don't care about any other bits */ - - nv3_color_expanded_t chroma_expanded = nv3_render_expand_color(nv3->pgraph.chroma_key, grobj_fake); - - uint32_t chroma_downconverted = nv3_render_downconvert_color(grobj, chroma_expanded); - - return !(chroma_downconverted == color); -} - -/* Convert expanded colour format to chroma key format */ -uint32_t nv3_render_to_chroma(nv3_color_expanded_t expanded) -{ - // convert the alpha to 1 bit. then return packed rgb10 - return !!expanded.a | (expanded.r << 30) | (expanded.g << 20) | (expanded.b << 10); -} - -/* Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.) */ -uint32_t nv3_render_get_palette_index(uint8_t index) -{ - uint32_t red_index = index * 3; - uint32_t green_index = red_index + 1; - uint32_t blue_index = red_index + 2; - - uint8_t red_colour = nv3->pramdac.palette[red_index]; - uint8_t green_colour = nv3->pramdac.palette[green_index]; - uint8_t blue_colour = nv3->pramdac.palette[blue_index]; - - /* Alpha is always 0xFF */ - return (0xFF << 24) | ((red_colour) << 16) | ((green_colour) << 8) | blue_colour; -} - -/* Convert a rgb10 colour to a pattern colour */ -void nv3_render_set_pattern_color(nv3_color_expanded_t pattern_colour, bool use_color1) -{ - /* select the right pattern colour, _rgb is already in RGB10 format, so we don't need to do any conversion */ - - if (!use_color1) - { - nv3->pgraph.pattern_color_0_alpha = (pattern_colour.a) & 0xFF; - nv3->pgraph.pattern_color_0_rgb.r = pattern_colour.r; - nv3->pgraph.pattern_color_0_rgb.g = pattern_colour.g; - nv3->pgraph.pattern_color_0_rgb.b = pattern_colour.b; - - } - else - { - nv3->pgraph.pattern_color_1_alpha = (pattern_colour.a) & 0xFF; - nv3->pgraph.pattern_color_1_rgb.r = pattern_colour.r; - nv3->pgraph.pattern_color_1_rgb.g = pattern_colour.g; - nv3->pgraph.pattern_color_1_rgb.b = pattern_colour.b; - } - -} - -/* Combine the current buffer with the pitch to get the address in the framebuffer to draw from for a given position. */ -uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj) -{ - uint32_t vram_x = position.x; - uint32_t vram_y = position.y; - uint32_t current_buffer = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_SRC_BUFFER) & 0x03; - - uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; - - // we have to multiply the x position by the number of bytes per pixel - switch (framebuffer_bpp) - { - case 8: - break; - case 15: - case 16: - vram_x = position.x << 1; - break; - case 32: - vram_x = position.x << 2; - break; - } - - uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[current_buffer] * vram_y) + nv3->pgraph.boffset[current_buffer]; - - pixel_addr_vram &= nv3->nvbase.svga.vram_mask; - - return pixel_addr_vram; -} - - -/* Combine the current buffer with the pitch to get the address in the video ram for a specific position relative to a specific framebuffer */ -uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer) -{ - uint32_t vram_x = position.x; - uint32_t vram_y = position.y; - - uint32_t framebuffer_bpp = nv3->nvbase.svga.bpp; - - // we have to multiply the x position by the number of bytes per pixel - switch (framebuffer_bpp) - { - case 8: - break; - case 15: - case 16: - vram_x = position.x << 1; - break; - case 32: - vram_x = position.x << 2; - break; - } - - uint32_t pixel_addr_vram = vram_x + (nv3->pgraph.bpitch[buffer] * vram_y) + nv3->pgraph.boffset[buffer]; - - pixel_addr_vram &= nv3->nvbase.svga.vram_mask; - - return pixel_addr_vram; -} - -/* Convert a dumb framebuffer address to a position. No buffer setup or anything, but just start at 0,0 for address 0. */ -nv3_coord_16_t nv3_render_get_dfb_position(uint32_t vram_address) -{ - nv3_coord_16_t pos = {0}; - - uint32_t pitch = nv3->nvbase.svga.hdisp; - - if (nv3->nvbase.svga.bpp == 15 - || nv3->nvbase.svga.bpp == 16) - pitch <<= 1; - else if (nv3->nvbase.svga.bpp == 32) - pitch <<= 2; - - pos.y = (vram_address / pitch); - pos.x = (vram_address % pitch); - - /* Fixup our x position */ - if (nv3->nvbase.svga.bpp == 15 - || nv3->nvbase.svga.bpp == 16) - pos.x >>= 1; - else if (nv3->nvbase.svga.bpp == 32) - pos.x >>= 2; - - - /* there is some strange behaviour where it writes long past the end of the fb */ - if (pos.y >= nv3->nvbase.svga.monitor->target_buffer->h) pos.y = nv3->nvbase.svga.monitor->target_buffer->h - 1; - - return pos; -} - -/* Read an 8bpp pixel from the framebuffer. */ -uint8_t nv3_render_read_pixel_8(nv3_coord_16_t position, nv3_grobj_t grobj) -{ - // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); - - return nv3->nvbase.svga.vram[vram_address]; -} - -/* Read an 16bpp pixel from the framebuffer. */ -uint16_t nv3_render_read_pixel_16(nv3_coord_16_t position, nv3_grobj_t grobj) -{ - // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); - - uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); - vram_address >>= 1; //convert to 16bit pointer - - return vram_16[vram_address]; -} - -/* Read an 16bpp pixel from the framebuffer. */ -uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj) -{ - // hope you call it with the right bit - uint32_t vram_address = nv3_render_get_vram_address(position, grobj); - - uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); - vram_address >>= 2; //convert to 32bit pointer - - return vram_32[vram_address]; -} - -void nv3_render_write_pixel_to_buffer(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj, uint32_t buffer) -{ - bool alpha_enabled = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_ALPHA) & 0x01; - - int32_t clip_end_x = nv3->pgraph.clip_start.x + nv3->pgraph.clip_size.x; - int32_t clip_end_y = nv3->pgraph.clip_start.y + nv3->pgraph.clip_size.y; - - /* First, check our current buffer. */ - /* Then do the clip. */ - if (position.x < nv3->pgraph.clip_start.x - || position.y < nv3->pgraph.clip_start.y - || position.x > clip_end_x - || position.y > clip_end_y) - { - // DO NOT DRAW THE PIXEL - return; - } - - /* TODO: Plane Mask...*/ - if (!nv3_render_chroma_test(color, grobj)) - return; - - uint32_t pixel_addr_vram = nv3_render_get_vram_address_for_buffer(position, buffer); - - uint32_t rop_src = 0, rop_dst = 0, rop_pattern = 0; - uint8_t bit = 0x00; - - /* Get our pattern data, may move to another function */ - switch (nv3->pgraph.pattern.shape) - { - - /* This logic is from NV1 envytoos docs, but seems to be same on NV3*/ - case NV3_PATTERN_SHAPE_8X8: - bit = (position.x & 7) | (position.y & 7) << 3; - break; - case NV3_PATTERN_SHAPE_1X64: - bit = (position.x & 0x3f); - break; - case NV3_PATTERN_SHAPE_64X1: - bit = (position.y & 0x3f); - break; - } - - // pull out the actual bit and see which colour we need to use - - bool use_color1 = (nv3->pgraph.pattern_bitmap >> bit) & 0x01; - - if (!use_color1) - { - if (!nv3->pgraph.pattern_color_0_alpha) - return; - - /* This is stupid */ - rop_pattern = nv3_render_downconvert_color(grobj, nv3->pgraph.pattern_color_0_rgb); - } - else - { - if (!nv3->pgraph.pattern_color_1_alpha) - return; - - rop_pattern = nv3_render_downconvert_color(grobj, nv3->pgraph.pattern_color_1_rgb); - } - - - /* Go to vram and do the final ROP for a basic bitblit. - It seems we can skip the downconversion step *for now*, since (framebuffer bits per pixel) == (object bits per pixel) - I'm not sure how games will react. But it depends on how the D3D drivers operate, we may need ro convert texture formats to the current bpp internally. - - We use the pixel format of the destination buffer to achieve this (thanks frostbite2000) - */ - - // translate the patch config to GDI rop - uint32_t final_rop = nv3_render_translate_nvrop(grobj, nv3->pgraph.rop); - - uint32_t destination_format = (nv3->pgraph.bpixel[buffer]) & 0x03; - - switch (destination_format) - { - case bpixel_fmt_8bit: - rop_src = color & 0xFF; - rop_dst = nv3->nvbase.svga.vram[pixel_addr_vram]; - nv3->nvbase.svga.vram[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern) & 0xFF; - - nv3->nvbase.svga.changedvram[pixel_addr_vram >> 12] = changeframecount; - - break; - case bpixel_fmt_16bit: - { - uint16_t* vram_16 = (uint16_t*)(nv3->nvbase.svga.vram); - pixel_addr_vram >>= 1; - - // mask to 16bit - - rop_src = color & 0xFFFF; - - /* if alpha is turned on and we aren't in 565 mode, reject transparent pixels */ - bool is_16bpp = (nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01; - - // a1r5g5b5 - if (!is_16bpp) - { - if (alpha_enabled && - !(color & 0x8000)) - return; - } - - // convert to 15bpp or 16bpp based on if we are in 16bpp mode - - rop_dst = vram_16[pixel_addr_vram]; - - vram_16[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern) & 0xFFFF; - - nv3->nvbase.svga.changedvram[pixel_addr_vram >> 11] = changeframecount; - - break; - } - case bpixel_fmt_32bit: - { - uint32_t* vram_32 = (uint32_t*)(nv3->nvbase.svga.vram); - pixel_addr_vram >>= 2; - - rop_src = color; - rop_dst = vram_32[pixel_addr_vram]; - vram_32[pixel_addr_vram] = video_rop_gdi_ternary(final_rop, rop_src, rop_dst, rop_pattern); - - nv3->nvbase.svga.changedvram[pixel_addr_vram >> 10] = changeframecount; - - break; - } - } -} - -/* Plots a pixel. */ -void nv3_render_write_pixel(nv3_coord_16_t position, uint32_t color, nv3_grobj_t grobj) -{ - // PFB_0 is always set to hardcoded "NO_TILING" value of 0x1114. - // It seems, you are meant to use the CRTC - - nv3_pgraph_destination_buffer dst_buffer = (nv3_pgraph_destination_buffer)grobj.grobj_0; - - if (dst_buffer & (pgraph_dest_buffer0)) - nv3_render_write_pixel_to_buffer(position, color, grobj, 0); - if (dst_buffer & (pgraph_dest_buffer1)) - nv3_render_write_pixel_to_buffer(position, color, grobj, 1); - if (dst_buffer & (pgraph_dest_buffer2)) - nv3_render_write_pixel_to_buffer(position, color, grobj, 2); - if (dst_buffer & (pgraph_dest_buffer3)) - nv3_render_write_pixel_to_buffer(position, color, grobj, 3); -} - -/* Ensure the correct monitor size */ -void nv3_render_ensure_screen_size(void) -{ - /* First check if hdisp == xsize and dispend == ysize. */ - bool changed = false; - - if (nv3->nvbase.svga.hdisp != nv3->nvbase.svga.monitor->mon_xsize) - { - changed = true; - nv3->nvbase.svga.monitor->mon_xsize = nv3->nvbase.svga.hdisp; - } - - if (nv3->nvbase.svga.dispend != nv3->nvbase.svga.monitor->mon_ysize) - { - changed = true; - nv3->nvbase.svga.monitor->mon_ysize = nv3->nvbase.svga.dispend; - } - - /* - if either changed: - -> set resolution - -> set refresh rate - this is just a rough estimation right now. we need it as we only blit what changes - */ - if (changed) - { - nv3->nvbase.refresh_time = 1 / (nv3->nvbase.pixel_clock_frequency / (double)ysize / (double)xsize); // rivatimers count in microseconds - set_screen_size(xsize, ysize); - } - -} - - -/* Blit to the monitor from DFB, 8bpp */ -void nv3_render_current_bpp_dfb_8(uint32_t address) -{ - /* Broken as fuck early vbios does this. Wtf? */ - if (!nv3->nvbase.svga.hdisp) - return; - - nv3_coord_16_t size = {0}; - size.x = size.y = 1; - - nv3_coord_16_t pos = nv3_render_get_dfb_position(address); - - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); - - *p = nv3_render_get_palette_index(data & 0xFF); -} - -/* Blit to the monitor from DFB, 15/16bpp */ -void nv3_render_current_bpp_dfb_16(uint32_t address) -{ - /* Broken as fuck early vbios does this. Wtf? */ - if (!nv3->nvbase.svga.hdisp) - return; - - nv3_coord_16_t size = {0}; - size.x = size.y = 1; - - nv3_coord_16_t pos = nv3_render_get_dfb_position(address); - - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); - - if ((nv3->pramdac.general_control >> NV3_PRAMDAC_GENERAL_CONTROL_565_MODE) & 0x01) - /* should just "tip over" to the next line */ - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 16); - else - /* should just "tip over" to the next line */ - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); - - /*does 8bpp packed into 16 occur/ i would be surprised*/ -} - -/* Blit to the monitor from DFB, 32bpp */ -void nv3_render_current_bpp_dfb_32(uint32_t address) -{ - /* Broken as fuck early vbios does this. Wtf? */ - if (!nv3->nvbase.svga.hdisp) - return; - - nv3_coord_16_t size = {0}; - size.x = size.y = 1; - - nv3_coord_16_t pos = nv3_render_get_dfb_position(address); - - uint32_t data = *(uint32_t*)&(nv3->nvbase.svga.vram[address]); - - uint32_t* p = &nv3->nvbase.svga.monitor->target_buffer->line[pos.y][pos.x]; - - if (nv3->nvbase.svga.bpp == 32) - { - *p = data; - } - /* Packed format */ - else if (nv3->nvbase.svga.bpp == 15 - || nv3->nvbase.svga.bpp == 16) - { - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, nv3->nvbase.svga.bpp); - *p++; - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, (data >> 16) & 0xFFFF, nv3->nvbase.svga.bpp); - } -} - - -/* Blit to the monitor from GPU, current bpp */ -void nv3_render_current_bpp() -{ - /* Figure out the Display Buffer Address from the CRTC */ - - uint32_t dba = ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_RPC0] & 0x1F) << 16) - + (nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_HIGH] << 8) - + nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_STARTADDR_LOW]; - - if (nv3->nvbase.debug_dba_enabled - && nv3->nvbase.debug_dba > 0) - dba = nv3->nvbase.debug_dba; - - nv3_coord_16_t screen_size = {0}; - screen_size.x = nv3->nvbase.svga.hdisp; - screen_size.y = nv3->nvbase.svga.dispend; - - /* Ensure that we are - in the correct mode. Modified SVGA core code */ - nv3_render_ensure_screen_size(); - - /* Don't try and draw stuff that is past the buffer, but, leave it in Video RAM, so it can be used for s2sb's etc */ - - switch (nv3->nvbase.svga.bpp) - { - case 4: - /* Uh we should never be here because we're in the SVGA mode(?) */ - fatal("NV3 - 4bpp not implemented (not even sure if it's SVGA only)"); - break; - case 8: - nv3_render_8bpp(dba, screen_size); - break; - case 15: - nv3_render_15bpp(dba, screen_size); - break; - case 16: - nv3_render_16bpp(dba, screen_size); - break; - case 32: - nv3_render_32bpp(dba, screen_size); - break; - } - -} - -/* - Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, indexed 8 bits per pixel format -*/ - -void nv3_render_8bpp(uint32_t vram_start, nv3_coord_16_t screen_size) -{ - if (!nv3) - return; - - uint32_t vram_current_position = vram_start; - uint32_t* p; - uint32_t data = 0; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - - for (uint32_t y = 0; y < screen_size.y; y++) - { - for (uint32_t x = 0; x < screen_size.x; x++) - { - if (vram_current_position >= nv3->nvbase.vram_amount) - return; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; - - /* should just "tip over" to the next line */ - *p = nv3_render_get_palette_index(data & 0xFF); - - vram_current_position++; - } - } -} - -/* - Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 15 bits per pixel format -*/ - -void nv3_render_15bpp(uint32_t vram_start, nv3_coord_16_t screen_size) -{ - if (!nv3) - return; - - uint32_t vram_current_position = vram_start; - uint32_t* p; - uint32_t data = 0; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - - for (uint32_t y = 0; y < screen_size.y; y++) - { - for (uint32_t x = 0; x < screen_size.x; x++) - { - if (vram_current_position >= nv3->nvbase.vram_amount) - return; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; - - /* should just "tip over" to the next line */ - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); - - vram_current_position += 2; - } - - } -} - -/* - Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 16 bits per pixel format -*/ - -void nv3_render_16bpp(uint32_t vram_start, nv3_coord_16_t screen_size) -{ - if (!nv3) - return; - - uint32_t vram_current_position = vram_start; - uint32_t* p; - uint32_t data = 0; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - - for (uint32_t y = 0; y < screen_size.y; y++) - { - for (uint32_t x = 0; x < screen_size.x; x++) - { - if (vram_current_position >= nv3->nvbase.vram_amount) - return; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; - - /* should just "tip over" to the next line */ - *p = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, data & 0xFFFF, 15); - - vram_current_position += 2; - - } - - } -} - -/* - Blit a certain region from the (destination buffer base + (position in vram)) to the 86Box monitor, 32 bits per pixel format -*/ - -void nv3_render_32bpp(uint32_t vram_start, nv3_coord_16_t screen_size) -{ - if (!nv3) - return; - - uint32_t vram_current_position = vram_start; - uint32_t* p; - uint32_t data = 0; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[0][0]; - - for (uint32_t y = 0; y < screen_size.y; y++) - { - for (uint32_t x = 0; x < screen_size.x; x++) - { - if (vram_current_position >= nv3->nvbase.vram_amount) - return; - - p = &nv3->nvbase.svga.monitor->target_buffer->line[y][x]; - data = *(uint32_t*)&nv3->nvbase.svga.vram[vram_current_position]; - - /* should just "tip over" to the next line */ - *p = data; - - vram_current_position += 4; - } - } -} - -// Translate an "NV-ROP" into a GDI Ternary ROP -uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop) -{ - // Credit to envytools for this function: - // https://github.com/envytools/envytools/blob/f102b82381f3f11cee113d16374c87091db039d9/nvhw/pgraph.c - // How does one even go about reverse engineering this (I'm sure the behaviour is simpler when you don't have to translate this but...) Marcelina is a legend. - - uint32_t patch_config_rop = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F; - - /* || patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0*/ - - // TODO: Blending - if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS) // 0x00 is used for "nothing here" it seems. - return VIDEO_ROP_SRC_COPY; - - uint8_t res = 0; - - int32_t swizzle[3]; - - if (patch_config_rop < 8) { - swizzle[0] = patch_config_rop >> 0 & 1; - swizzle[1] = patch_config_rop >> 1 & 1; - swizzle[2] = patch_config_rop >> 2 & 1; - } else if (patch_config_rop < 0x10) { - swizzle[0] = (patch_config_rop >> 0 & 1) + 1; - swizzle[1] = (patch_config_rop >> 1 & 1) + 1; - swizzle[2] = (patch_config_rop >> 2 & 1) + 1; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST) { - swizzle[0] = 0, swizzle[1] = 1, swizzle[2] = 2; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_DST_SRC) { - swizzle[0] = 1, swizzle[1] = 0, swizzle[2] = 2; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_PAT_DST) { - swizzle[0] = 0, swizzle[1] = 2, swizzle[2] = 1; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_DST_PAT) { - swizzle[0] = 2, swizzle[1] = 0, swizzle[2] = 1; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_PAT_SRC) { - swizzle[0] = 1, swizzle[1] = 2, swizzle[2] = 0; - } else if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_DST_SRC_PAT) { - swizzle[0] = 2, swizzle[1] = 1, swizzle[2] = 0; - } else { - warning("NV3 ROP: Invalid patch configuration %02x!", rop); - } - if (patch_config_rop == 0) { - if (rop & 0x01) - res |= 0x11; - if (rop & 0x16) - res |= 0x44; - if (rop & 0x68) - res |= 0x22; - if (rop & 0x80) - res |= 0x88; - } else if (patch_config_rop == 0xf) { - if (rop & 0x01) - res |= 0x03; - if (rop & 0x16) - res |= 0x0c; - if (rop & 0x68) - res |= 0x30; - if (rop & 0x80) - res |= 0xc0; - } else { - int32_t i; - for (i = 0; i < 8; i++) { - int32_t s0 = i >> swizzle[0] & 1; - int32_t s1 = i >> swizzle[1] & 1; - int32_t s2 = i >> swizzle[2] & 1; - int32_t s = s2 << 2 | s1 << 1 | s0; - if (rop >> s & 1) - res |= 1 << i; - } - } - - return res; -} \ No newline at end of file diff --git a/src/video/nv/nv3/render/nv3_render_primitives.c b/src/video/nv/nv3/render/nv3_render_primitives.c deleted file mode 100644 index e2d088c3f..000000000 --- a/src/video/nv/nv3/render/nv3_render_primitives.c +++ /dev/null @@ -1,355 +0,0 @@ -/* -* 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 code to render basic objects. -* -* -* -* Authors: Connor Hyde, I need a better email address ;^) -* -* Copyright 2024-2025 Connor Hyde -*/ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> -#include <86box/utils/video_stdlib.h> - -void nv3_render_rect(nv3_coord_16_t position, nv3_coord_16_t size, uint32_t color, nv3_grobj_t grobj) -{ - nv3_coord_16_t current_pos = {0}; - - for (int32_t y = position.y; y < (position.y + size.y); y++) - { - current_pos.y = y; - - for (int32_t x = position.x; x < (position.x + size.x); x++) - { - current_pos.x = x; - - nv3_render_write_pixel(current_pos, color, grobj); - } - } -} - -/* Render GDI-B clipped rectangle */ -void nv3_render_rect_clipped(nv3_clip_16_t clip, uint32_t color, nv3_grobj_t grobj) -{ - nv3_coord_16_t current_pos = {0}; - - for (int32_t y = clip.top; y < clip.bottom; y++) - { - current_pos.y = y; - - for (int32_t x = clip.left; x < clip.right; x++) - { - current_pos.x = x; - - /* compare against the global clip too */ - if (current_pos.x >= nv3->pgraph.win95_gdi_text.clip_b.left - && current_pos.x <= nv3->pgraph.win95_gdi_text.clip_b.right - && current_pos.y >= nv3->pgraph.win95_gdi_text.clip_b.top - && current_pos.y <= nv3->pgraph.win95_gdi_text.clip_b.bottom) - { - nv3_render_write_pixel(current_pos, color, grobj); - } - } - } -} - -void nv3_render_gdi_transparent_bitmap_blit(bool bit, bool clip, uint32_t color, nv3_grobj_t grobj) -{ - /* If the bit is set, and cliping is enabled (Type D) tru and lcip */ - if (bit && clip) - { - /* Turn the bit off if we need to clip (Type D ) */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_d.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_d.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_d.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_d.bottom) - { - bit = false; - } - - /* - Also clip if we are outside of the SIZE_OUT range - We only need to do this in one direction just to get rid of the crud sent by NV - */ - uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_d.x + (nv3->pgraph.win95_gdi_text.size_out_d.x); - uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_d.y + (nv3->pgraph.win95_gdi_text.size_out_d.y); - - if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x - || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) - bit = false; - } - - /* We don't need to and it, because it seems the Riva only uses non-packed bpp formats for this class */ - if (bit) - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, color, grobj); - - /* - Check if we've reached the bottom - Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) - */ - - uint32_t end_x = (clip) ? nv3->pgraph.win95_gdi_text.point_d.x + nv3->pgraph.win95_gdi_text.size_in_d.x : nv3->pgraph.win95_gdi_text.point_c.x + nv3->pgraph.win95_gdi_text.size_c.x; - - nv3->pgraph.win95_gdi_text_current_position.x++; - - if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) - { - nv3->pgraph.win95_gdi_text_current_position.y++; - - if (!clip) - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_c.x; - else - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_d.x; - } - -} - -/* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ -void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj) -{ - /* - First, we need to figure out how many bits we have left. - If we have less than 32, don't process all of the bits. - - Bits are processed in the following order: [7-0] [15-8] [23-16] [31-24] - TODO: Store this somewhere, so it doesn't need to be recalculated. - - We store a global bit count for this purpose. - */ - - uint32_t bitmap_size = (clip) ? nv3->pgraph.win95_gdi_text.size_in_d.x * nv3->pgraph.win95_gdi_text.size_in_d.y : nv3->pgraph.win95_gdi_text.size_c.x * nv3->pgraph.win95_gdi_text.size_c.y; - uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; - - /* we have to interpret every bit in reverse bit order but in the right byte order */ - - bool current_bit = false; - - /* Start by rendering bits 7 through 0 */ - for (int32_t bit = 7; bit >= 0; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 15 through 8 */ - for (int32_t bit = 15; bit >= 8; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 23 through 16 */ - for (int32_t bit = 23; bit >= 16; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 31 through 24 */ - for (int32_t bit = 31; bit >= 24; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_transparent_bitmap_blit(current_bit, clip, color, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - -} - -void nv3_render_gdi_1bpp_bitmap_blit(bool bit, uint32_t color0, uint32_t color1, nv3_grobj_t grobj) -{ - /* We can't force the bit off because this is a 1bpp bitmap */ - bool skip = false; - - /* For Type E, always clip */ - /* Turn the bit off if we need to clip (Type D ) */ - if (nv3->pgraph.win95_gdi_text_current_position.x < nv3->pgraph.win95_gdi_text.clip_e.left - || nv3->pgraph.win95_gdi_text_current_position.x > nv3->pgraph.win95_gdi_text.clip_e.right - || nv3->pgraph.win95_gdi_text_current_position.y < nv3->pgraph.win95_gdi_text.clip_e.top - || nv3->pgraph.win95_gdi_text_current_position.y > nv3->pgraph.win95_gdi_text.clip_e.bottom) - { - skip = true; - } - - /* - Also clip if we are outside of the SIZE_OUT range - We only need to do this in one direction just to get rid of the crud sent by NV - */ - uint32_t clip_x = nv3->pgraph.win95_gdi_text.point_e.x + (nv3->pgraph.win95_gdi_text.size_out_e.x); - uint32_t clip_y = nv3->pgraph.win95_gdi_text.point_e.y + (nv3->pgraph.win95_gdi_text.size_out_e.y); - - if (nv3->pgraph.win95_gdi_text_current_position.x >= clip_x - || nv3->pgraph.win95_gdi_text_current_position.y >= clip_y) - skip = true; - - /* We don't need to and it, because it seems the Riva only uses non-packed bpp formats for this class */ - if (!skip) - { - if (bit) - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, nv3->pgraph.win95_gdi_text.color1_e, grobj); - else - nv3_render_write_pixel(nv3->pgraph.win95_gdi_text_current_position, nv3->pgraph.win95_gdi_text.color0_e, grobj); - } - - - /* - Check if we've reached the bottom, if so, advance to the next horizontal lin - Because we check the bits in reverse, we go forward (bits 7,6,5 were set for a 1x3 bitmap) - */ - - uint32_t end_x = nv3->pgraph.win95_gdi_text.point_e.x + nv3->pgraph.win95_gdi_text.size_in_e.x; - - nv3->pgraph.win95_gdi_text_current_position.x++; - - if (nv3->pgraph.win95_gdi_text_current_position.x >= end_x) - { - nv3->pgraph.win95_gdi_text_current_position.y++; - nv3->pgraph.win95_gdi_text_current_position.x = nv3->pgraph.win95_gdi_text.point_e.x; - } -} - - -/* Originally written 23 March 2025, but then, redone, properly, on 30 March 2025 */ -void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj) -{ - /* - First, we need to figure out how many bits we have left. - If we have less than 32, don't process all of the bits. - - Bits are processed in the following order: [7-0] [15-8] [23-16] [31-24] - TODO: Store this somewhere, so it doesn't need to be recalculated. - - We store a global bit count for this purpose. - */ - - uint32_t bitmap_size = nv3->pgraph.win95_gdi_text.size_in_e.x * nv3->pgraph.win95_gdi_text.size_in_e.y; - uint32_t bits_remaining_in_bitmap = bitmap_size - nv3->pgraph.win95_gdi_text_bit_count; - - /* we have to interpret every bit in reverse bit order but in the right byte order */ - - bool current_bit = false; - - /* Start by rendering bits 7 through 0 */ - for (int32_t bit = 7; bit >= 0; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 15 through 8 */ - for (int32_t bit = 15; bit >= 8; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 23 through 16 */ - for (int32_t bit = 23; bit >= 16; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; - - /* Now for 31 through 24 */ - for (int32_t bit = 31; bit >= 24; bit--) - { - current_bit = (bitmap_data >> bit) & 0x01; - - nv3_render_gdi_1bpp_bitmap_blit(current_bit, color0, color1, grobj); - nv3->pgraph.win95_gdi_text_bit_count++; - bits_remaining_in_bitmap--; - - if (!bits_remaining_in_bitmap) - break; - } - - /* IF we're done, let's return */ - if (!bits_remaining_in_bitmap) - return; -} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pbus.c b/src/video/nv/nv3/subsystems/nv3_pbus.c deleted file mode 100644 index a2e340e85..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pbus.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_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_DEBUG_0, "PBUS - Debug Register", NULL, NULL}, - { 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(void) -{ - nv_log("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])); - - uint32_t ret = 0x00; - - // todo: friendly logging - - nv_log_verbose_only("PBUS Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - switch (reg->address) - { - case NV3_PBUS_DEBUG_0: - ret = nv3->pbus.debug_0; - break; - case NV3_PBUS_INTR: - ret = nv3->pbus.interrupt_status; - break; - case NV3_PBUS_INTR_EN: - ret = nv3->pbus.interrupt_enable; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PBUS Write 0x%08x -> 0x%08x\n", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - case NV3_PBUS_DEBUG_0: - nv3->pbus.debug_0 = value; - break; - // 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; - } - - } - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} - -uint8_t nv3_pbus_rma_read(uint16_t addr) -{ - addr &= 0xFF; - uint32_t real_final_address = 0x0; - uint8_t ret = 0x0; - - 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 - { - /* Do we need to read RAMIN here? */ - ret = nv3->nvbase.svga.vram[real_final_address - NV3_MMIO_SIZE] & (nv3->nvbase.svga.vram_max - 1); - } - - // log current location for vbios RE - nv_log_verbose_only("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) -{ - // 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_verbose_only("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? - { - uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; - vram_32[(nv3->pbus.rma.addr - NV3_MMIO_SIZE) >> 2] = nv3->pbus.rma.data; - } - - - 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 deleted file mode 100644 index 3359e9079..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pbus_dma.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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 & Notifier Engine - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/dma.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> - -/* Nvidia DMA Engine */ - -void nv3_perform_dma_m2mf(nv3_grobj_t grobj) -{ - // notify object base=grobj_1 >> 12 - uint32_t notify_obj_base = grobj.grobj_1 >> 12; - - uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); - uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); - uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); - - /* extract some important information*/ - uint32_t info_adjust = notify_obj_info & 0xFFF; - bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; - uint8_t info_dma_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; - - /* paging information */ - bool page_is_present = notify_obj_page & 0x01; - bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); - uint32_t frame_base = notify_obj_page & 0xFFFFF000; - - // This code is temporary and will probably be moved somewhere else - // Print torns of debug info - #ifdef DEBUG - nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); - - nv_log_verbose_only("M2MF DMA Information:\n"); - nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); - (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); - - switch (info_dma_target) - { - case NV3_DMA_TARGET_NODE_VRAM: - nv_log_verbose_only("Notification Target: VRAM\n"); - break; - case NV3_DMA_TARGET_NODE_CART: - nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); - break; - case NV3_DMA_TARGET_NODE_PCI: - (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); - break; - case NV3_DMA_TARGET_NODE_AGP: - (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); - break; - } - - nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); - (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); - (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); - nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); - #endif - - // set up the dma transfer. we need to translate to a physical address. - - uint32_t final_address = 0; - - /* M2MF DMA only uses HW type */ - - final_address = frame_base + info_adjust; - - /* send the notification off */ - nv_log("About to send M2MF DMA to 0x%08x (Check target)\n", final_address); - - uint32_t offset_in = (nv3->pgraph.m2mf.offset_in); - uint32_t offset_out = (nv3->pgraph.m2mf.offset_out); - - uint32_t pitch_in = nv3->pgraph.m2mf.pitch_in; - uint32_t pitch_out = nv3->pgraph.m2mf.pitch_out; - - // pitch out surely can't be 0 - if (pitch_out == 0) - pitch_out = pitch_in; - - uint32_t bytes_per_scanline = nv3->pgraph.m2mf.scanline_length; - - uint8_t increment_in = (nv3->pgraph.m2mf.format) & 0x07; - uint8_t increment_out = (nv3->pgraph.m2mf.format >> NV3_M2MF_FORMAT_INPUT) & 0x07; - - for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++) - { - for (uint32_t pixel = offset_in; pixel < (offset_in + bytes_per_scanline); pixel += increment_in) - { - nv3->nvbase.svga.vram[offset_out] = nv3->nvbase.svga.vram[offset_in]; - offset_out += increment_out; - } - - offset_in += pitch_in; - offset_out += pitch_out; - } - - /* - switch (info_dma_target) - { - // for M2MF only NVM target node is used. - - case NV3_DMA_TARGET_NODE_VRAM: - - - uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; - - break; - case NV3_DMA_TARGET_NODE_PCI: - case NV3_DMA_TARGET_NODE_AGP: - // Idk how to implement increments of more than 1 but only 1 increments seem to be used with these. - uint32_t size_in = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_in; - uint32_t size_out = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_out; - - uint8_t* page_in = calloc(1, size_in); - - for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++) - { - - } - - dma_bm_read(offset_in, page_in, size_in, size_in); - dma_bm_write(offset_out, page_in, size_out, size_out); - - break; - } -*/ - // we're done - nv3->pgraph.notify_pending = false; -} - - -/* Sees if any notification is required after an object method is executed. If so, executes it... */ -void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t context, nv3_grobj_t grobj) -{ - if (!nv3->pgraph.notify_pending) - return; - - uint32_t current_notification_object = nv3->pgraph.notifier; - uint32_t notification_type = ((current_notification_object >> NV3_PGRAPH_NOTIFY_REQUEST_TYPE) & 0x07); - - // check for a software method (0 = hardware, 1 = software) - if (notification_type != 0) - { - nv_log("Software Notification, firing interrupt"); - nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_SOFTWARE_NOTIFY); - //return; - } - - // set up the NvNotification structure - nv3_notification_t notify = {0}; - notify.nanoseconds = nv3->ptimer.time; - notify.status = NV3_NOTIFICATION_STATUS_DONE_OK; // it should be fine to just signal that it's ok - - // these are only nonzero when there is an error - notify.info32 = notify.info16 = 0; - - // notify object base=grobj_1 >> 12 - uint32_t notify_obj_base = grobj.grobj_1 >> 12; - - uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3); - uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3); - uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3); - - /* extract some important information*/ - uint32_t info_adjust = notify_obj_info & 0xFFF; - bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01; - uint8_t info_notification_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03; - - /* paging information */ - bool page_is_present = notify_obj_page & 0x01; - bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS); - uint32_t frame_base = notify_obj_page & 0xFFFFF000; - - // This code is temporary and will probably be moved somewhere else - // Print torns of debug info - #ifdef DEBUG - nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n"); - - nv_log_verbose_only("Notification Information:\n"); - nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust); - (info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n"); - - switch (info_notification_target) - { - case NV3_DMA_TARGET_NODE_VRAM: - nv_log_verbose_only("Notification Target: VRAM\n"); - break; - case NV3_DMA_TARGET_NODE_CART: - nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n"); - break; - case NV3_DMA_TARGET_NODE_PCI: - (nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n"); - break; - case NV3_DMA_TARGET_NODE_AGP: - (nv3->nvbase.bus_generation == nv_bus_agp_1x - || nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n"); - break; - } - - nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit); - (page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n"); - (page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n"); - nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base); - #endif - - // set up the dma transfer. we need to translate to a physical address. - - uint32_t final_address = 0; - - /* Simple case: hardware notification, we can just take the pte since it's based on the type */ - if (notification_type == 0) - { - final_address = frame_base + info_adjust; - } - else - { - // for software we have to calculate the pte index - uint32_t pte_num = ((notification_type << 4) + info_adjust) >> 12; - - /* ramin entries are sorted - 1 object for each pte entry...*/ - final_address = nv3_ramin_read32(notify_obj_base + (0x10 * pte_num) + 8, nv3); - final_address += (info_adjust & 0xFFF); - } - - /* send the notification off */ - nv_log("About to send hardware notification to 0x%08x (Check target)\n", final_address); - - switch (info_notification_target) - { - case NV3_DMA_TARGET_NODE_VRAM: - - uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram; - - // increment by 1 because each index increments by 4 - vram_32[final_address] = (notify.nanoseconds & 0xFFFFFFFF); - vram_32[final_address + 1] = (notify.nanoseconds >> 32); - vram_32[final_address + 2] = notify.info32; - vram_32[final_address + 3] = (notify.info16 | notify.status); - break; - case NV3_DMA_TARGET_NODE_PCI: - case NV3_DMA_TARGET_NODE_AGP: - dma_bm_write(final_address, (uint8_t*)¬ify, sizeof(nv3_notification_t), 4); - break; - } - - // we're done - nv3->pgraph.notify_pending = false; -} \ 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 deleted file mode 100644 index c89d540c3..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pextdev.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_pextdev_init(void) -{ - nv_log("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("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("Straps = 0x%04x\n", nv3->pextdev.straps); - nv_log("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])); - - uint32_t ret = 0x00; - - // special consideration for straps - if (address == NV3_PSTRAPS) - { - nv_log_verbose_only("Straps Read (current value=0x%08x)\n", nv3->pextdev.straps); - } - else - { - nv_log_verbose_only("PEXTDEV Read from 0x%08x", address); - } - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - switch (reg->address) - { - case NV3_PSTRAPS: - ret = nv3->pextdev.straps; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PEXTDEV Write 0x%08x -> 0x%08x\n", value, address); - - // special consideration for straps - if (address == NV3_PSTRAPS) - { - /* For some reason, all RIVA 128 ZX VBIOSes try to write to the straps. So only indicate this as a problem and return on Rev A/B */ - if (nv3->nvbase.gpu_revision != NV3_PCI_CFG_REVISION_C00) - { - warning("Huh? Tried to write to the straps (value=%d). Something is wrong...\n", nv3->pextdev.straps); - return; - } - } - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} \ 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 deleted file mode 100644 index 7360a2c9a..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pfb.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -// Functions only used in this translation unit -uint32_t nv3_pfb_config0_read(void); -void nv3_pfb_config0_write(uint32_t val); - -nv_register_t pfb_registers[] = { - { NV3_PFB_BOOT, "PFB Boot Config", NULL, NULL}, - { NV3_PFB_DELAY, "PFB Delay", NULL, NULL}, - { NV3_PFB_DEBUG_0, "PFB Debug", NULL, NULL }, - { NV3_PFB_GREEN_0, "PFB Green / Power Saving", NULL, NULL,}, - { NV3_PFB_CONFIG_0, "PFB Framebuffer Config 0", nv3_pfb_config0_read, nv3_pfb_config0_write }, - { NV3_PFB_CONFIG_1, "PFB Framebuffer Config 1", NULL, NULL }, - { NV3_PFB_RTL, "PFB RTL (Part of memory timings)", NULL, NULL }, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -void nv3_pfb_init(void) -{ - nv_log("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) - ); - - if (nv3->nvbase.vram_amount == NV3_VRAM_SIZE_4MB) - nv3->pfb.boot |= (NV3_PFB_BOOT_RAM_AMOUNT_4MB << NV3_PFB_BOOT_RAM_AMOUNT); - else - nv3->pfb.boot |= (NV3_PFB_BOOT_RAM_AMOUNT_8MB << 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])); - - uint32_t ret = 0x00; - - // todo: friendly logging - - nv_log_verbose_only("PFB Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - switch (reg->address) - { - case NV3_PFB_BOOT: - ret = nv3->pfb.boot; - break; - case NV3_PFB_DEBUG_0: - ret = nv3->pfb.debug_0; - break; - // Config 0 has a read/write function - case NV3_PFB_CONFIG_1: - ret = nv3->pfb.config_1; - break; - case NV3_PFB_GREEN_0: - ret = nv3->pfb.green; - break; - case NV3_PFB_DELAY: - ret = nv3->pfb.delay; - break; - case NV3_PFB_RTL: - ret = nv3->pfb.rtl; - break; - - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PFB Write 0x%08x -> 0x%08x", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - // Boot is read only - case NV3_PFB_DEBUG_0: - nv3->pfb.debug_0 = value; - break; - // Config 0 has a read/write function - case NV3_PFB_CONFIG_1: // Config Register 1 - nv3->pfb.config_1 = value; - break; - case NV3_PFB_GREEN_0: - nv3->pfb.green = value; - break; - case NV3_PFB_DELAY: - nv3->pfb.delay = value; - break; - case NV3_PFB_RTL: - nv3->pfb.rtl = value; - break; - } - } - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} - -uint32_t nv3_pfb_config0_read(void) -{ - return nv3->pfb.config_0; -} - -void nv3_pfb_config0_write(uint32_t val) -{ - nv3->pfb.config_0 = val; - - // i think the actual size and pixel depth are set in PRAMDAC - // so we don't update things here for now - - uint32_t new_pfb_htotal = (nv3->pfb.config_0 & 0x3F) << 5; - // i don't think 16:9 is supported - uint32_t new_pfb_vtotal = new_pfb_htotal * (3.0/4.0); - uint32_t new_bit_depth = (nv3->pfb.config_0 >> 8) & 0x03; - - - // This doesn't actually seem very useful - - nv_log_verbose_only("Framebuffer Config Change\n"); - nv_log_verbose_only("Horizontal Size=%d pixels\n", new_pfb_htotal); - nv_log_verbose_only("Vertical Size @ 4:3=%d pixels\n", new_pfb_vtotal); - - if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_8BPP) - nv_log_verbose_only("Bit Depth=8bpp\n"); - else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_16BPP) - nv_log_verbose_only("Bit Depth=16bpp\n"); - else if (new_bit_depth == NV3_PFB_CONFIG_0_DEPTH_32BPP) - nv_log_verbose_only("Bit Depth=32bpp\n"); - -} \ 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 deleted file mode 100644 index 9ddcda0d3..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pfifo.c +++ /dev/null @@ -1,985 +0,0 @@ -/* - * 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) - * PIO object submission - * Gray code conversion routines - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/dma.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> - -// -// ****** 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,}, - { NV3_PFIFO_DELAY_0, "PFIFO - DMA Delay/Retry Register", NULL, NULL}, - { NV3_PFIFO_DEBUG_0, "PFIFO - Debug 0", NULL, NULL, }, - { NV3_PFIFO_CONFIG_0, "PFIFO - Config 0", NULL, NULL, }, - { NV3_PFIFO_CONFIG_RAMFC, "PFIFO - RAMIN RAMFC Config", NULL, NULL }, - { NV3_PFIFO_CONFIG_RAMHT, "PFIFO - RAMIN RAMHT Config", NULL, NULL }, - { NV3_PFIFO_CONFIG_RAMRO, "PFIFO - RAMIN RAMRO Config", NULL, NULL }, - { NV3_PFIFO_CACHE_REASSIGNMENT, "PFIFO - Allow Cache Channel Reassignment", NULL, NULL }, - { NV3_PFIFO_CACHE0_PULL0, "PFIFO - Cache0 Puller Control", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller Control"}, - { NV3_PFIFO_CACHE0_PULLER_CTX_STATE, "PFIFO - Cache0 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULL0, "PFIFO - Cache1 Puller State0", NULL, NULL}, - { NV3_PFIFO_CACHE1_PULLER_CTX_STATE, "PFIFO - Cache1 Puller State1 (Is context clean?)", NULL, NULL}, - { NV3_PFIFO_CACHE0_PUSH_ENABLED, "PFIFO - Cache0 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE1_PUSH_ENABLED, "PFIFO - Cache1 Access", NULL, NULL, }, - { NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID, "PFIFO - Cache0 Push Channel ID", NULL, NULL, }, - { NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID, "PFIFO - Cache1 Push Channel ID", NULL, NULL, }, - { NV3_PFIFO_CACHE0_ERROR_PENDING, "PFIFO - Cache0 DMA Error Pending?", NULL, NULL, }, - { NV3_PFIFO_CACHE0_STATUS, "PFIFO - Cache0 Status", NULL, NULL}, - { NV3_PFIFO_CACHE1_STATUS, "PFIFO - Cache1 Status", NULL, NULL}, - { NV3_PFIFO_CACHE0_GET, "PFIFO - Cache0 Get", NULL, NULL }, - { NV3_PFIFO_CACHE0_CTX, "PFIFO - Cache0 Context", NULL, NULL }, - { NV3_PFIFO_CACHE1_GET, "PFIFO - Cache1 Get", NULL, NULL }, - { NV3_PFIFO_CACHE0_PUT, "PFIFO - Cache0 Put", NULL, NULL }, - { NV3_PFIFO_CACHE1_PUT, "PFIFO - Cache1 Put", NULL, NULL }, - //Cache1 exclusive stuff - { NV3_PFIFO_CACHE1_DMA_CONFIG_0, "PFIFO - Cache1 DMA Access (bit 0: is running, bit 4: is busy)"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_1, "PFIFO - Cache1 DMA Length"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_2, "PFIFO - Cache1 DMA Address"}, - { NV3_PFIFO_CACHE1_DMA_CONFIG_3, "PFIFO - Cache1 DMA Target Node"}, - { NV3_PFIFO_CACHE1_DMA_STATUS, "PFIFO - Cache1 DMA Status"}, - { NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE, "PFIFO - Cache1 DMA TLB - Pagetable Base"}, - { NV3_PFIFO_CACHE1_DMA_TLB_PTE, "PFIFO - Cache1 DMA TLB - Pagetable Entry (31:12 - Frame Address; bit 0 - Is Present)"}, - { NV3_PFIFO_CACHE1_DMA_TLB_TAG, "PFIFO - Cache1 DMA TLB - Tag"}, - //Runout - { NV3_PFIFO_RUNOUT_GET, "PFIFO Runout Get Address [8:3 if 512b, otherwise 12:3]"}, - { NV3_PFIFO_RUNOUT_PUT, "PFIFO Runout Put Address [8:3 if 512b, otherwise 12:3]"}, - { NV3_PFIFO_RUNOUT_STATUS, "PFIFO Runout Status"}, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -// PFIFO init code -void nv3_pfifo_init(void) -{ - nv_log("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("Repressing PFIFO read. The subsystem is disabled according to pmc_enable, returning 0\n"); - return 0x00; - } - - uint32_t ret = 0x00; - - nv_register_t* reg = nv_get_register(address, pfifo_registers, sizeof(pfifo_registers)/sizeof(pfifo_registers[0])); - - // todo: friendly logging - - nv_log_verbose_only("PFIFO Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - - // on-read function - if (reg->on_read) - ret = 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: - ret = nv3->pfifo.interrupt_status; - break; - case NV3_PFIFO_INTR_EN: - ret = nv3->pfifo.interrupt_enable; - break; - case NV3_PFIFO_DELAY_0: - ret = nv3->pfifo.dma_delay_retry; - break; - // Debug - case NV3_PFIFO_DEBUG_0: - ret = nv3->pfifo.debug_0; - break; - case NV3_PFIFO_CONFIG_0: - ret = nv3->pfifo.config_0; - break; - // Some of these may need to become functions. - case NV3_PFIFO_CONFIG_RAMFC: - ret = nv3->pfifo.ramfc_config; - break; - case NV3_PFIFO_CONFIG_RAMHT: - ret = nv3->pfifo.ramht_config; - break; - case NV3_PFIFO_CONFIG_RAMRO: - ret = nv3->pfifo.ramro_config; - break; - /* These automatically trigger pulls when 1 is written */ - case NV3_PFIFO_CACHE0_PULL0: - ret = nv3->pfifo.cache0_settings.pull0; - break; - case NV3_PFIFO_CACHE1_PULL0: - ret = nv3->pfifo.cache1_settings.pull0; - break; - case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: - ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; - break; - case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: - ret = (nv3->pfifo.cache0_settings.context_is_dirty) ? (1 << NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) : 0; - break; - /* Does this automatically push? */ - case NV3_PFIFO_CACHE0_PUSH_ENABLED: - ret = nv3->pfifo.cache0_settings.push0; - break; - case NV3_PFIFO_CACHE1_PUSH_ENABLED: - ret = nv3->pfifo.cache1_settings.push0; - break; - case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: - ret = nv3->pfifo.cache0_settings.channel; - break; - case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: - ret = nv3->pfifo.cache1_settings.channel; - break; - case NV3_PFIFO_CACHE0_STATUS: - // CACHE0 has only one entry so it can only ever be empty or full - - if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache0_settings.get_address) - ret |= 1 << NV3_PFIFO_CACHE0_STATUS_EMPTY; - else - ret |= 1 << NV3_PFIFO_CACHE0_STATUS_FULL; - - break; - case NV3_PFIFO_CACHE1_STATUS: - // CACHE1 doesn't... - - if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) - ret |= 1 << NV3_PFIFO_CACHE1_STATUS_EMPTY; - - // Check if Cache1 (0x7C bytes in size depending on gpu?) is full - // Based on how the drivers do it - if (!nv3_pfifo_cache1_num_free_spaces()) - ret |= 1 << NV3_PFIFO_CACHE1_STATUS_FULL; - - if (nv3->pfifo.runout_put != nv3->pfifo.runout_get) - ret |= 1 << NV3_PFIFO_CACHE1_STATUS_RANOUT; - - break; - case NV3_PFIFO_CACHE0_PUT: - ret = nv3->pfifo.cache0_settings.put_address; - break; - case NV3_PFIFO_CACHE0_GET: - ret = nv3->pfifo.cache0_settings.get_address; - break; - case NV3_PFIFO_CACHE1_PUT: - ret = nv3->pfifo.cache1_settings.put_address; - break; - case NV3_PFIFO_CACHE1_GET: - ret = nv3->pfifo.cache1_settings.get_address; - break; - // Reassignment - case NV3_PFIFO_CACHE_REASSIGNMENT: - ret = nv3->pfifo.cache_reassignment & 0x01; //1bit meaningful - break; - // Cache1 exclusive stuff - // Control - case NV3_PFIFO_CACHE1_DMA_CONFIG_0: - ret = nv3->pfifo.cache1_settings.dma_state; - break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_1: - ret = nv3->pfifo.cache1_settings.dma_length & (NV3_VRAM_SIZE_8MB) - 4; //MAX vram size - break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_2: - ret = nv3->pfifo.cache1_settings.dma_address; - break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_3: - if (nv3->nvbase.bus_generation == nv_bus_pci) - return NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_PCI; - else - return NV3_PFIFO_CACHE1_DMA_CONFIG_3_TARGET_NODE_AGP; - break; - case NV3_PFIFO_CACHE1_DMA_STATUS: - ret = nv3->pfifo.cache1_settings.dma_status; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE: - ret = nv3->pfifo.cache1_settings.dma_tlb_pt_base; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_PTE: - ret = nv3->pfifo.cache1_settings.dma_tlb_pte; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_TAG: - ret = nv3->pfifo.cache1_settings.dma_tlb_tag; - break; - // Runout - case NV3_PFIFO_RUNOUT_GET: - ret = nv3->pfifo.runout_get; - break; - case NV3_PFIFO_RUNOUT_PUT: - ret = nv3->pfifo.runout_put; - break; - case NV3_PFIFO_RUNOUT_STATUS: - if (nv3->pfifo.runout_put == nv3->pfifo.runout_get) - ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_EMPTY; /* good news */ - else - ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_RANOUT; /* bad news */ - - /* TODO: the following code sucks (move to a functio?) */ - - uint32_t new_size_ramro = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - if (new_size_ramro == 0) - new_size_ramro = 0x200; - else if (new_size_ramro == 1) - new_size_ramro = 0x2000; - - // WTF? - if (nv3->pfifo.runout_put + 0x08 & (new_size_ramro - 0x08) == nv3->pfifo.runout_get) - ret |= 1 << NV3_PFIFO_RUNOUT_STATUS_FULL; /* VERY BAD news */ - - break; - - /* Cache1 is handled below - cache0 only has one entry */ - case NV3_PFIFO_CACHE0_CTX: - ret = nv3->pfifo.cache0_settings.context[0]; - break; - - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - /* Handle some special memory areas */ - else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) - { - uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; - ret = nv3->pfifo.cache1_settings.context[ctx_entry_id]; - - nv_log_verbose_only("PFIFO Cache1 CTX Read Entry=%d Value=0x%04x\n", ctx_entry_id, ret); - } - /* Direct cache read stuff */ - else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) - { - nv_log_verbose_only("PFIFO Cache0 Read\n"); - - // See if we want the object name or the channel/subchannel information. - if (address & 4) - { - nv_log_verbose_only("Data=0x%08x\n", nv3->pfifo.cache0_entry.data); - - return nv3->pfifo.cache0_entry.data; - } - else - { - uint32_t final = nv3->pfifo.cache0_entry.method | (nv3->pfifo.cache0_entry.subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - - nv_log_verbose_only("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); - - - return final; - } - } - else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) - { - // Not sure if REV C changes this. It should... - uint32_t slot = 0; - - // shift right by 3, convert from address, to slot. - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - slot = (address >> 3) & 0x3F; - else - slot = (address >> 3) & 0x1F; - - nv_log_verbose_only("PFIFO Cache1 Read slot=%d", slot); - - // See if we want the object name or the channel/subchannel information. - if (address & 4) - { - nv_log_verbose_only("Data=0x%08x\n", nv3->pfifo.cache1_entries[slot].data); - return nv3->pfifo.cache1_entries[slot].data; - } - else - { - uint32_t final = nv3->pfifo.cache1_entries[slot].method | (nv3->pfifo.cache1_entries[slot].subchannel << NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL); - nv_log_verbose_only("Param (subchannel=15:13, method=12:2)=0x%08x\n", final); - return final; - } - - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void nv3_pfifo_trigger_dma_if_required(void) -{ - // Not a thing for cache0 - - bool cache1_dma = false; - - /* Check that DMA is enabled */ - if ((nv3->pfifo.cache1_settings.dma_state & NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING) - && nv3->pfifo.cache1_settings.dma_enabled) - { - uint32_t bytes_to_send = nv3->pfifo.cache1_settings.dma_length; - uint32_t where_to_send = nv3->pfifo.cache1_settings.dma_address; - uint32_t target_node = nv3->pfifo.cache1_settings.dma_target_node; //2=pci, 3=agp. - - /* Pagetable information */ - uint32_t tlb_pt_base = nv3->pfifo.cache1_settings.dma_tlb_pt_base; - uint32_t tlb_pt_entry = nv3->pfifo.cache1_settings.dma_tlb_pte; // notify_obj_page - uint32_t tlb_pt_tag = nv3->pfifo.cache1_settings.dma_tlb_tag; // 0xFFFFFFFF usually? - - /* - going to treat the format the same as notifiers - */ - if (!(tlb_pt_entry & NV3_PFIFO_CACHE1_DMA_TLB_PTE_IS_PRESENT)) - { - warning("NV3: Tried to DMA to a non-existent page! Big Problem!"); - return; - } - - uint32_t final_page_base = tlb_pt_entry & 0xFFFFF000; /* pull out 31:12 */ - - /* - page size is 0x1000 - */ - uint32_t final_address = final_page_base + (tlb_pt_entry << 10) + where_to_send; //x86 page size is 0x1000 (maybe rsh where_to_send by 2) - - nv_log_verbose_only("DMA Engine: DMA to %08x length=%08x", final_address, bytes_to_send); - - //-dma_bm_write() - } - - //we're done - nv3->pfifo.cache1_settings.dma_state &= ~NV3_PFIFO_CACHE1_DMA_STATUS_STATE_RUNNING; -} - -void nv3_pfifo_write(uint32_t address, uint32_t val) -{ - // before doing anything, check the subsystem enablement - - if (!(nv3->pmc.enable >> NV3_PMC_ENABLE_PFIFO) - & NV3_PMC_ENABLE_PFIFO_ENABLED) - { - nv_log("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_verbose_only("PFIFO Write 0x%08x -> 0x%08x", val, address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_write) - reg->on_write(val); - 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 &= ~val; - nv3_pmc_clear_interrupts(); - - // update the internal cache error state - if (!nv3->pfifo.interrupt_status & NV3_PFIFO_INTR_CACHE_ERROR) - nv3->pfifo.debug_0 &= ~NV3_PFIFO_INTR_CACHE_ERROR; - break; - case NV3_PFIFO_INTR_EN: - nv3->pfifo.interrupt_enable = val & 0x00011111; - nv3_pmc_handle_interrupts(true); - break; - case NV3_PFIFO_DELAY_0: - nv3->pfifo.dma_delay_retry = val; - break; - case NV3_PFIFO_CONFIG_0: - nv3->pfifo.config_0 = val; - break; - - case NV3_PFIFO_CONFIG_RAMHT: - nv3->pfifo.ramht_config = val; -// This code sucks a bit fix it later -#ifdef ENABLE_NV_LOG - uint32_t new_size_ramht = ((val >> 16) & 0x03); - - if (new_size_ramht == 0) - new_size_ramht = 0x1000; - else if (new_size_ramht == 1) - new_size_ramht = 0x2000; - else if (new_size_ramht == 2) - new_size_ramht = 0x4000; - else if (new_size_ramht == 3) - new_size_ramht = 0x8000; - - nv_log("RAMHT Reconfiguration\n" - "Base Address in RAMIN: %d\n" - "Size: 0x%08x bytes\n", ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12, new_size_ramht); -#endif - break; - case NV3_PFIFO_CONFIG_RAMFC: - nv3->pfifo.ramfc_config = val; - - nv_log("RAMFC Reconfiguration\n" - "Base Address in RAMIN: %d\n", ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9); - break; - case NV3_PFIFO_CONFIG_RAMRO: - nv3->pfifo.ramro_config = val; - - uint32_t new_size_ramro = ((val >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - if (new_size_ramro == 0) - new_size_ramro = 0x200; - else if (new_size_ramro == 1) - new_size_ramro = 0x2000; - - nv_log("RAMRO Reconfiguration\n" - "Base Address in RAMIN: %d\n" - "Size: 0x%08x bytes\n", ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9, new_size_ramro); - break; - case NV3_PFIFO_DEBUG_0: - nv3->pfifo.debug_0 = val; - break; - // Reassignment - case NV3_PFIFO_CACHE_REASSIGNMENT: - nv3->pfifo.cache_reassignment = val & 0x01; //1bit meaningful - break; - // Control - these can trigger pulls - case NV3_PFIFO_CACHE0_PULL0: - nv3->pfifo.cache0_settings.pull0 = val; // 8bits meaningful - - if (nv3->pfifo.cache0_settings.pull0 & (1 >> NV3_PFIFO_CACHE0_PULL0_ENABLED)) - nv3_pfifo_cache0_pull(); - - break; - case NV3_PFIFO_CACHE1_PULL0: - nv3->pfifo.cache1_settings.pull0 = val; // 8bits meaningful - - if (nv3->pfifo.cache1_settings.pull0 & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) - nv3_pfifo_cache1_pull(); - - break; - case NV3_PFIFO_CACHE0_PULLER_CTX_STATE: - nv3->pfifo.cache0_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; - break; - case NV3_PFIFO_CACHE1_PULLER_CTX_STATE: - nv3->pfifo.cache1_settings.context_is_dirty = (val >> NV3_PFIFO_CACHE0_PULLER_CTX_STATE_DIRTY) & 0x01; - break; - case NV3_PFIFO_CACHE0_PUSH_ENABLED: - nv3->pfifo.cache0_settings.push0 = val; - break; - case NV3_PFIFO_CACHE1_PUSH_ENABLED: - nv3->pfifo.cache1_settings.push0 = val; - break; - case NV3_PFIFO_CACHE0_PUSH_CHANNEL_ID: - nv3->pfifo.cache0_settings.channel = val; - break; - case NV3_PFIFO_CACHE1_PUSH_CHANNEL_ID: - nv3->pfifo.cache1_settings.channel = val; - break; - // CACHE0_STATUS and CACHE1_STATUS are not writable - // DMA configuration - case NV3_PFIFO_CACHE1_DMA_CONFIG_0: - nv3->pfifo.cache1_settings.dma_state = val; - break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_1: - nv3->pfifo.cache1_settings.dma_length = val; - break; - case NV3_PFIFO_CACHE1_DMA_CONFIG_2: - nv3->pfifo.cache1_settings.dma_address = val; - break; - case NV3_PFIFO_CACHE1_DMA_STATUS: - nv3->pfifo.cache1_settings.dma_status = val; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_PT_BASE: - nv3->pfifo.cache1_settings.dma_tlb_pt_base = val; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_PTE: - nv3->pfifo.cache1_settings.dma_tlb_pte = val; - break; - case NV3_PFIFO_CACHE1_DMA_TLB_TAG: - nv3->pfifo.cache1_settings.dma_tlb_tag = val; - break; - /* Put and Get addresses */ - case NV3_PFIFO_CACHE0_PUT: - nv3->pfifo.cache0_settings.put_address = val; - break; - case NV3_PFIFO_CACHE0_GET: - nv3->pfifo.cache0_settings.get_address = val; - break; - case NV3_PFIFO_CACHE1_PUT: - nv3->pfifo.cache1_settings.put_address = val; - break; - case NV3_PFIFO_CACHE1_GET: - nv3->pfifo.cache1_settings.get_address = val; - break; - case NV3_PFIFO_RUNOUT_GET: - { - uint32_t size_get = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - if (size_get == 0) //512b - nv3->pfifo.runout_get = val & (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); - else - nv3->pfifo.runout_get = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); - break; - } - case NV3_PFIFO_RUNOUT_PUT: - { - uint32_t size_put = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - if (size_put == 0) //512b - nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); - else - nv3->pfifo.runout_put = val & (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); - - break; - } - /* Cache1 Context is handled below */ - case NV3_PFIFO_CACHE0_CTX: - nv3->pfifo.cache0_settings.context[0] = val; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else if (address >= NV3_PFIFO_CACHE0_METHOD_START && address <= NV3_PFIFO_CACHE0_METHOD_END) - { - nv_log_verbose_only("PFIFO Cache0 Write\n"); - - // 3104 always written after 3100 - if (address & 4) - { - nv_log_verbose_only("Name = 0x%08x\n", val); - nv3->pfifo.cache0_entry.data = val; - nv3_pfifo_cache0_pull(); // immediately pull out - } - else - { - nv3->pfifo.cache0_entry.method = (val & 0x1FFC); - nv3->pfifo.cache0_entry.subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; - nv_log_verbose_only("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache0_entry.subchannel, nv3->pfifo.cache0_entry.method); - } - - } - else if (address >= NV3_PFIFO_CACHE1_METHOD_START && address <= NV3_PFIFO_CACHE1_METHOD_END) - { - // Not sure if REV C changes this. It should... - uint32_t slot = 0; - - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_C00) - slot = (address >> 3) & 0x3F; - else - slot = (address >> 3) & 0x1F; - - uint32_t real_entry = nv3_pfifo_cache1_normal2gray(slot); - - nv_log_verbose_only("Cache1 Write Slot %d (Gray code)", real_entry); - - // See if we want the object name or the channel/subchannel information. - if (address & 4) - { - nv_log_verbose_only("Name = 0x%08x\n", val); - nv3->pfifo.cache1_entries[real_entry].data = val; - } - else - { - nv3->pfifo.cache1_entries[real_entry].method = (val & 0x1FFC); - nv3->pfifo.cache1_entries[real_entry].subchannel = (val >> NV3_PFIFO_CACHE1_METHOD_SUBCHANNEL) & 0x07; - nv_log_verbose_only("Subchannel = 0x%08x, method = 0x%04x\n", nv3->pfifo.cache1_entries[real_entry].subchannel, nv3->pfifo.cache1_entries[real_entry].method); - } - } - /* Handle some special memory areas */ - else if (address >= NV3_PFIFO_CACHE1_CTX_START && address <= NV3_PFIFO_CACHE1_CTX_END) - { - uint32_t ctx_entry_id = ((address - NV3_PFIFO_CACHE1_CTX_START) / 16) % 8; - nv3->pfifo.cache1_settings.context[ctx_entry_id] = val; - - nv_log_verbose_only("PFIFO Cache1 CTX Write Entry=%d value=0x%04x\n", ctx_entry_id, val); - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } - - /* Trigger DMA for notifications if we need to */ - nv3_pfifo_trigger_dma_if_required(); -} - - -/* -https://en.wikipedia.org/wiki/Gray_code -WHY?????? IT'S NOT A TELEGRAPH IT'S A GPU????? - -Convert from a normal number to a total insanity number which is only used in PFIFO CACHE1 for ungodly and totally unknowable reasons -(Possibly it just makes it easier to implement in logic) - -I decided to use a lookup table to save everyone's time, also the numbers generated from the function -that existed here before didn't make any sense -*/ - -#define NV3_GRAY_TABLE_NUM_ENTRIES 64 - -uint8_t nv3_pfifo_cache1_gray_code_table[NV3_GRAY_TABLE_NUM_ENTRIES] = { - 0b000000, 0b000001, 0b000011, 0b000010, 0b000110, 0b000111, 0b000101, 0b000100, //0x07 - 0b001100, 0b001101, 0b001111, 0b001110, 0b001010, 0b001011, 0b001001, 0b001000, //0x0F - 0b011000, 0b011001, 0b011011, 0b011010, 0b011110, 0b011111, 0b011101, 0b011100, //0x17 - 0b010100, 0b010101, 0b010111, 0b010110, 0b010010, 0b010011, 0b010001, 0b010000, //0x1F - 0b110000, 0b110001, 0b110011, 0b110010, 0b110110, 0b110111, 0b110101, 0b110100, //0x27 - 0b111100, 0b111101, 0b111111, 0b111110, 0b111010, 0b111011, 0b111001, 0b111000, //0x2F - 0b101000, 0b101001, 0b101011, 0b101010, 0b101110, 0b101111, 0b101101, 0b101100, //0x37 - 0b100100, 0b100101, 0b100111, 0b100110, 0b100010, 0b100011, 0b100001, 0b100000 //0x3F -}; - -/* The function is called up to hundreds of thousands of times per second, it's too slow to do anything else */ -uint8_t nv3_pfifo_cache1_binary_code_table[NV3_GRAY_TABLE_NUM_ENTRIES] = -{ - 0x00, 0x01, 0x03, 0x02, 0x07, 0x06, 0x04, 0x05, // 0x07 (0) - 0x0F, 0x0E, 0x0C, 0x0D, 0x08, 0x09, 0x0B, 0x0A, // 0x0F (1000) - 0x1F, 0x1E, 0x1C, 0x1D, 0x18, 0x19, 0x1B, 0x1A, // 0x17 (10000) - 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, // 0x1F (11000) - 0x3F, 0x3E, 0x3C, 0x3D, 0x38, 0x39, 0x3B, 0x3A, // 0x27 (100000) - 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, // 0x2F (101000) - 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, // 0x37 (110000) - 0x2F, 0x2E, 0x2C, 0x2D, 0x28, 0x29, 0x2B, 0x2A, // 0X3f (111000) -}; - -uint32_t nv3_pfifo_cache1_normal2gray(uint32_t val) -{ - return nv3_pfifo_cache1_gray_code_table[val]; -} - -/* -Back to sanity -*/ -uint32_t nv3_pfifo_cache1_gray2normal(uint32_t val) -{ - return nv3_pfifo_cache1_binary_code_table[val]; -} - -/* -You can't push into cache0 on the real hardware, but it's not practically done because Cache0 is meant to be reserved for software objects, -NV_USER writes always go to CACHE1 -*/ - -// Pulls graphics objects OUT of cache0 -void nv3_pfifo_cache0_pull(void) -{ - // Do nothing if PFIFO CACHE0 is disabled - if (!nv3->pfifo.cache0_settings.pull0 & (1 >> NV3_PFIFO_CACHE0_PULL0_ENABLED)) - return; - - // Do nothing if there is nothing in cache0 to pull - if (nv3->pfifo.cache0_settings.put_address == nv3->pfifo.cache0_settings.get_address) - return; - - // There is only one entry for cache0 - uint8_t current_channel = nv3->pfifo.cache0_settings.channel; - uint8_t current_subchannel = nv3->pfifo.cache0_entry.subchannel; - uint32_t current_param = nv3->pfifo.cache0_entry.data; - uint16_t current_method = nv3->pfifo.cache0_entry.method; - - // i.e. there is no method in cache0, so we have to find the object. - if (!current_method) - { - // flip the get address over - nv3->pfifo.cache0_settings.get_address ^= 0x04; - - if (!nv3_ramin_find_object(current_param, 0, current_channel, current_subchannel)) - return; // interrupt was fired, and we went to ramro - } - - uint32_t current_context = nv3->pfifo.cache0_settings.context[0]; // only 1 entry for CACHE0 so basically ignore the other context entries? - uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; - - // Tell the CPU if we found a software method and turn off cache pulling - if (!(current_context & 0x800000)) - { - nv_log_verbose_only("The object in CACHE0 is a software object\n"); - - nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; - nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); - return; - } - - // Is this needed? - nv3->pfifo.cache0_settings.get_address ^= 0x04; - - #ifndef RELEASE_BUILD - - nv_log_verbose_only("***** DEBUG: CACHE0 PULLED ****** Contextual information below\n"); - - - nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; - - nv3_debug_ramin_print_context_info(current_param, context_structure); - - nv3_pgraph_submit(current_param, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); - #endif - -} - -void nv3_pfifo_context_switch(uint32_t new_channel) -{ - /* Send our contexts to RAMFC. Load the new ones from RAMFC. */ - if (new_channel >= NV3_DMA_CHANNELS) - fatal("nv3_pfifo_context_switch: Tried to switch to invalid dma channel"); - - //uint16_t ramfc_base = nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS & 0xF; -} - -// NV_USER writes go here! -// Pushes graphics objects into cache1 -void nv3_pfifo_cache1_push(uint32_t addr, uint32_t param) -{ - bool oh_shit = false; // RAMRO needed - nv3_ramin_ramro_reason oh_shit_reason = 0x00; // It's all good for now - - // bit 23 of a ramin dword means it's a write... - uint32_t new_address = 0; - - uint32_t method_offset = (addr & 0x1FFC); // size of dma object is 0x2000 and some universal methods are implemented at this point, like free - - // Up to 128 per envytools? - uint32_t channel = (addr >> NV3_OBJECT_SUBMIT_CHANNEL) & 0x7F; - uint32_t subchannel = (addr >> NV3_OBJECT_SUBMIT_SUBCHANNEL) & (NV3_DMA_CHANNELS - 1); - - // first make sure there is even any cache available - if (!nv3->pfifo.cache1_settings.push0) - { - oh_shit = true; - oh_shit_reason = nv3_runout_reason_no_cache_available; - new_address |= (nv3_runout_reason_no_cache_available << NV3_PFIFO_RUNOUT_RAMIN_ERR); - - } - - // Check if runout is full - if (nv3->pfifo.runout_get != nv3->pfifo.runout_put) - { - oh_shit = true; - oh_shit_reason = nv3_runout_reason_cache_ran_out; // ? really ? I guess this means we already ran out.. - new_address |= (nv3_runout_reason_cache_ran_out << NV3_PFIFO_RUNOUT_RAMIN_ERR); - } - - if (!nv3_pfifo_cache1_num_free_spaces()) - { - oh_shit = true; - oh_shit_reason = nv3_runout_reason_free_count_overrun; - new_address |= (nv3_runout_reason_free_count_overrun << NV3_PFIFO_RUNOUT_RAMIN_ERR); - } - - // 0x0 is used for creating the object. - if (method_offset > 0 && method_offset < 0x100) - { - // Reserved nvidia methods - oh_shit = true; - oh_shit_reason = nv3_runout_reason_reserved_access; - new_address |= (nv3_runout_reason_reserved_access << NV3_PFIFO_RUNOUT_RAMIN_ERR); - - } - - // Now check for context switching - - if (channel != nv3->pfifo.cache1_settings.channel) - { - // Cache reassignment required - if (!nv3->pfifo.cache_reassignment - || (nv3->pfifo.cache1_settings.get_address != nv3->pfifo.cache1_settings.put_address)) - { - oh_shit = true; - oh_shit_reason = nv3_runout_reason_no_cache_available; - new_address |= (nv3_runout_reason_no_cache_available << NV3_PFIFO_RUNOUT_RAMIN_ERR); - } - - nv3_pfifo_context_switch(channel); - } - - // Did we fuck up? - if (oh_shit) - { - nv_log("OH CRAP: Runout Error=%d Channel=%d Subchannel=%d Method=0x%04x", - oh_shit_reason, channel, subchannel, method_offset); - - nv3_ramro_write(nv3->pfifo.runout_put, new_address); - nv3_ramro_write(nv3->pfifo.runout_put + 4, param); - - nv3->pfifo.runout_put += 0x08; - - uint32_t ramro_size = (nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01; - - /* Make sure it's valid */ - switch (ramro_size) - { - case 0: - nv3->pfifo.runout_put &= (NV3_RAMIN_RAMRO_SIZE_0 - 0x07); - break; - case 1: - nv3->pfifo.runout_put &= (NV3_RAMIN_RAMRO_SIZE_1 - 0x07); - break; - } - - //Fire the interrupt. Also the very bad interrupt... - if (nv3->pfifo.runout_get == nv3->pfifo.runout_put) - nv3_pfifo_interrupt(NV3_PFIFO_INTR_RUNOUT_OVERFLOW, true); - else - nv3_pfifo_interrupt(NV3_PFIFO_INTR_RUNOUT, true); - - return; - } - - // We didn't. Let's put it in CACHE1 - uint32_t current_put_index = nv3->pfifo.cache1_settings.put_address >> 2; - nv3->pfifo.cache1_entries[current_put_index].subchannel = subchannel; - nv3->pfifo.cache1_entries[current_put_index].method = method_offset; - nv3->pfifo.cache1_entries[current_put_index].data = param; - - // now we have to recalculate the cache1 put address - uint32_t next_put_address = nv3_pfifo_cache1_gray2normal(current_put_index); - next_put_address++; - - if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX# - next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); - else - next_put_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); - - nv3->pfifo.cache1_settings.put_address = nv3_pfifo_cache1_normal2gray(next_put_address) << 2; - - nv_log_verbose_only("Submitted object [PIO]: Channel %d.%d, Parameter 0x%08x, Method ID 0x%04x (Put Address is now %d)\n", - channel, subchannel, param, method_offset, nv3->pfifo.cache1_settings.put_address); - - // Now we're done. Phew! -} - -// Pulls graphics objects OUT of cache1 -void nv3_pfifo_cache1_pull(void) -{ - // Do nothing if PFIFO CACHE1 is disabled - if (!nv3->pfifo.cache1_settings.pull0 & (1 >> NV3_PFIFO_CACHE1_PULL0_ENABLED)) - return; - - // Do nothing if there is nothing in cache1 to pull - if (nv3->pfifo.cache1_settings.put_address == nv3->pfifo.cache1_settings.get_address) - return; - - uint32_t get_index = nv3->pfifo.cache1_settings.get_address >> 2; // 32 bit aligned probably - - uint8_t current_channel = nv3->pfifo.cache1_settings.channel; - uint8_t current_subchannel = nv3->pfifo.cache1_entries[get_index].subchannel; - uint32_t current_param = nv3->pfifo.cache1_entries[get_index].data; - uint16_t current_method = nv3->pfifo.cache1_entries[get_index].method; - - // NV_ROOT - if (!current_method) - { - if (!nv3_ramin_find_object(current_param, 1, current_channel, current_subchannel)) - return; // interrupt was fired, and we went to ramro - } - - // should this be obtained from the grobj? Test on real nv3 h/w after drawrect.nvp works - uint32_t current_context = nv3->pfifo.cache1_settings.context[current_subchannel]; // get the current subchannel - - uint8_t class_id = ((nv3_ramin_context_t*)¤t_context)->class_id; - - - - // start by incrementing - uint32_t next_get_address = nv3_pfifo_cache1_gray2normal(get_index) + 1; - - if (nv3->nvbase.gpu_revision >= NV3_PCI_CFG_REVISION_C00) // RIVA 128ZX - next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_C - 1); - else - next_get_address &= (NV3_PFIFO_CACHE1_SIZE_REV_AB - 1); - - // Tell the CPU if we found a software method - //bit23 unset=software - //bit23 set=hardware - if (!(current_context & 0x800000)) - { - nv_log_verbose_only("The object in CACHE1 is a software object\n"); - - nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; - nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); - return; - } - - // Is this needed? - nv3->pfifo.cache1_settings.get_address = nv3_pfifo_cache1_normal2gray(next_get_address) << 2; - - #ifndef RELEASE_BUILD - - nv_log_verbose_only("***** DEBUG: CACHE1 PULLED ****** Contextual information below\n"); - - nv3_ramin_context_t context_structure = *(nv3_ramin_context_t*)¤t_context; - - nv3_debug_ramin_print_context_info(current_param, context_structure); - #endif - - nv3_pgraph_submit(current_param, current_method, current_channel, current_subchannel, class_id & 0x1F, context_structure); - - - //Todo: finish it -} - -// THIS IS PER SUBCHANNEL! -uint32_t nv3_pfifo_cache1_num_free_spaces(void) -{ - // get the index - - uint32_t get_index = nv3->pfifo.cache1_settings.get_address >> 2; - uint32_t put_index = nv3->pfifo.cache1_settings.put_address >> 2; - - uint32_t real_get_address = nv3_pfifo_cache1_gray2normal(get_index) << 2; - uint32_t real_put_address = nv3_pfifo_cache1_gray2normal(put_index) << 2; - - // There is no hope of being able to understand it. Nobody can understand - return (real_get_address - real_put_address - 4) & 0x7C; // there are 64 entries what -} \ 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 deleted file mode 100644 index 3ad9fa2a1..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pgraph.c +++ /dev/null @@ -1,632 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -// Initialise the PGRAPH subsystem. -void nv3_pgraph_init(void) -{ - nv_log("Initialising PGRAPH..."); - // Set up the vblank interrupt - nv3->nvbase.svga.vblank_start = nv3_pgraph_vblank_start; - nv_log("Done!\n"); -} - -// -// ****** PGRAPH register list START ****** -// - -nv_register_t pgraph_registers[] = { - { NV3_PGRAPH_DEBUG_0, "PGRAPH Debug 0", NULL, NULL }, - { NV3_PGRAPH_DEBUG_1, "PGRAPH Debug 1", NULL, NULL }, - { NV3_PGRAPH_DEBUG_2, "PGRAPH Debug 2", NULL, NULL }, - { NV3_PGRAPH_DEBUG_3, "PGRAPH Debug 3", NULL, NULL }, - { 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 }, - { NV3_PGRAPH_CTX_SWITCH, "PGRAPH DMA Context Switch", NULL, NULL }, - { NV3_PGRAPH_CONTEXT_CONTROL, "PGRAPH DMA Context Control", NULL, NULL }, - { NV3_PGRAPH_CONTEXT_USER, "PGRAPH DMA Context User", NULL, NULL }, - //{ NV3_PGRAPH_CONTEXT_CACHE(0), "PGRAPH DMA Context Cache", NULL, NULL }, - { NV3_PGRAPH_ABS_UCLIP_XMIN, "PGRAPH Absolute Clip Minimum X [17:0]", NULL, NULL }, - { NV3_PGRAPH_ABS_UCLIP_XMAX, "PGRAPH Absolute Clip Maximum X [17:0]", NULL, NULL }, - { NV3_PGRAPH_ABS_UCLIP_YMIN, "PGRAPH Absolute Clip Minimum Y [17:0]", NULL, NULL }, - { NV3_PGRAPH_ABS_UCLIP_YMAX, "PGRAPH Absolute Clip Maximum Y [17:0]", NULL, NULL }, - { NV3_PGRAPH_SRC_CANVAS_MIN, "PGRAPH Source Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_SRC_CANVAS_MAX, "PGRAPH Source Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_DST_CANVAS_MIN, "PGRAPH Destination Canvas Minimum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_DST_CANVAS_MAX, "PGRAPH Destination Canvas Maximum Coordinates (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_PATTERN_COLOR_0_RGB, "PGRAPH Pattern Color 0_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_0_ALPHA, "PGRAPH Pattern Color 0_1 (Bits 7:0 = Alpha)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_1_RGB, "PGRAPH Pattern Color 1_0 (Bits 29:20 = Red, Bits 19:10 = Green, Bits 9:0 = Blue)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_COLOR_1_ALPHA, "PGRAPH Pattern Color 1_1 (Bits 7:0 = Alpha)", NULL, NULL, }, - { NV3_PGRAPH_PATTERN_BITMAP_HIGH, "PGRAPH Pattern Bitmap (High 32bits)", NULL, NULL}, - { NV3_PGRAPH_PATTERN_BITMAP_LOW, "PGRAPH Pattern Bitmap (Low 32bits)", NULL, NULL}, - { NV3_PGRAPH_PATTERN_SHAPE, "PGRAPH Pattern Shape (1:0 - 0=8x8, 1=64x1, 2=1x64)", NULL, NULL}, - { NV3_PGRAPH_ROP3, "PGRAPH GDI Ternary Render Operation ROP3 (2^3 bits = 256 possible operations)", NULL, NULL}, - { NV3_PGRAPH_PLANE_MASK, "PGRAPH Current Plane Mask (7:0)", NULL, NULL}, - { NV3_PGRAPH_CHROMA_KEY, "PGRAPH Chroma Key (17:0) (Bit 30 = Alpha, 29:20 = Red, 19:10 = Green, 9:0 = Blue)", NULL, NULL}, - { NV3_PGRAPH_BETA, "PGRAPH Beta factor", NULL, NULL }, - { NV3_PGRAPH_DMA, "PGRAPH DMA", NULL, NULL }, - { NV3_PGRAPH_CLIP_MISC, "PGRAPH Clipping Miscellaneous Settings", NULL, NULL }, - { NV3_PGRAPH_NOTIFY, "PGRAPH Notifier (Wip...)", NULL, NULL }, - { NV3_PGRAPH_CLIP0_MIN, "PGRAPH Clip0 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_CLIP0_MAX, "PGRAPH Clip0 Max (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_CLIP1_MIN, "PGRAPH Clip1 Min (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_CLIP1_MAX, "PGRAPH Clip1 Max (Bits 30:16 = Y, Bits 10:0 = X)", NULL, NULL}, - { NV3_PGRAPH_FIFO_ACCESS, "PGRAPH - Can we access PFIFO?", NULL, NULL, }, - { NV3_PGRAPH_STATUS, "PGRAPH Status", NULL, NULL }, - { NV3_PGRAPH_TRAPPED_ADDRESS, "PGRAPH Trapped Address", NULL, NULL }, - { NV3_PGRAPH_TRAPPED_DATA, "PGRAPH Trapped Data", NULL, NULL }, - { NV3_PGRAPH_INSTANCE, "PGRAPH Object Instance", NULL, NULL}, - { NV3_PGRAPH_TRAPPED_INSTANCE, "PGRAPH Trapped Object Instance", NULL, NULL }, - { NV3_PGRAPH_DMA_INTR_0, "PGRAPH DMA Interrupt Status (unimplemented)", NULL, NULL }, - { NV3_PGRAPH_DMA_INTR_EN_0, "PGRAPH DMA Interrupt Enable (unimplemented)", 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("Repressing PGRAPH read. The subsystem is disabled according to pmc_enable, returning 0\n"); - return 0x00; - } - - uint32_t ret = 0x00; - - nv_register_t* reg = nv_get_register(address, pgraph_registers, sizeof(pgraph_registers)/sizeof(pgraph_registers[0])); - - // todo: friendly logging - - nv_log_verbose_only("PGRAPH Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - switch (reg->address) - { - case NV3_PGRAPH_DEBUG_0: - ret = nv3->pgraph.debug_0; - break; - case NV3_PGRAPH_DEBUG_1: - ret = nv3->pgraph.debug_1; - break; - case NV3_PGRAPH_DEBUG_2: - ret = nv3->pgraph.debug_2; - break; - case NV3_PGRAPH_DEBUG_3: - ret = nv3->pgraph.debug_3; - break; - //interrupt status and enable regs - case NV3_PGRAPH_INTR_0: - ret = nv3->pgraph.interrupt_status_0; - nv3_pmc_clear_interrupts(); - break; - case NV3_PGRAPH_INTR_1: - ret = nv3->pgraph.interrupt_status_1; - nv3_pmc_clear_interrupts(); - break; - case NV3_PGRAPH_INTR_EN_0: - ret = nv3->pgraph.interrupt_enable_0; - nv3_pmc_handle_interrupts(true); - break; - case NV3_PGRAPH_INTR_EN_1: - ret = nv3->pgraph.interrupt_enable_1; - nv3_pmc_handle_interrupts(true); - break; - // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like - // during the driver initialisation process - - // In the future, these will most likely have their own functions... - - // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) - case NV3_PGRAPH_CTX_SWITCH: - ret = nv3->pgraph.context_switch; - break; - case NV3_PGRAPH_CONTEXT_CONTROL: - ret = *(uint32_t*)&nv3->pgraph.context_control; - break; - case NV3_PGRAPH_CONTEXT_USER: - ret = *(uint32_t*)&nv3->pgraph.context_user; - break; - // Clip - case NV3_PGRAPH_ABS_UCLIP_XMIN: - ret = nv3->pgraph.abs_uclip_xmin; - break; - case NV3_PGRAPH_ABS_UCLIP_XMAX: - ret = nv3->pgraph.abs_uclip_xmax; - break; - case NV3_PGRAPH_ABS_UCLIP_YMIN: - ret = nv3->pgraph.abs_uclip_ymin; - break; - case NV3_PGRAPH_ABS_UCLIP_YMAX: - ret = nv3->pgraph.abs_uclip_ymax; - break; - // Canvas - case NV3_PGRAPH_SRC_CANVAS_MIN: - ret = *(uint32_t*)&nv3->pgraph.src_canvas_min; - break; - case NV3_PGRAPH_SRC_CANVAS_MAX: - ret = *(uint32_t*)&nv3->pgraph.src_canvas_max; - break; - // Pattern - case NV3_PGRAPH_PATTERN_COLOR_0_RGB: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb; - break; - case NV3_PGRAPH_PATTERN_COLOR_0_ALPHA: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha; - break; - case NV3_PGRAPH_PATTERN_COLOR_1_RGB: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb; - break; - case NV3_PGRAPH_PATTERN_COLOR_1_ALPHA: - ret = *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha; - break; - case NV3_PGRAPH_PATTERN_BITMAP_HIGH: - ret = (nv3->pgraph.pattern_bitmap >> 32) & 0xFFFFFFFF; - break; - case NV3_PGRAPH_PATTERN_BITMAP_LOW: - ret = (nv3->pgraph.pattern_bitmap & 0xFFFFFFFF); - break; - // Beta factor - case NV3_PGRAPH_BETA: - ret = nv3->pgraph.beta_factor; - break; - // Todo: Massive table of ROP IDs or at least known ones? - case NV3_PGRAPH_ROP3: - ret = nv3->pgraph.rop; - break; - case NV3_PGRAPH_CHROMA_KEY: - ret = *(uint32_t*)&nv3->pgraph.chroma_key; - break; - case NV3_PGRAPH_PLANE_MASK: - ret = nv3->pgraph.plane_mask; - break; - // DMA - case NV3_PGRAPH_DMA: - ret = *(uint32_t*)&nv3->pgraph.dma_settings; - break; - case NV3_PGRAPH_NOTIFY: - ret = *(uint32_t*)&nv3->pgraph.notifier; - break; - // More clip - case NV3_PGRAPH_CLIP0_MIN: - ret = *(uint32_t*)&nv3->pgraph.clip0_min; - break; - case NV3_PGRAPH_CLIP0_MAX: - ret = *(uint32_t*)&nv3->pgraph.clip0_max; - break; - case NV3_PGRAPH_CLIP1_MIN: - ret = *(uint32_t*)&nv3->pgraph.clip1_min; - break; - case NV3_PGRAPH_CLIP1_MAX: - ret = *(uint32_t*)&nv3->pgraph.clip1_max; - break; - case NV3_PGRAPH_CLIP_MISC: - ret = *(uint32_t*)&nv3->pgraph.clip_misc_settings; - break; - - // Overall Status - case NV3_PGRAPH_STATUS: - ret = *(uint32_t*)&nv3->pgraph.status; - break; - // Trapped Address - case NV3_PGRAPH_TRAPPED_ADDRESS: - ret = nv3->pgraph.trapped_address; - break; - case NV3_PGRAPH_TRAPPED_DATA: - ret = nv3->pgraph.trapped_data; - break; - case NV3_PGRAPH_INSTANCE: - ret = nv3->pgraph.instance; - break; - case NV3_PGRAPH_TRAPPED_INSTANCE: - ret = nv3->pgraph.trapped_instance; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - /* Special exception for memory areas */ - if (address >= NV3_PGRAPH_CONTEXT_CACHE(0) - && address <= NV3_PGRAPH_CONTEXT_CACHE(NV3_PGRAPH_CONTEXT_CACHE_SIZE)) - { - // Addresses should be aligned to 4 bytes. - uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)); - - nv_log_verbose_only("PGRAPH Context Cache Read (Entry=%04x Value=%04x)\n", entry, nv3->pgraph.context_cache[entry]); - } - else /* Completely unknown */ - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - } - - return ret; -} - -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("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_verbose_only("PGRAPH Write 0x%08x -> 0x%08x\n", value, address); - - // if the register actually exists - if (reg) - { - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - case NV3_PGRAPH_DEBUG_0: - nv3->pgraph.debug_0 = value; - break; - case NV3_PGRAPH_DEBUG_1: - nv3->pgraph.debug_1 = value; - break; - case NV3_PGRAPH_DEBUG_2: - nv3->pgraph.debug_2 = value; - break; - case NV3_PGRAPH_DEBUG_3: - nv3->pgraph.debug_3 = value; - break; - //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; - nv3_pmc_handle_interrupts(true); - break; - case NV3_PGRAPH_INTR_EN_1: - nv3->pgraph.interrupt_enable_1 = value & 0x00011111; - nv3_pmc_handle_interrupts(true); - break; - case NV3_PGRAPH_DMA_INTR_0: - nv3->pgraph.interrupt_status_dma &= ~value; - nv3_pmc_clear_interrupts(); - break; - case NV3_PGRAPH_DMA_INTR_EN_0: - nv3->pgraph.interrupt_enable_dma = value & 0x000111111; - nv_log("Handling PGRAPH_DMA interrupts not implemented"); - nv3_pmc_handle_interrupts(true); - break; - // A lot of this is currently a temporary implementation so that we can just debug what the current state looks like - // during the driver initialisation process - - // In the future, these will most likely have their own functions... - - // Context Swithcing (THIS IS CONTROLLED BY PFIFO!) - case NV3_PGRAPH_CTX_SWITCH: - nv3->pgraph.context_switch = value; - break; - case NV3_PGRAPH_CONTEXT_CONTROL: - *(uint32_t*)&nv3->pgraph.context_control = value; - break; - case NV3_PGRAPH_CONTEXT_USER: - *(uint32_t*)&nv3->pgraph.context_user = value; - break; - // Clip - case NV3_PGRAPH_ABS_UCLIP_XMIN: - nv3->pgraph.abs_uclip_xmin = value; - break; - case NV3_PGRAPH_ABS_UCLIP_XMAX: - nv3->pgraph.abs_uclip_xmax = value; - break; - case NV3_PGRAPH_ABS_UCLIP_YMIN: - nv3->pgraph.abs_uclip_ymin = value; - break; - case NV3_PGRAPH_ABS_UCLIP_YMAX: - nv3->pgraph.abs_uclip_ymax = value; - break; - // Canvas - case NV3_PGRAPH_SRC_CANVAS_MIN: - *(uint32_t*)&nv3->pgraph.src_canvas_min = value; - break; - case NV3_PGRAPH_SRC_CANVAS_MAX: - *(uint32_t*)&nv3->pgraph.src_canvas_max = value; - break; - // Pattern - case NV3_PGRAPH_PATTERN_COLOR_0_RGB: - *(uint32_t*)&nv3->pgraph.pattern_color_0_rgb = value; - break; - case NV3_PGRAPH_PATTERN_COLOR_0_ALPHA: - *(uint32_t*)&nv3->pgraph.pattern_color_0_alpha = value; - break; - case NV3_PGRAPH_PATTERN_COLOR_1_RGB: - *(uint32_t*)&nv3->pgraph.pattern_color_1_rgb = value; - break; - case NV3_PGRAPH_PATTERN_COLOR_1_ALPHA: - *(uint32_t*)&nv3->pgraph.pattern_color_1_alpha = value; - break; - case NV3_PGRAPH_PATTERN_BITMAP_HIGH: - nv3->pgraph.pattern_bitmap |= ((uint64_t)value << 32); - break; - case NV3_PGRAPH_PATTERN_BITMAP_LOW: - nv3->pgraph.pattern_bitmap |= value; - break; - // Beta factor - case NV3_PGRAPH_BETA: - nv3->pgraph.beta_factor = value; - break; - // Todo: Massive table of ROP IDs or at least known ones? - case NV3_PGRAPH_ROP3: - nv3->pgraph.rop = value & 0xFF; - break; - case NV3_PGRAPH_CHROMA_KEY: - nv3->pgraph.chroma_key = value; - break; - case NV3_PGRAPH_PLANE_MASK: - nv3->pgraph.plane_mask = value; - break; - // DMA - case NV3_PGRAPH_DMA: - *(uint32_t*)&nv3->pgraph.dma_settings = value; - break; - case NV3_PGRAPH_NOTIFY: - *(uint32_t*)&nv3->pgraph.notifier = value; - break; - // More clip - case NV3_PGRAPH_CLIP0_MIN: - *(uint32_t*)&nv3->pgraph.clip0_min = value; - break; - case NV3_PGRAPH_CLIP0_MAX: - *(uint32_t*)&nv3->pgraph.clip0_max = value; - break; - case NV3_PGRAPH_CLIP1_MIN: - *(uint32_t*)&nv3->pgraph.clip1_min = value; - break; - case NV3_PGRAPH_CLIP1_MAX: - *(uint32_t*)&nv3->pgraph.clip1_max = value; - break; - case NV3_PGRAPH_CLIP_MISC: - *(uint32_t*)&nv3->pgraph.clip_misc_settings = value; - break; - // Overall Status - case NV3_PGRAPH_STATUS: - *(uint32_t*)&nv3->pgraph.status = value; - break; - // Trapped Address - case NV3_PGRAPH_TRAPPED_ADDRESS: - nv3->pgraph.trapped_address = value; - break; - case NV3_PGRAPH_TRAPPED_DATA: - nv3->pgraph.trapped_data = value; - break; - case NV3_PGRAPH_INSTANCE: - nv3->pgraph.instance = value; - break; - case NV3_PGRAPH_TRAPPED_INSTANCE: - nv3->pgraph.trapped_instance = value; - break; - - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - } - else - { - /* Special exception for memory areas */ - if (address >= NV3_PGRAPH_CONTEXT_CACHE(0) - && address <= NV3_PGRAPH_CONTEXT_CACHE(NV3_PGRAPH_CONTEXT_CACHE_SIZE)) - { - // Addresses should be aligned to 4 bytes. - uint32_t entry = (address - NV3_PGRAPH_CONTEXT_CACHE(0)) >> 2; - - nv_log_verbose_only("PGRAPH Context Cache Write (Entry=%04x Value=0x%08x)\n", entry, value); - nv3->pgraph.context_cache[entry] = value; - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } - } -} - -// Fire a VALID Pgraph interrupt: num is the bit# of the interrupt in the GPU subsystem INTR_EN register. -void nv3_pgraph_interrupt_valid(uint32_t num) -{ - nv3->pgraph.interrupt_status_0 |= (1 << num); - nv3_pmc_handle_interrupts(true); -} - -// Fire an INVALID pgraph interrupt -void nv3_pgraph_interrupt_invalid(uint32_t num) -{ - nv3->pgraph.interrupt_status_1 |= (1 << num); - - // Some code in pcbox hat enables the "reserved" bit HERE if it's set in intr 0. What??? - nv3_pmc_handle_interrupts(true); -} - -// VBlank. Fired every single frame. -void nv3_pgraph_vblank_start(svga_t* svga) -{ - nv3_pgraph_interrupt_valid(NV3_PGRAPH_INTR_0_VBLANK); -} - -/* Sends off method execution to the right class */ -void nv3_pgraph_arbitrate_method(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) -{ - /* Obtain the grobj information from the context in ramin */ - nv3_grobj_t grobj = {0}; - - // we need to shift left by 4 to get the real address, something to do with the 16 byte unit of reversal - uint32_t real_ramin_base = context.ramin_offset << 4; - - // readin our grobj - grobj.grobj_0 = nv3_ramin_read32(real_ramin_base, nv3); - grobj.grobj_1 = nv3_ramin_read32(real_ramin_base + 4, nv3); - grobj.grobj_2 = nv3_ramin_read32(real_ramin_base + 8, nv3); - grobj.grobj_3 = nv3_ramin_read32(real_ramin_base + 12, nv3); - - nv_log_verbose_only("**** About to execute method **** method=0x%04x param=0x%08x, channel=%d.%d, class=%s, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n", - method, param, channel, subchannel, nv3_class_names[class_id], grobj.grobj_0, grobj.grobj_1, grobj.grobj_2, grobj.grobj_3); - - /* Methods below 0x104 are shared across all classids, so call generic_method for that*/ - if (method <= NV3_SET_NOTIFY) - { - nv3_generic_method(param, method, context, grobj); - } - else - { - // By this point, we already ANDed the class ID to 0x1F. - // Send the grobj, the context, the method and the name off to actually be acted upon. - switch (class_id) - { - case nv3_pgraph_class01_beta_factor: - nv3_class_001_method(param, method, context, grobj); - break; - case nv3_pgraph_class02_rop: - nv3_class_002_method(param, method, context, grobj); - break; - case nv3_pgraph_class03_chroma_key: - nv3_class_003_method(param, method, context, grobj); - break; - case nv3_pgraph_class04_plane_mask: - nv3_class_004_method(param, method, context, grobj); - break; - case nv3_pgraph_class05_clipping_rectangle: - nv3_class_005_method(param, method, context, grobj); - break; - case nv3_pgraph_class06_pattern: - nv3_class_006_method(param, method, context, grobj); - break; - case nv3_pgraph_class07_rectangle: - nv3_class_007_method(param, method, context, grobj); - break; - case nv3_pgraph_class08_point: - nv3_class_008_method(param, method, context, grobj); - break; - case nv3_pgraph_class09_line: - nv3_class_009_method(param, method, context, grobj); - break; - case nv3_pgraph_class0a_lin: - nv3_class_00a_method(param, method, context, grobj); - break; - case nv3_pgraph_class0b_triangle: - nv3_class_00b_method(param, method, context, grobj); - break; - case nv3_pgraph_class0c_w95txt: - nv3_class_00c_method(param, method, context, grobj); - break; - case nv3_pgraph_class0d_m2mf: - nv3_class_00d_method(param, method, context, grobj); - break; - case nv3_pgraph_class0e_scaled_image_from_memory: - nv3_class_00e_method(param, method, context, grobj); - break; - case nv3_pgraph_class10_blit: - nv3_class_010_method(param, method, context, grobj); - break; - case nv3_pgraph_class11_image: - nv3_class_011_method(param, method, context, grobj); - break; - case nv3_pgraph_class12_bitmap: - nv3_class_012_method(param, method, context, grobj); - break; - case nv3_pgraph_class14_transfer2memory: - nv3_class_014_method(param, method, context, grobj); - break; - case nv3_pgraph_class15_stretched_image_from_cpu: - nv3_class_015_method(param, method, context, grobj); - break; - case nv3_pgraph_class17_d3d5tri_zeta_buffer: - nv3_class_017_method(param, method, context, grobj); - break; - case nv3_pgraph_class18_point_zeta_buffer: - nv3_class_018_method(param, method, context, grobj); - break; - case nv3_pgraph_class1c_image_in_memory: - nv3_class_01c_method(param, method, context, grobj); - break; - default: - fatal("NV3 (nv3_pgraph_arbitrate_method): Attempted to execute method on invalid, or unimplemented, class ID %s", nv3_class_names[class_id]); - return; - } - } - - nv3_notify_if_needed(param, method, context, grobj); -} - -/* Arbitrates graphics object submission to the right object types */ -void nv3_pgraph_submit(uint32_t param, uint16_t method, uint8_t channel, uint8_t subchannel, uint8_t class_id, nv3_ramin_context_t context) -{ - // class id can be derived from the context but we debug log it before we get here - // Do we need to read grobj here? - - switch (method) - { - default: - // Object Method arbitration - nv3_pgraph_arbitrate_method(param, method, channel, subchannel, class_id, context); - 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 deleted file mode 100644 index b6528fe0a..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pmc.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -void nv3_pmc_init(void) -{ - nv_log("Initialising PMC....\n"); - - if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_A00) - nv3->pmc.boot = NV3_BOOT_REG_REV_A00; - else if (nv3->nvbase.gpu_revision == NV3_PCI_CFG_REVISION_B00) - nv3->pmc.boot = NV3_BOOT_REG_REV_B00; - else - nv3->pmc.boot = NV3_BOOT_REG_REV_C00; - - nv3->pmc.interrupt_enable = NV3_PMC_INTERRUPT_ENABLE_HARDWARE | NV3_PMC_INTERRUPT_ENABLE_SOFTWARE; - - nv_log("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 -}; - -void nv3_pmc_clear_interrupts(void) -{ - nv_log_verbose_only("Clearing IRQs\n"); - pci_clear_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 = 0x00; - - // 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... - if (nv3->pgraph.interrupt_status_0 & (1 << 8) - && nv3->pgraph.interrupt_enable_0 & (1 << 8)) - new_intr_value |= (NV3_PMC_INTERRUPT_PFB_PENDING << NV3_PMC_INTERRUPT_PFB); - - if (nv3->pgraph.interrupt_status_0 & ~(1 << 8) - && nv3->pgraph.interrupt_enable_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(); - return nv3->pmc.interrupt_status; - } - - - // if we actually need to send the interrupt (i.e. this is a write) send it now - if (send_now) - { - // no interrupts to send - if (!(nv3->pmc.interrupt_status) - || !(nv3->pmc.interrupt_status - 0x80000000)) - { - nv3_pmc_clear_interrupts(); - return nv3->pmc.interrupt_status; - } - - if ((nv3->pmc.interrupt_status & 0x7FFFFFFF)) - { - if (nv3->pmc.interrupt_enable & NV3_PMC_INTERRUPT_ENABLE_HARDWARE) - { - nv_log_verbose_only("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_verbose_only("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_verbose_only("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_verbose_only("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])); - - uint32_t ret = 0x00; - - // todo: friendly logging - nv_log_verbose_only("PMC Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - switch (reg->address) - { - case NV3_PMC_BOOT: - ret = nv3->pmc.boot; - break; - case NV3_PMC_INTERRUPT_STATUS: - nv_log_verbose_only("\n"); // clear_interrupts logs - nv3_pmc_clear_interrupts(); - - ret = nv3_pmc_handle_interrupts(false); - break; - case NV3_PMC_INTERRUPT_ENABLE: - //TODO: ACTUALLY CHANGE THE INTERRUPT STATE - ret = nv3->pmc.interrupt_enable; - break; - case NV3_PMC_ENABLE: - ret = nv3->pmc.enable; - break; - - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PMC Write 0x%08x -> 0x%08x", value, address); - - // if the register actually exists... - if (reg) - { - - // ... 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 can only be done by software interrupts... - if (!(nv3->pmc.interrupt_status & 0x7FFFFFFF)) - { - warning("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 & 0x03; - nv3_pmc_handle_interrupts(value != 0); - break; - case NV3_PMC_ENABLE: - nv3->pmc.enable = value; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} \ 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 deleted file mode 100644 index 1f0da2aac..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pme.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_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(void) -{ - nv_log("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])); - - uint32_t ret = 0x00; - - // todo: friendly logging - - nv_log_verbose_only("PME Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - // Interrupt state: - // Bit 0 - Image Notifier - // Bit 4 - Vertical Blank Interval Notifier - // Bit 8 - Video Notifier - // Bit 12 - Audio Notifier - // Bit 16 - VMI Notifer - switch (reg->address) - { - case NV3_PME_INTR: - ret = nv3->pme.interrupt_status; - break; - case NV3_PME_INTR_EN: - ret = nv3->pme.interrupt_enable; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PME Write 0x%08x -> 0x%08x\n", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - // Interrupt state: - // Bit 0 - 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; - } - } - - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } - -} \ 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 deleted file mode 100644 index 9ce5a189a..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pramdac.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * 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-2025 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(void) -{ - nv_log("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("Initialising PRAMDAC: Done\n"); -} - -// Polls the pixel clock. -void nv3_pramdac_pixel_clock_poll(double real_time) -{ - /* Ignore in VGA mode */ - if (!nv3->nvbase.svga.override) - return; - - /* Figure out our refresh time. */ - if (!nv3->nvbase.refresh_time) - nv3->nvbase.refresh_time = (1/60.0); // rivatimers count in microseconds but present the info as seconds - - nv3->nvbase.refresh_clock += real_time; - - if (nv3->nvbase.refresh_clock > nv3->nvbase.refresh_time) - { - /* Update the screen because something changed */ - nv3_render_current_bpp(); - video_blit_memtoscreen(0, 0, xsize, ysize); - nv3->nvbase.refresh_clock = 0; - } - - // TODO: ???? -} - -// Polls the memory clock. -// This updates the 2D/3D engine PGRAPH, PTIMER and more -void nv3_pramdac_memory_clock_poll(double real_time) -{ - nv3_ptimer_tick(real_time); - - nv3_pfifo_cache0_pull(); - nv3_pfifo_cache1_pull(); - // TODO: UPDATE PGRAPH! -} - -// Gets the vram clock register. -uint32_t nv3_pramdac_get_vram_clock_register(void) -{ - // the clock format is packed into 19 bits - // M divisor [7-0] - // N divisor [16-8] - // P divisor [18-16] - return (nv3->pramdac.memory_clock_m) - + (nv3->pramdac.memory_clock_n << 8) - + (nv3->pramdac.memory_clock_p << 16); // 0-3 -} - -uint32_t nv3_pramdac_get_pixel_clock_register(void) -{ - return (nv3->pramdac.pixel_clock_m) - + (nv3->pramdac.pixel_clock_n << 8) - + (nv3->pramdac.pixel_clock_p << 16); // 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(void) -{ - // 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; - - if (nv3->pramdac.memory_clock_n == 0) - nv3->pramdac.memory_clock_n = 1; - - // Convert to microseconds - frequency = (frequency * nv3->pramdac.memory_clock_n) / (nv3->pramdac.memory_clock_m << nv3->pramdac.memory_clock_p); - - double time = 1000000.0 / (double)frequency; // needs to be a double for 86box - - nv_log("Memory clock = %.2f MHz\n", frequency / 1000000.0f); - - nv3->nvbase.memory_clock_frequency = frequency; - - // Create and start if it it's not running. - if (!nv3->nvbase.memory_clock_timer) - { - nv3->nvbase.memory_clock_timer = rivatimer_create(time, nv3_pramdac_memory_clock_poll); - rivatimer_start(nv3->nvbase.memory_clock_timer); - } - - rivatimer_set_period(nv3->nvbase.memory_clock_timer, time); -} - -void nv3_pramdac_set_pixel_clock(void) -{ - // frequency divider algorithm from old varcem/86box/pcbox riva driver, - // verified by reversing NT drivers v1.50e CalcMNP [symbols] function - - // 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; - - if (nv3->pramdac.memory_clock_n == 0) - nv3->pramdac.memory_clock_n = 1; - - frequency = (frequency * nv3->pramdac.pixel_clock_n) / (nv3->pramdac.pixel_clock_m << nv3->pramdac.pixel_clock_p); - - nv3->nvbase.svga.clock = cpuclock / frequency; - - double time = 1000000.0 / (double)frequency; // needs to be a double for 86box - - nv_log("Pixel clock = %.2f MHz\n", frequency / 1000000.0f); - - nv3->nvbase.pixel_clock_frequency = frequency; - - // Create and start if it it's not running. - if (!nv3->nvbase.pixel_clock_timer) - { - nv3->nvbase.pixel_clock_timer = rivatimer_create(time, nv3_pramdac_pixel_clock_poll); - rivatimer_start(nv3->nvbase.pixel_clock_timer); - } - - rivatimer_set_period(nv3->nvbase.pixel_clock_timer, time); -} - -// -// ****** PRAMDAC register list START ****** -// - -// NULL means handle in read functions -nv_register_t pramdac_registers[] = -{ - { NV3_PRAMDAC_CURSOR_START, "PRAMDAC - Cursor Start Position"}, - { 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_START, "PRAMDAC - VBBlank Start", NULL, NULL}, - { NV3_PRAMDAC_VBBLANK_END, "PRAMDAC - VBBlank End", NULL, NULL}, - { NV3_PRAMDAC_HBLANK_END, "PRAMDAC - Horizontal Blanking Interval End", NULL, NULL}, - { NV3_PRAMDAC_HBLANK_START, "PRAMDAC - Horizontal Blanking Interval Start", 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}, - { NV3_USER_DAC_PIXEL_MASK, "PRAMDAC - User DAC Pixel Mask", NULL, NULL}, - { NV3_USER_DAC_READ_MODE_ADDRESS, "PRAMDAC - User DAC Read Mode Address", NULL, NULL}, - { NV3_USER_DAC_WRITE_MODE_ADDRESS, "PRAMDAC - User DAC Write Mode Address", NULL, NULL}, - { NV3_USER_DAC_PALETTE_DATA, "PRAMDAC - User DAC Palette Data", 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])); - - uint32_t ret = 0x00; - - // todo: friendly logging - - nv_log_verbose_only("PRAMDAC Read from 0x%08x\n", address); - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - //s hould be pretty easy to understand - switch (reg->address) - { - case NV3_PRAMDAC_COEFF_SELECT: - ret = nv3->pramdac.coeff_select; - break; - case NV3_PRAMDAC_GENERAL_CONTROL: - ret = nv3->pramdac.general_control; - break; - case NV3_PRAMDAC_VSERR_WIDTH: - ret = nv3->pramdac.vserr_width; - break; - case NV3_PRAMDAC_VBBLANK_END: - ret = nv3->pramdac.vbblank_end; - break; - case NV3_PRAMDAC_VBLANK_END: - ret = nv3->pramdac.vblank_end; - break; - case NV3_PRAMDAC_VBLANK_START: - ret = nv3->pramdac.vblank_start; - break; - case NV3_PRAMDAC_VEQU_START: - ret = nv3->pramdac.vequ_start; - break; - case NV3_PRAMDAC_VTOTAL: - ret = nv3->pramdac.vtotal; - break; - case NV3_PRAMDAC_HSYNC_WIDTH: - ret = nv3->pramdac.hsync_width; - break; - case NV3_PRAMDAC_HBURST_START: - ret = nv3->pramdac.hburst_start; - break; - case NV3_PRAMDAC_HBURST_END: - ret = nv3->pramdac.hburst_end; - break; - case NV3_PRAMDAC_HBLANK_START: - ret = nv3->pramdac.hblank_start; - break; - case NV3_PRAMDAC_HBLANK_END: - ret = nv3->pramdac.hblank_end; - break; - case NV3_PRAMDAC_HTOTAL: - ret = nv3->pramdac.htotal; - break; - case NV3_PRAMDAC_HEQU_WIDTH: - ret = nv3->pramdac.hequ_width; - break; - case NV3_PRAMDAC_HSERR_WIDTH: - ret = nv3->pramdac.hserr_width; - break; - case NV3_USER_DAC_PIXEL_MASK: - ret = nv3->pramdac.user_pixel_mask; - break; - case NV3_USER_DAC_READ_MODE_ADDRESS: - ret = nv3->pramdac.user_read_mode_address; - break; - case NV3_USER_DAC_WRITE_MODE_ADDRESS: - ret = nv3->pramdac.user_write_mode_address; - break; - case NV3_USER_DAC_PALETTE_DATA: - /* I doubt NV actually read this in their drivers, but it's worth doing anyway */ - /* Bit 1 is listed as "read or write mode" and 7:0 as "Write-only address", but NV only ever set this to 0 too, so i think this should be fine for now */ - ret = nv3->pramdac.palette[nv3->pramdac.user_read_mode_address]; - nv3->pramdac.user_read_mode_address++; - break; - case NV3_PRAMDAC_CURSOR_START: - ret = (nv3->pramdac.cursor_start.y << 16) | nv3->pramdac.cursor_start.x; - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PRAMDAC Write 0x%08x -> 0x%08x\n", value, address); - - // if the register actually exists - if (reg) - { - // 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; - nv3_recalc_timings(&nv3->nvbase.svga); - 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; - case NV3_USER_DAC_PIXEL_MASK: - nv3->pramdac.user_pixel_mask = value; - break; - case NV3_USER_DAC_READ_MODE_ADDRESS: - nv3->pramdac.user_read_mode_address = value; - break; - case NV3_USER_DAC_WRITE_MODE_ADDRESS: - /* - This seems to get reset to 0 after 256 writes, but, the palette is 768 bytes in size. - Clearly there's some mechanism here, but I'm not sure what it is. So let's just reset if we reach 768. - */ - if (nv3->pramdac.user_write_mode_address >= NV3_USER_DAC_PALETTE_SIZE) - nv3->pramdac.user_write_mode_address = value; - - break; - case NV3_USER_DAC_PALETTE_DATA: - /* I doubt NV actually read this in their drivers, but it's worth doing anyway */ - /* Bit 1 is listed as "read or write mode" and 7:0 as "Write-only address", but NV only ever set this to 0 too, so i think this should be fine for now */ - nv3->pramdac.palette[nv3->pramdac.user_write_mode_address] = value; - - nv3->pramdac.user_write_mode_address++; - - break; - /* cursor start location */ - case NV3_PRAMDAC_CURSOR_START: - // only 12 bits are used here instead of 16 for some stupid reason - nv3->pramdac.cursor_start.y = (value >> 16) & 0xFFF; - nv3->pramdac.cursor_start.x = (value) & 0xFFF; - nv3_draw_cursor(&nv3->nvbase.svga, 0);//drawline doesn't matter here - break; - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} - diff --git a/src/video/nv/nv3/subsystems/nv3_pramin.c b/src/video/nv/nv3/subsystems/nv3_pramin.c deleted file mode 100644 index 77e2d1a6a..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pramin.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> -#include <86box/nv/classes/vid_nv3_classes.h> - -// Functions only used in this translation unit -#ifndef RELEASE_BUILD -void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context); -#endif - -// 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) -{ - if (!nv3) return 0x00; - - addr &= (nv3->nvbase.svga.vram_max - 1); - uint32_t raw_addr = addr; // saved after and - - addr ^= (nv3->nvbase.svga.vram_max - 0x10); - - uint32_t val = 0x00; - - if (!nv3_ramin_arbitrate_read(addr, &val)) // Oh well - { - val = (uint8_t)nv3->nvbase.svga.vram[addr]; - nv_log_verbose_only("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); - } - - return (uint8_t)val; -} - -// Read 16-bit ramin -uint16_t nv3_ramin_read16(uint32_t addr, void* priv) -{ - if (!nv3) return 0x00; - - addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - addr >>= 1; // what - - uint32_t val = 0x00; - - if (!nv3_ramin_arbitrate_read(addr, &val)) - { - val = (uint16_t)vram_16bit[addr]; - nv_log_verbose_only("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); - } - - return val; -} - -// Read 32-bit ramin -uint32_t nv3_ramin_read32(uint32_t addr, void* priv) -{ - if (!nv3) - return 0x00; - - addr &= (nv3->nvbase.svga.vram_max - 1); - - // why does this not work in one line - uint32_t* vram_32bit = (uint32_t*)nv3->nvbase.svga.vram; - uint32_t raw_addr = addr; // saved after and logged - - addr ^= (nv3->nvbase.svga.vram_max - 0x10); - addr >>= 2; // what - - uint32_t val = 0x00; - - if (!nv3_ramin_arbitrate_read(addr, &val)) - { - val = vram_32bit[addr]; - - nv_log_verbose_only("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); - } - - return val; -} - -// Write 8-bit ramin -void nv3_ramin_write8(uint32_t addr, uint8_t val, void* priv) -{ - if (!nv3) return; - - addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - - uint32_t val32 = (uint32_t)val; - - if (!nv3_ramin_arbitrate_write(addr, val32)) - { - nv3->nvbase.svga.vram[addr] = val; - nv_log_verbose_only("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); - } - - -} - -// Write 16-bit ramin -void nv3_ramin_write16(uint32_t addr, uint16_t val, void* priv) -{ - if (!nv3) return; - - addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - addr >>= 1; // what - - uint32_t val32 = (uint32_t)val; - - if (!nv3_ramin_arbitrate_write(addr, val32)) - { - vram_16bit[addr] = val; - nv_log_verbose_only("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); - } - - -} - -// Write 32-bit ramin -void nv3_ramin_write32(uint32_t addr, uint32_t val, void* priv) -{ - if (!nv3) return; - - addr &= (nv3->nvbase.svga.vram_max - 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 ^= (nv3->nvbase.svga.vram_max - 0x10); - addr >>= 2; // what - - if (!nv3_ramin_arbitrate_write(addr, val)) - { - vram_32bit[addr] = val; - nv_log_verbose_only("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); - } - -} - -void nv3_pfifo_interrupt(uint32_t id, bool fire_now) -{ - nv3->pfifo.interrupt_status |= (1 << id); - nv3_pmc_handle_interrupts(fire_now); -} - -/* -RAMIN access arbitration functions -Arbitrates reads and writes to RAMFC (unused dma context storage), RAMRO (invalid object submission location), RAMHT (hashtable for graphics objectstorage) unused audio memory (RAMAU?) -and generic RAMIN - -Takes a pointer to a result integer. This is because we need to check its result in our normal write function. -Returns true if a valid "non-generic" address was found (e.g. RAMFC/RAMRO/RAMHT). False if the specified address is a generic RAMIN address -*/ -bool nv3_ramin_arbitrate_read(uint32_t address, uint32_t* value) -{ - if (!nv3) return 0x00; - - uint32_t ramht_size = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03); - uint32_t ramro_size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - // Get the addresses of RAMHT, RAMFC, RAMRO - // They must be within first 64KB of PRAMIN! - uint32_t ramht_start = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12; // Must be 0x1000 aligned - uint32_t ramfc_start = ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned - uint32_t ramro_start = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned - - // Calculate the RAMHT and RAMRO end points. - // (RAMFC is always 0x1000 bytes on NV3.) - uint32_t ramht_end = ramht_start; - uint32_t ramfc_end = ramfc_start + 0x1000; - uint32_t ramro_end = ramro_start; - - switch (ramht_size) - { - case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_0; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_1; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_2; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_3; - break; - } - - switch (ramro_size) - { - case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: - ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_0; - break; - case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: - ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_1; - break; - } - - if (address >= ramht_start - && address <= ramht_end) - { - *value = nv3_ramht_read(address); - return true; - } - else if (address >= ramfc_start - && address <= ramfc_end) - { - *value = nv3_ramfc_read(address); - return true; - } - else if (address >= ramro_start - && address <= ramro_end) - { - *value = nv3_ramro_read(address); - return true; - } - - /* temp */ - return false; -} - -bool nv3_ramin_arbitrate_write(uint32_t address, uint32_t value) -{ - if (!nv3) return 0x00; - - uint32_t ramht_size = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03); - uint32_t ramro_size = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_SIZE) & 0x01); - - // Get the addresses of RAMHT, RAMFC, RAMRO - // They must be within first 64KB of PRAMIN! - uint32_t ramht_start = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << 12; // Must be 0x1000 aligned - uint32_t ramfc_start = ((nv3->pfifo.ramfc_config >> NV3_PFIFO_CONFIG_RAMFC_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned - uint32_t ramro_start = ((nv3->pfifo.ramro_config >> NV3_PFIFO_CONFIG_RAMRO_BASE_ADDRESS) & 0x7F) << 9; // Must be 0x200 aligned - - // Calculate the RAMHT and RAMRO end points. - // (RAMFC is always 0x1000 bytes on NV3.) - uint32_t ramht_end = ramht_start; - uint32_t ramfc_end = ramfc_start + 0x1000; - uint32_t ramro_end = ramro_start; - - switch (ramht_size) - { - case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_0; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_1; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_2; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - ramht_end = ramht_start + NV3_RAMIN_RAMHT_SIZE_3; - break; - } - - switch (ramro_size) - { - case NV3_PFIFO_CONFIG_RAMRO_SIZE_512B: - ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_0; - break; - case NV3_PFIFO_CONFIG_RAMRO_SIZE_8K: - ramro_end = ramro_start + NV3_RAMIN_RAMRO_SIZE_1; - break; - } - - // send the addresses to the right part - if (address >= ramht_start - && address <= ramht_end) - { - nv3_ramht_write(address, value); - return true; - } - else if (address >= ramfc_start - && address <= ramfc_end) - { - nv3_ramfc_write(address, value); - return true; - } - else if (address >= ramro_start - && address <= ramro_end) - { - nv3_ramro_write(address, value); - return true; - } - - return false; -} - -// THIS IS THE MOST IMPORTANT FUNCTION! -bool nv3_ramin_find_object(uint32_t name, uint32_t cache_num, uint8_t channel, uint8_t subchannel) -{ - // TODO: WRITE IT!!! - // Set the number of entries to search based on the ramht size (2*(size+1)) - // Not a switch statement in case newer gpus have larger ramins - - uint32_t bucket_entries = 2; - uint8_t ramht_size = (nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_SIZE) & 0x03; - - switch (ramht_size) - { - case NV3_PFIFO_CONFIG_RAMHT_SIZE_4K: - // stays as is - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_8K: - bucket_entries = 4; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_16K: - bucket_entries = 8; - break; - case NV3_PFIFO_CONFIG_RAMHT_SIZE_32K: - bucket_entries = 16; - break; - - } - - // Calculate the address in the hashtable - uint32_t ramht_base = ((nv3->pfifo.ramht_config >> NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS) & 0x0F) << NV3_PFIFO_CONFIG_RAMHT_BASE_ADDRESS; - - // This is certainly wrong. But the objects seem to be written to 4600? So I just multiply it by 80 to multiply the final address by 10. - // Why does this work? - uint32_t ramht_cur_address = ramht_base + (nv3_ramht_hash(name, channel) * bucket_entries * 8); - - nv_log_verbose_only("Beginning search for graphics object at RAMHT base=0x%04x, name=0x%08x, Cache%d, channel=%d.%d)\n", - ramht_cur_address, name, cache_num, channel, subchannel); - - bool found_object = false; - - // set up some variables - uint32_t found_obj_name = 0x00; - nv3_ramin_context_t obj_context_struct = {0}; - - for (uint32_t bucket_entry = 0; bucket_entry < bucket_entries; bucket_entry++) - { - found_obj_name = nv3_ramin_read32(ramht_cur_address, NULL); - ramht_cur_address += 0x04; - uint32_t obj_context = nv3_ramin_read32(ramht_cur_address, NULL); - ramht_cur_address += 0x04; - obj_context_struct = *(nv3_ramin_context_t*)&obj_context; - - // see if the object is in the right channel - if (found_obj_name == name - && obj_context_struct.channel == channel) - { - found_object = true; - break; - } - } - - if (!found_object) - { - if (!cache_num) - { - nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE0_ERROR_PENDING; - nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE; - //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; - } - else - { - nv3->pfifo.debug_0 |= NV3_PFIFO_CACHE1_ERROR_PENDING; - nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; - //It turns itself off on failure, the drivers turn it back on - nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; - } - - nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); - - return false; - } - - // So we did find an object. - // Now try to read some of this... - - // Class ID is 5 bits in all other parts of the gpu but 7 bits here. A move in a direction that didn't pan out? - // Represented as 0x40-0x5f? Some other meaning - - // Perform more validation - - if (obj_context_struct.class_id < NV3_PFIFO_FIRST_VALID_GRAPHICS_OBJECT_ID - || obj_context_struct.class_id > NV3_PFIFO_LAST_VALID_GRAPHICS_OBJECT_ID) - { - fatal("NV3: Invalid graphics object class ID name=0x%04x type=%04x, interpreted by pgraph as: %04x (Contact starfrost)", - name, obj_context_struct.class_id, obj_context_struct.class_id & 0x1F); - } - else if (obj_context_struct.channel > (NV3_DMA_CHANNELS - 1)) - fatal("NV3: Super fucked up graphics object. Contact starfrost with the error string: DMA Channel ID=%d, it should be 0-7", obj_context_struct.channel); - - // Illegal accesses sent to RAMRO, so ignore here - // TODO: SEND THESE TO RAMRO!!!!! - - #ifndef RELEASE_BUILD - nv3_debug_ramin_print_context_info(name, obj_context_struct); - #endif - - // By definition we can't have a cache error by here so take it off - if (!cache_num) - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_HASH_FAILURE; - else - nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_HASH_FAILURE; - - // Caches store all the subchannels for our current dma channel and basically get stale every context switch - // Also we have to check that a osftware object didn't end up in here... - - bool is_software = false; - if (!cache_num) - is_software = (nv3->pfifo.cache0_settings.context[subchannel] & 0x800000); - else - is_software = (nv3->pfifo.cache1_settings.context[subchannel] & 0x800000); - - // This isn't an error but it's sent as an interrupt so the drivers can sync - if (is_software) - { - // handle it as an error - if (!cache_num) - { - nv3->pfifo.cache0_settings.pull0 |= NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_ENABLED; - } - else - { - nv3->pfifo.cache1_settings.pull0 |= NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; - nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_ENABLED; - } - - // It's an error but it isn't lol - nv3_pfifo_interrupt(NV3_PFIFO_INTR_CACHE_ERROR, true); - - } - else - { - // obviously turn off the "is software" if it's not - if (!cache_num) - nv3->pfifo.cache0_settings.pull0 &= ~NV3_PFIFO_CACHE0_PULL0_SOFTWARE_METHOD; - else - nv3->pfifo.cache1_settings.pull0 &= ~NV3_PFIFO_CACHE1_PULL0_SOFTWARE_METHOD; - } - - // Ok we found it. Lol - return true; - -} - - -// Prints out some informaiton about the object -void nv3_debug_ramin_print_context_info(uint32_t name, nv3_ramin_context_t context) -{ - #ifndef RELEASE_BUILD - nv_log_verbose_only("Found object:\n"); - nv_log_verbose_only("Param: 0x%04x\n", name); - - nv_log_verbose_only("Context:\n"); - nv_log_verbose_only("DMA Channel %d (0-7 valid)\n", context.channel); - nv_log_verbose_only("Class ID: 0x%04x (%s)\n", context.class_id & 0x1F, nv3_class_names[context.class_id & 0x1F]); - nv_log_verbose_only("Render Engine %d (0=Software, also DMA? 1=Accelerated Renderer)\n", context.is_rendering); - nv_log_verbose_only("PRAMIN Offset 0x%08x\n", context.ramin_offset << 4); - #endif -} diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c deleted file mode 100644 index 59fa41e8d..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 for unused DMA channels - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -uint32_t nv3_ramfc_read(uint32_t address) -{ - nv_log_verbose_only("RAMFC (Unused DMA channel context) Read (0x%04x) (UNIMPLEMENTED returning 0x00)\n", address); - return 0x00; //temp -} - -void nv3_ramfc_write(uint32_t address, uint32_t value) -{ - nv_log_verbose_only("RAMFC (Unused DMA channel context) Write (0x%04x -> 0x%04x)\n", value, address); -} \ 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 deleted file mode 100644 index 9f90b3434..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramht.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -/* This implements the hash that all the objects are stored within. -It is used to get the offset within RAMHT of a graphics object. - */ - -uint32_t nv3_ramht_hash(uint32_t name, uint32_t channel) -{ - // the official nvidia hash algorithm, tweaked for readability - uint32_t hash = ((name ^ (name >> 8) ^ (name >> 16) ^ (name >> 24)) & 0xFF) ^ (channel & NV3_DMA_CHANNELS_TOTAL); - - - // is this the right endianness? - nv_log_verbose_only("Generated RAMHT hash 0x%04x (RAMHT slot=0x%04x (from name 0x%08x for DMA channel 0x%04x)\n)\n", hash, (hash/8), name, channel); - return hash; -} - - -uint32_t nv3_ramht_read(uint32_t address) -{ - nv_log_verbose_only("RAMHT (Graphics object storage hashtable) Read (0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - RETURNING 0x00\n", address); - return 0x00; -} - -void nv3_ramht_write(uint32_t address, uint32_t value) -{ - nv_log_verbose_only("RAMHT (Graphics object storage hashtable) Write (0x%04x -> 0x%04x), I DON'T BELIEVE THIS SHOULD EVER HAPPEN - UNIMPLEMENTED\n", value, address); -} diff --git a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c b/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c deleted file mode 100644 index 142d746d2..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pramin_ramro.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - -uint32_t nv3_ramro_read(uint32_t address) -{ - nv_log("BIG Problem: RAM Runout (invalid dma object submission) Read (0x%04x)\n", address); - return 0x00; -} - -void nv3_ramro_write(uint32_t address, uint32_t value) -{ - nv_log("BIG Problem: RAM Runout WRITE, OH CRAP!!!! (0x%04x -> 0x%04x)", value, address); -} \ 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 deleted file mode 100644 index ab14be4f9..000000000 --- a/src/video/nv/nv3/subsystems/nv3_ptimer.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - - -nv_register_t ptimer_registers[] = { - { NV3_PTIMER_INTR, "PTIMER - Interrupt Status", NULL, NULL}, - { NV3_PTIMER_INTR_EN, "PTIMER - Interrupt Enable", NULL, NULL,}, - { NV3_PTIMER_NUMERATOR, "PTIMER - Numerator", NULL, NULL, }, - { NV3_PTIMER_DENOMINATOR, "PTIMER - Denominator", NULL, NULL, }, - { NV3_PTIMER_TIME_0_NSEC, "PTIMER - Time0", NULL, NULL, }, - { NV3_PTIMER_TIME_1_NSEC, "PTIMER - Time1", NULL, NULL, }, - { NV3_PTIMER_ALARM_NSEC, "PTIMER - Alarm", NULL, NULL, }, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -// ptimer init code -void nv3_ptimer_init(void) -{ - nv_log("Initialising PTIMER..."); - - nv_log("Done!\n"); -} - -// Handles the PTIMER alarm interrupt -void nv3_ptimer_interrupt(uint32_t num) -{ - nv3->ptimer.interrupt_status |= (1 << num); - - nv3_pmc_handle_interrupts(true); -} - -// Ticks the timer. -void nv3_ptimer_tick(double real_time) -{ - // prevent a divide by zero - if (nv3->ptimer.clock_numerator == 0 - || nv3->ptimer.clock_denominator == 0) - return; - - // get the current time - - // See Envytools. We need to use the frequency as a source. - // We need to figure out how many cycles actually occurred because this counts up every cycle... - // However it seems that their formula is wrong. I can't be bothered to figure out what's going on and, based on documentation from NVIDIA, - // timer_0 is meant to roll over every 4 seconds. Multiplying by 10 basically does the job. - - // Convert to microseconds - double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv3->nvbase.memory_clock_frequency) * 10.0f; - double current_time = freq_base * ((double)nv3->ptimer.clock_numerator) / (double)nv3->ptimer.clock_denominator; // *10.0? - - // truncate it - nv3->ptimer.time += (uint64_t)current_time; - - // Check if the alarm has actually triggered.. - // Only log on ptimer alarm. Otherwise, it's too much spam. - if (nv3->ptimer.time >= nv3->ptimer.alarm) - { - nv_log_verbose_only("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv3->ptimer.alarm); - nv3_ptimer_interrupt(NV3_PTIMER_INTR_ALARM); - } -} - -uint32_t nv3_ptimer_read(uint32_t address) -{ - // always enabled - - nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); - - // Only log these when tehy actually tick - if (address != NV3_PTIMER_TIME_0_NSEC - && address != NV3_PTIMER_TIME_1_NSEC) - { - nv_log_verbose_only("PTIMER Read from 0x%08x", address); - } - - uint32_t ret = 0x00; - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - // Interrupt state: - // Bit 0: Alarm - - switch (reg->address) - { - case NV3_PTIMER_INTR: - ret = nv3->ptimer.interrupt_status; - break; - case NV3_PTIMER_INTR_EN: - ret = nv3->ptimer.interrupt_enable; - break; - case NV3_PTIMER_NUMERATOR: - ret = nv3->ptimer.clock_numerator; // 15:0 - break; - case NV3_PTIMER_DENOMINATOR: - ret = nv3->ptimer.clock_denominator ; //15:0 - break; - // 64-bit value - // High part - case NV3_PTIMER_TIME_0_NSEC: - ret = nv3->ptimer.time & 0xFFFFFFFF; //28:0 - break; - // Low part - case NV3_PTIMER_TIME_1_NSEC: - ret = nv3->ptimer.time >> 32; // 31:5 - break; - case NV3_PTIMER_ALARM_NSEC: - ret = nv3->ptimer.alarm; // 31:5 - break; - } - - } - //TIME0 and TIME1 produce too much log spam that slows everything down - if (reg->address != NV3_PTIMER_TIME_0_NSEC - && reg->address != NV3_PTIMER_TIME_1_NSEC) - { - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PTIMER Write 0x%08x -> 0x%08x", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - // Interrupt state: - // Bit 0 - Alarm - - case NV3_PTIMER_INTR: - nv3->ptimer.interrupt_status &= ~value; - nv3_pmc_clear_interrupts(); - break; - - // Interrupt enablement state - case NV3_PTIMER_INTR_EN: - nv3->ptimer.interrupt_enable = value & 0x1; - break; - // nUMERATOR - case NV3_PTIMER_NUMERATOR: - nv3->ptimer.clock_numerator = value & 0xFFFF; // 15:0 - break; - case NV3_PTIMER_DENOMINATOR: - // prevent Div0 - if (!value) - value = 1; - - nv3->ptimer.clock_denominator = value & 0xFFFF; //15:0 - break; - // 64-bit value - // High part - case NV3_PTIMER_TIME_0_NSEC: - nv3->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 - break; - // Low part - case NV3_PTIMER_TIME_1_NSEC: - nv3->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 - break; - case NV3_PTIMER_ALARM_NSEC: - nv3->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 - break; - } - } - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_pvideo.c b/src/video/nv/nv3/subsystems/nv3_pvideo.c deleted file mode 100644 index 60019b9c6..000000000 --- a/src/video/nv/nv3/subsystems/nv3_pvideo.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - - -nv_register_t pvideo_registers[] = { - { NV3_PVIDEO_INTR, "PVIDEO - Interrupt Status", NULL, NULL}, - { NV3_PVIDEO_INTR_EN, "PVIDEO - Interrupt Enable", NULL, NULL,}, - { NV3_PVIDEO_FIFO_THRESHOLD, "PVIDEO - FIFO Fill Threshold", NULL, NULL}, - { NV3_PVIDEO_FIFO_BURST_LENGTH, "PVIDEO - FIFO Burst Length (1=32, 2=64, 3=128)", NULL, NULL}, - { NV3_PVIDEO_OVERLAY, "PVIDEO - Overlay Info (Bit0 = Video On, Bit4 = Key On, Bit8 = Format, 0=CCIR, 1=YUV2)", NULL, NULL }, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -// ptimer init code -void nv3_pvideo_init(void) -{ - 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])); - uint32_t ret = 0x00; - - // todo: friendly logging - - nv_log_verbose_only("PVIDEO Read from 0x%08x", address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - // Interrupt state: - // Bit 0 - Notifier - - switch (reg->address) - { - case NV3_PVIDEO_INTR: - ret = nv3->pvideo.interrupt_status; - break; - case NV3_PVIDEO_INTR_EN: - ret = nv3->pvideo.interrupt_enable; - break; - case NV3_PVIDEO_FIFO_THRESHOLD: - ret = nv3->pvideo.fifo_threshold; - break; - case NV3_PVIDEO_FIFO_BURST_LENGTH: - ret = nv3->pvideo.fifo_burst_size & 0x03; - break; - case NV3_PVIDEO_OVERLAY: - ret = nv3->pvideo.overlay_settings & 0xFF; - break; - - } - } - - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void 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_verbose_only("PVIDEO Write 0x%08x -> 0x%08x\n", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - // Interrupt state: - // Bit 0 - 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; - case NV3_PVIDEO_FIFO_THRESHOLD: - // only bits 6:3 matter - nv3->pvideo.fifo_threshold = ((value >> 3) & 0x0F) << 3; - break; - case NV3_PVIDEO_FIFO_BURST_LENGTH: - nv3->pvideo.fifo_burst_size = value & 0x03; - break; - case NV3_PVIDEO_OVERLAY: - nv3->pvideo.overlay_settings = value & 0xFF; - break; - } - } - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} \ No newline at end of file diff --git a/src/video/nv/nv3/subsystems/nv3_user.c b/src/video/nv/nv3/subsystems/nv3_user.c deleted file mode 100644 index f10e28793..000000000 --- a/src/video/nv/nv3/subsystems/nv3_user.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 User Submission Area (NV_USER, conceptually considered "Cache1 Pusher") - * - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv3.h> - - -// PIO Method Submission -// 128 channels conceptually supported - a hangover from nv1 where multiple windows all directly programming the gpu were supported? total lunacy. -uint32_t nv3_user_read(uint32_t address) -{ - // Get the address within the subchannel - //todo: print out the subchannel - uint8_t method_offset = (address & 0x1FFC); - - uint8_t channel = (address - NV3_USER_START) / 0x10000; - uint8_t subchannel = ((address - NV3_USER_START)) / 0x2000 % NV3_DMA_SUBCHANNELS_PER_CHANNEL; - - nv_log_verbose_only("User Submission Area PIO Channel %d.%d method_offset=0x%04x\n", channel, subchannel, method_offset); - - // 0x10 is free CACHE1 object - // TODO: THERE ARE OTHER STUFF! - switch (method_offset) - { - case NV3_SUBCHANNEL_PIO_IS_PFIFO_FREE: - return nv3_pfifo_cache1_num_free_spaces(); - case NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_START ... NV3_SUBCHANNEL_PIO_ALWAYS_ZERO_END: - return 0x00; - - } - - nv_log("NV_USER READ: Channel FIELD NOT IMPLEMENTED!!!! offset=0x%04x\n", method_offset); - - return 0x00; -}; - -// Although NV3 doesn't have DMA mode unlike NV4 and later, it's conceptually similar to a "pusher" that pushes graphics commands that you write into CACHE1 that are then pulled out. -// So we send the writes here. This might do other stuff, so we keep this function -void nv3_user_write(uint32_t address, uint32_t value) -{ - nv3_pfifo_cache1_push(address, value); - - // This isn't ideal, but otherwise, the dynarec causes the GPU to write so many objects into CACHE1, it starts overwriting the old objects - // This basically makes the fifo not a fifo, but oh well - nv3_pfifo_cache1_pull(); -} \ No newline at end of file diff --git a/src/video/nv/nv4/nv4_core.c b/src/video/nv/nv4/nv4_core.c deleted file mode 100644 index 5712cf7d1..000000000 --- a/src/video/nv/nv4/nv4_core.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. - * - * This file is part of the 86Box distribution. - * - * NV4 bringup and device emulation. - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - -nv4_t* nv4; - -// Stolen from Voodoo 3 -static video_timings_t timing_nv4_agp = { .type = VIDEO_AGP, .write_b = 2, .write_w = 2, .write_l = 1, .read_b = 20, .read_w = 20, .read_l = 21 }; - -// Initialise the MMIO mappings -void nv4_init_mappings_mmio(void) -{ - nv_log("Initialising MMIO mapping\n"); - - // 0x0 - 1000000: regs - // 0x1000000-2000000 - - // initialize the mmio mapping - mem_mapping_add(&nv4->nvbase.mmio_mapping, 0, 0, - nv4_mmio_read8, - nv4_mmio_read16, - nv4_mmio_read32, - nv4_mmio_write8, - nv4_mmio_write16, - nv4_mmio_write32, - NULL, MEM_MAPPING_EXTERNAL, nv4); - - // initialize the mmio mapping - mem_mapping_add(&nv4->nvbase.ramin_mapping, 0, 0, - nv4_ramin_read8, - nv4_ramin_read16, - nv4_ramin_read32, - nv4_ramin_write8, - nv4_ramin_write16, - nv4_ramin_write32, - NULL, MEM_MAPPING_EXTERNAL, nv4); - -} - -void nv4_init_mappings_svga(void) -{ - nv_log("Initialising SVGA core memory mapping\n"); - // setup the svga mappings - - mem_mapping_add(&nv4->nvbase.framebuffer_mapping, 0, 0, - nv4_dfb_read8, - nv4_dfb_read16, - nv4_dfb_read32, - nv4_dfb_write8, - nv4_dfb_write16, - nv4_dfb_write32, - nv4->nvbase.svga.vram, 0, &nv4->nvbase.svga); - - // the SVGA/LFB mapping is also mirrored - mem_mapping_add(&nv4->nvbase.framebuffer_mapping_mirror, 0, 0, - nv4_dfb_read8, - nv4_dfb_read16, - nv4_dfb_read32, - nv4_dfb_write8, - nv4_dfb_write16, - nv4_dfb_write32, - nv4->nvbase.svga.vram, 0, &nv4->nvbase.svga); - - io_sethandler(NV4_CIO_START, NV4_CIO_SIZE, - nv4_svga_read, NULL, NULL, - nv4_svga_write, NULL, NULL, - nv4); -} - -void nv4_init_mappings(void) -{ - nv4_init_mappings_mmio(); - nv4_init_mappings_svga(); -} - -// Updates the mappings after initialisation. -void nv4_update_mappings(void) -{ - // sanity check - if (!nv4) - return; - - // setting this to 0 doesn't seem to disable it, based on the datasheet - - nv_log("\nMemory Mapping Config Change:\n"); - - (nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) ? nv_log("Enable I/O\n") : nv_log("Disable I/O\n"); - - io_removehandler(NV4_CIO_START, NV4_CIO_SIZE, - nv4_svga_read, NULL, NULL, - nv4_svga_write, NULL, NULL, - nv4); - - if (nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) - io_sethandler(NV4_CIO_START, NV4_CIO_SIZE, - nv4_svga_read, NULL, NULL, - nv4_svga_write, NULL, NULL, - nv4); - - if (!(nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM)) - { - nv_log("The memory was turned off, not much is going to happen.\n"); - return; - } - - // turn off bar0 and bar1 by defualt - mem_mapping_disable(&nv4->nvbase.mmio_mapping); - mem_mapping_disable(&nv4->nvbase.framebuffer_mapping); - mem_mapping_disable(&nv4->nvbase.framebuffer_mapping_mirror); - mem_mapping_disable(&nv4->nvbase.ramin_mapping); - - // Setup BAR0 (MMIO) - - nv_log("BAR0 (MMIO Base) = 0x%08x\n", nv4->nvbase.bar0_mmio_base); - - if (nv4->nvbase.bar0_mmio_base) - { - mem_mapping_set_addr(&nv4->nvbase.mmio_mapping, nv4->nvbase.bar0_mmio_base, NV4_MMIO_SIZE); - mem_mapping_set_addr(&nv4->nvbase.ramin_mapping, nv4->nvbase.bar0_mmio_base + NV4_PRAMIN_START, NV4_PRAMIN_SIZE); - } - - // if this breaks anything, remove it - nv_log("BAR1 (Linear Framebuffer & VRAM) = 0x%08x\n", nv4->nvbase.bar1_lfb_base); - - if (nv4->nvbase.bar1_lfb_base) - { - if (nv4->nvbase.vram_amount == NV4_VRAM_SIZE_16MB) - { - // we don't need this one in the case of 16mb, - mem_mapping_disable(&nv4->nvbase.framebuffer_mapping_mirror); - mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping, nv4->nvbase.bar1_lfb_base, NV4_VRAM_SIZE_16MB); - } - else if (nv4->nvbase.vram_amount == NV4_VRAM_SIZE_8MB) - { - mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping, nv4->nvbase.bar1_lfb_base, NV4_VRAM_SIZE_8MB); - mem_mapping_set_addr(&nv4->nvbase.framebuffer_mapping_mirror, nv4->nvbase.bar1_lfb_base + NV4_VRAM_SIZE_8MB, NV4_VRAM_SIZE_8MB); - } - } - - // Did we change the banked SVGA mode? - switch (nv4->nvbase.svga.gdcreg[NV4_PRMVIO_GX_MISC_INDEX] & 0x0c) - { - case NV4_PRMVIO_GX_MISC_BANKED_128K_A0000: - nv_log("SVGA Banked Mode = 128K @ A0000h\n"); - mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xA0000, 0x20000); // 128kb @ 0xA0000 - nv4->nvbase.svga.banked_mask = 0x1FFFF; - break; - case NV4_PRMVIO_GX_MISC_BANKED_64K_A0000: - nv_log("SVGA Banked Mode = 64K @ A0000h\n"); - mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xA0000, 0x10000); // 64kb @ 0xA0000 - nv4->nvbase.svga.banked_mask = 0xFFFF; - break; - case NV4_PRMVIO_GX_MISC_BANKED_32K_B0000: - nv_log("SVGA Banked Mode = 32K @ B0000h\n"); - mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xB0000, 0x8000); // 32kb @ 0xB0000 - nv4->nvbase.svga.banked_mask = 0x7FFF; - break; - case NV4_PRMVIO_GX_MISC_BANKED_32K_B8000: - nv_log("SVGA Banked Mode = 32K @ B8000h\n"); - mem_mapping_set_addr(&nv4->nvbase.svga.mapping, 0xB8000, 0x8000); // 32kb @ 0xB8000 - nv4->nvbase.svga.banked_mask = 0x7FFF; - break; - } -} - - -bool nv4_init() -{ - nv4 = calloc(1, sizeof(nv4_t)); - - if (!nv4->nvbase.vram_amount) - nv4->nvbase.vram_amount = device_get_config_int("vram_size"); - - /* Set log device name based on card model */ - const char* log_device_name = "NV4"; - - /* Just hardcode full logging */ - - if (device_get_config_int("nv_debug_fulllog")) - nv4->nvbase.log = log_open(log_device_name); - else - nv4->nvbase.log = log_open_cyclic(log_device_name); - - nv_log_set_device(nv4->nvbase.log); - - nv4->nvbase.bus_generation = nv_bus_agp_2x; - - // Figure out which vbios the user selected - // This depends on the bus we are using and if the gpu is rev a/b or rev c - - const char* vbios_file = NV4_VBIOS_STB_REVA; - - int32_t err = rom_init(&nv4->nvbase.vbios, vbios_file, 0xC0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); - - if (err) - { - nv_log("NV4 FATAL: failed to load VBIOS err=%d\n", err); - fatal("Nvidia NV4 init failed: Somehow selected a nonexistent VBIOS? err=%d\n", err); - return false; - } - else - nv_log("Successfully loaded VBIOS located at %s\n", vbios_file); - - pci_add_card(PCI_ADD_AGP, nv4_pci_read, nv4_pci_write, NULL, &nv4->nvbase.pci_slot); - - svga_init(&nv4_device_agp, &nv4->nvbase.svga, nv4, nv4->nvbase.vram_amount, - nv4_recalc_timings, nv4_svga_read, nv4_svga_write, nv4_draw_cursor, NULL); - - video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_nv4_agp); - - // Setup clock information. These values don't matter, I pulled them out of an STB BIOS, we don't need to macro them - nv4->pramdac.nvclk = 0x17809; - nv4->pramdac.mclk = 0x1a30a; - nv4->pramdac.vclk = 0x1400c; - - //timer_add(nv4->pramdac.nvclk, ) - - - - nv4_init_mappings(); - //nv4_update_mappings(); - - return true; -} - -void* nv4_init_stb4400(const device_t *info) -{ - bool successful = nv4_init(); - - if (successful) - return nv4; - else - return NULL; -} - -void nv4_nvclk_tick() -{ - -} - -void nv4_mclk_tick() -{ - -} - -void nv4_vclk_tick() -{ - -} - -void nv4_close(void* priv) -{ - free(nv4); -} - -void nv4_speed_changed(void *priv) -{ - -} - -void nv4_draw_cursor(svga_t* svga, int32_t drawline) -{ - -} - - -// -// SVGA functions -// -void nv4_recalc_timings(svga_t* svga) -{ - // sanity check - if (!nv4) - return; - - - nv4_t* nv4 = (nv4_t*)svga->priv; - - // TODO: Everything, this code sucks, incl. NV4_PRAMDAC_GENERAL_CONTROL_BPC and the offset register - uint32_t pixel_mode = svga->crtc[NV4_CIO_CRE_PIXEL_INDEX] & 0x03; - - svga->memaddr_latch += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0x1F) << 16; - - /* Turn off override if we are in VGA mode */ - svga->override = !(pixel_mode == NV4_CIO_CRE_PIXEL_FORMAT_VGA); - - /* NOTE: The RIVA 128 draws in a way almost completely separate to any other 86Box GPU. - - Basically, we only blit to buffer32 when something changes and we don't even bother using a timer. We only render when there is something to actually render. - - This is because there is no linear relationship between the contents of VRAM and the contents of the display which 86box's SVGA subsystem cannot tolerate. - In fact, the position in VRAM and pitch can be changed at any time via an NV_IMAGE_IN_MEMORY object. - - Therefore, we need to completely bypass it using svga->override and draw our own rendering functions. This allows us to use a neat optimisation trick - to only ever actually draw when we need to do something. This shouldn't be a problem in games, because the drivers will read the current refresh rate from - the Windows settings, and then, just submit objects at that pace for anything that changes on the screen. - */ - - // Set the pixel mode - switch (pixel_mode) - { - case NV4_CIO_CRE_PIXEL_FORMAT_8BPP: - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 1; // ????? - svga->bpp = 8; - svga->lowres = 0; - svga->map8 = svga->pallook; - break; - case NV4_CIO_CRE_PIXEL_FORMAT_16BPP: - /* This is some sketchy shit that is an attempt at an educated guess - at pixel clock differences between 9x and NT only in 16bpp. If there is ever an error on 9x with "interlaced" looking graphics, - this is what's causing it. Possibly fucking up the drivers under *ReactOS* of all things */ - if ((svga->crtc[NV4_CIO_CR_VRS_INDEX] >> 1) & 0x01) - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 2; - else - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; - - // 15bpp mode is removed on NV4 - // TODO: Not svga - svga->bpp = 16; - svga->lowres = 0; - - break; - case NV4_CIO_CRE_PIXEL_FORMAT_32BPP: - svga->rowoffset += (svga->crtc[NV4_CIO_CRE_RPC0_INDEX] & 0xE0) << 3; - - svga->bpp = 32; - svga->lowres = 0; - //svga->render = nv4_render_32bpp; - break; - } - - - if (((svga->miscout >> 2) & 2) == 2) - { - // set clocks - //nv4_pramdac_set_pixel_clock(); - //nv4_pramdac_set_vram_clock(); - } -} - - -// See if the bios rom is available. -int32_t nv4_available(void) -{ - return (rom_present(NV4_VBIOS_STB_REVA)); -} - -// NV4 (RIVA 128) -// AGP -// 8MB or 16MB VRAM -const device_t nv4_device_agp = -{ - .name = "nVIDIA RIVA TNT (STB Velocity 4400)", - .internal_name = "nv4_stb4400", - .flags = DEVICE_AGP, - .local = 0, - .init = nv4_init_stb4400, - .close = nv4_close, - .speed_changed = nv4_speed_changed, - .force_redraw = nv4_force_redraw, - .available = nv4_available, - .config = nv4_config, -}; diff --git a/src/video/nv/nv4/nv4_core_config.c b/src/video/nv/nv4/nv4_core_config.c deleted file mode 100644 index 2dd88eed6..000000000 --- a/src/video/nv/nv4/nv4_core_config.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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. - * - * Provides NV4 configuration - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - -const device_config_t nv4_config[] = -{ - // Memory configuration - { - .name = "vram_size", - .description = "VRAM Size", - .type = CONFIG_SELECTION, - .default_int = NV4_VRAM_SIZE_16MB, - .selection = - { - // I thought this was never released, but it seems that at least one was released: - // The card was called the "NEC G7AGK" - { - .description = "8 MB", - .value = NV4_VRAM_SIZE_8MB, - }, - - { - .description = "16 MB", - .value = NV4_VRAM_SIZE_16MB, - }, - } - - }, - // Multithreading configuration - { - - .name = "pgraph_threads", - .description = "Render threads", - .type = CONFIG_SELECTION, - .default_int = 1, // todo: change later - .selection = - { - { - .description = "1 thread (Only use if issues appear with more threads)", - .value = 1, - }, - { - .description = "2 threads", - .value = 2, - }, - { - .description = "4 threads", - .value = 4, - }, - { - .description = "8 threads", - .value = 8, - }, - }, - }, -#ifndef RELEASE_BUILD - { - .name = "nv_debug_fulllog", - .description = "Disable Cyclical Lines Detection for nv_log (Use for getting full context at cost of VERY large log files)", - .type = CONFIG_BINARY, - .default_int = 0, - }, -#endif - { - .type = CONFIG_END - } -}; \ No newline at end of file diff --git a/src/video/nv/nv4/nv4_core_io.c b/src/video/nv/nv4/nv4_core_io.c deleted file mode 100644 index 4c5aa5a40..000000000 --- a/src/video/nv/nv4/nv4_core_io.c +++ /dev/null @@ -1,1283 +0,0 @@ -/* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. - * - * This file is part of the 86Box distribution. - * - * NV4 I/O, including Real-Mode Access (RMA), memory mapping, PCI, AGP, SVGA and MMIO via PCI BARs - * - * MMIO dumps are available at: https://nvwiki.org/misc/NVDumps/ - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ - - -// Prototypes for functions only used in this translation unit - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - - -void nv4_init_mappings_mmio(void); -void nv4_init_mappings_svga(void); -bool nv4_is_svga_redirect_address(uint32_t addr); - -uint8_t nv4_svga_read(uint16_t addr, void* priv); -void nv4_svga_write(uint16_t addr, uint8_t val, void* priv); - - -uint32_t nv4_mmio_arbitrate_read(uint32_t addr) -{ - uint32_t ret = 0x00; - - if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) - ret = nv4_ptimer_read(addr); - else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) - ret = nv4_pramdac_read(addr); - - #ifdef NV_LOG - nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); - - if (reg) - { - if (reg->on_read) - ret = reg->on_read(); - - nv_log_verbose_only("Register read from 0x%08x value=%08x", addr, ret); - - } - #endif - -} - -void nv4_mmio_arbitrate_write(uint32_t addr, uint32_t val) -{ - if (addr >= NV4_PTIMER_START && addr <= NV4_PTIMER_END) - nv4_ptimer_write(addr, val); - else if (addr >= NV4_PRAMDAC_START && addr <= NV4_PRAMDAC_END) - nv4_pramdac_write(addr, val); - - #ifdef NV_LOG - nv_register_t reg = nv_get_register(addr, &nv4_registers, sizeof(nv4_registers)/sizeof(nv4_registers[0])); - - if (reg) - { - if (reg->on_write) - reg->on_write(val); - - nv_log_verbose_only("Register write from 0x%08x value=%08x", addr, val); - - } - #endif -} - -// Determine if this address needs to be redirected to the SVGA subsystem. - -bool nv4_is_svga_redirect_address(uint32_t addr) -{ - return (addr >= NV4_PRMVIO_START && addr <= NV4_PRMVIO_END) // VGA - || (addr >= NV4_PRMCIO_START && addr <= NV4_PRMCIO_END) // CRTC - || (addr >= NV4_USER_DAC_START && addr <= NV4_USER_DAC_END); // Note: 6813c6-6813c9 are ignored somewhere else -} - -uint8_t nv4_rma_read(uint16_t addr) -{ - addr &= 0xFF; - uint32_t real_final_address = 0x0; - uint8_t ret = 0x0; - - switch (addr) - { - // signature so you know reads work - case 0x00: - ret = NV4_PRMIO_RMA_ID_CODE_VALID & 0xFF; - break; - case 0x01: - ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 8) & 0xFF; - break; - case 0x02: - ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 16) & 0xFF; - break; - case 0x03: - ret = (NV4_PRMIO_RMA_ID_CODE_VALID >> 24) & 0xFF; - break; - case 0x08 ... 0x0B: - // reads must be dword aligned - real_final_address = (nv4->pbus.rma.addr + (addr & 0x03)); - - if (nv4->pbus.rma.addr < NV4_MMIO_SIZE) - ret = nv4_mmio_read8(real_final_address, NULL); - else - { - /* Do we need to read RAMIN here? */ - ret = nv4->nvbase.svga.vram[real_final_address - NV4_MMIO_SIZE] & (nv4->nvbase.svga.vram_max - 1); - } - - // log current location for vbios RE - nv_log_verbose_only("MMIO Real Mode Access read, initial address=0x%08x 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 nv4_rma_write(uint16_t addr, uint8_t val) -{ - // 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 - nv4->pbus.rma.addr &= ~0xff; - nv4->pbus.rma.addr |= val; - break; - case 0x01: // 2nd highest byte - nv4->pbus.rma.addr &= ~0xff00; - nv4->pbus.rma.addr |= (val << 8); - break; - case 0x02: // 3rd highest byte - nv4->pbus.rma.addr &= ~0xff0000; - nv4->pbus.rma.addr |= (val << 16); - break; - case 0x03: // 4th highest byte - nv4->pbus.rma.addr &= ~0xff000000; - nv4->pbus.rma.addr |= (val << 24); - break; - } - } - // Data to send to MMIO - else - { - switch (addr % 0x04) - { - case 0x00: // lowest byte - nv4->pbus.rma.data &= ~0xff; - nv4->pbus.rma.data |= val; - break; - case 0x01: // 2nd highest byte - nv4->pbus.rma.data &= ~0xff00; - nv4->pbus.rma.data |= (val << 8); - break; - case 0x02: // 3rd highest byte - nv4->pbus.rma.data &= ~0xff0000; - nv4->pbus.rma.data |= (val << 16); - break; - case 0x03: // 4th highest byte - nv4->pbus.rma.data &= ~0xff000000; - nv4->pbus.rma.data |= (val << 24); - - nv_log_verbose_only("MMIO Real Mode Access write transaction complete, initial address=0x%04x final RMA MMIO address=0x%08x data=0x%08x\n", - addr, nv4->pbus.rma.addr, nv4->pbus.rma.data); - - if (nv4->pbus.rma.addr < NV4_MMIO_SIZE) - nv4_mmio_write32(nv4->pbus.rma.addr, nv4->pbus.rma.data, NULL); - else // failsafe code, i don't think you will ever write outside of VRAM? - { - uint32_t* vram_32 = (uint32_t*)nv4->nvbase.svga.vram; - vram_32[(nv4->pbus.rma.addr - NV4_MMIO_SIZE) >> 2] = nv4->pbus.rma.data; - } - - - break; - } - } - - if (addr & 0x10) - nv4->pbus.rma.addr += 0x04; // Alignment -} - - - -// 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 nv4_mmio_read8(uint32_t addr, void* priv) -{ - uint32_t ret = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - // We need to specifically exclude this particular set of registers - // so we can write the 4/8bpp CLUT - if (addr >= NV4_USER_DAC_PALETTE_START && addr <= NV4_USER_DAC_PALETTE_END) - { - // Throw directly into PRAMDAC - return nv4_mmio_arbitrate_read(addr); - } - - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv4_svga_read(real_address, nv4); - - nv_log_verbose_only("Redirected MMIO read8 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - - // see if unaligned reads are a problem - ret = nv4_mmio_read32(addr, priv); - return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFF); -} - -// Read 16-bit MMIO -uint16_t nv4_mmio_read16(uint32_t addr, void* priv) -{ - uint32_t ret = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv4_svga_read(real_address, nv4) - | (nv4_svga_read(real_address + 1, nv4) << 8); - - nv_log_verbose_only("Redirected MMIO read16 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - - ret = nv4_mmio_read32(addr, priv); - return (uint8_t)(ret >> ((addr & 3) << 3) & 0xFFFF); -} - -// Read 32-bit MMIO -uint32_t nv4_mmio_read32(uint32_t addr, void* priv) -{ - uint32_t ret = 0x00; - - // Some of these addresses are Weitek VGA stuff and we need to mask it to this first because the weitek addresses are 8-bit aligned. - addr &= 0xFFFFFF; - - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - ret = nv4_svga_read(real_address, nv4) - | (nv4_svga_read(real_address + 1, nv4) << 8) - | (nv4_svga_read(real_address + 2, nv4) << 16) - | (nv4_svga_read(real_address + 3, nv4) << 24); - - nv_log_verbose_only("Redirected MMIO read32 to SVGA: addr=0x%04x returned 0x%04x\n", addr, ret); - - return ret; - } - - ret = nv4_mmio_arbitrate_read(addr); - - return ret; -} - -// Write 8-bit MMIO -void nv4_mmio_write8(uint32_t addr, uint8_t val, void* priv) -{ - addr &= 0xFFFFFF; - - // We need to specifically exclude this particular set of registers - // so we can write the 4/8bpp CLUT - if (addr >= NV4_USER_DAC_PALETTE_START && addr <= NV4_USER_DAC_PALETTE_END) - { - // Throw directly into PRAMDAC - nv4_mmio_arbitrate_write(addr, val); - return; - } - - // This is weitek vga stuff - // If we need to add more of these we can convert these to a switch statement - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write8 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv4_svga_write(real_address, val & 0xFF, nv4); - - return; - } - - // overwrite first 8bits of a 32 bit value - uint32_t new_val = nv4_mmio_read32(addr, NULL); - - new_val &= (~0xFF << (addr & 3) << 3); - new_val |= (val << ((addr & 3) << 3)); - - nv4_mmio_write32(addr, new_val, priv); -} - -// Write 16-bit MMIO -void nv4_mmio_write16(uint32_t addr, uint16_t val, void* priv) -{ - addr &= 0xFFFFFF; - - // This is weitek vga stuff - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write16 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv4_svga_write(real_address, val & 0xFF, nv4); - nv4_svga_write(real_address + 1, (val >> 8) & 0xFF, nv4); - - return; - } - - // overwrite first 16bits of a 32 bit value - uint32_t new_val = nv4_mmio_read32(addr, NULL); - - new_val &= (~0xFFFF << (addr & 3) << 3); - new_val |= (val << ((addr & 3) << 3)); - - nv4_mmio_write32(addr, new_val, priv); -} - -// Write 32-bit MMIO -void nv4_mmio_write32(uint32_t addr, uint32_t val, void* priv) -{ - addr &= 0xFFFFFF; - - // This is weitek vga stuff - if (nv4_is_svga_redirect_address(addr)) - { - // svga writes are not logged anyway rn - uint32_t real_address = addr & 0x3FF; - - nv_log_verbose_only("Redirected MMIO write32 to SVGA: addr=0x%04x val=0x%02x\n", addr, val); - - nv4_svga_write(real_address, val & 0xFF, nv4); - nv4_svga_write(real_address + 1, (val >> 8) & 0xFF, nv4); - nv4_svga_write(real_address + 2, (val >> 16) & 0xFF, nv4); - nv4_svga_write(real_address + 3, (val >> 24) & 0xFF, nv4); - - return; - } - - nv4_mmio_arbitrate_write(addr, val); -} - -// PCI stuff -// BAR0 Pointer to MMIO space & RAMIN -// BAR1 Pointer to Linear Framebuffer -uint8_t nv4_pci_read(int32_t func, int32_t addr, void* priv) -{ - uint8_t ret = 0x00; - - // sanity check - if (!nv4) - return ret; - - // Convert to the MMIO addresses - if (addr <= 0xFF) - addr += 0x1800; - - // Anything not listed is 0x00 - // PCI values extracted from https://nvwiki.org/misc/NVDumps/RivaMobileNV4/Nv4win_HL/nv4bar0.bin - // STB V4400 (running Half-Life) - switch (addr) - { - // Get the pci vendor id.. - - case NV4_PBUS_PCI_VENDOR_ID: - ret = (NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA & 0xFF); - break; - - case NV4_PBUS_PCI_VENDOR_ID + 1: // all access 8bit - ret = (NV4_PBUS_PCI_DEVICE_VENDOR_NVIDIA >> 8); - break; - - // device id - - case NV4_PBUS_PCI_DEVICE_ID: - ret = (NV_PCI_DEVICE_NV4 & 0xFF); - break; - - case NV4_PBUS_PCI_DEVICE_ID + 1: - ret = (NV_PCI_DEVICE_NV4 >> 8); - break; - - // various capabilities enabled by default - // IO space enabled - // Memory space enabled - // Bus master enabled - // Write/inval enabled - // Pal snoop enabled - // Capabiliies list enabled - // 66Mhz FSB capable - - case NV4_PBUS_PCI_COMMAND: - ret = nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L]; - break; - // COMMAND_L not used - - // pci status register - case NV4_PBUS_PCI_STATUS: - if (nv4->straps - & NV4_STRAP_BUS_SPEED_66MHZ) - ret = (nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] | NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE); - else - ret = nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L]; - - break; - - case NV4_PBUS_PCI_STATUS_2: - ret = (nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H]) & (NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST << NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING); - break; - - case NV4_PBUS_PCI_REVISION_ID: - ret = nv4->nvbase.gpu_revision; // Commercial release - break; - - case PCI_REG_PROG_IF: - ret = 0x00; - break; - - // We only need to return 0x30 since the VGA class code is 0x30000 - case NV4_PBUS_PCI_CLASS_CODE: - ret = (NV4_PBUS_PCI_CLASS_CODE_VGA) >> 16; // CLASS_CODE_VGA - break; - - - case NV4_PBUS_PCI_LATENCY_TIMER: - case NV4_PBUS_PCI_HEADER_TYPE: - ret = 0x00; - break; - - // BARs are marked as prefetchable per the datasheet - case NV4_PBUS_PCI_BAR0_INFO: - case NV4_PBUS_PCI_BAR1_INFO: - // only bit that matters is bit 3 (prefetch bit) - ret = (NV4_PBUS_PCI_BAR_PREFETCHABLE_MERGABLE << NV4_PBUS_PCI_BAR_PREFETCHABLE); - break; - - // MMIO base address - case NV4_PBUS_PCI_BAR0_BASE_31_TO_24: - ret = nv4->nvbase.bar0_mmio_base >> 24;//8bit value - break; - - case NV4_PBUS_PCI_BAR1_BASE_31_TO_24: - ret = nv4->nvbase.bar1_lfb_base >> 24; //8bit value - break; - - case NV4_PBUS_PCI_BAR_RESERVED_START ... NV4_PBUS_PCI_BAR_RESERVED_END: - case NV4_PBUS_PCI_BAR0_UNUSED1: - case NV4_PBUS_PCI_BAR0_UNUSED2: - case NV4_PBUS_PCI_BAR1_UNUSED1: - case NV4_PBUS_PCI_BAR1_UNUSED2: - - ret = 0x00; // hard lock - break; - - case NV4_PBUS_PCI_ROM: - ret = nv4->nvbase.pci_config.vbios_enabled; - break; - - case NV4_PBUS_PCI_INTR_LINE: - ret = nv4->nvbase.pci_config.int_line; - break; - - case NV4_PBUS_PCI_INTR_PIN: - ret = PCI_INTA; - break; - - // - // Capabilities pointers - // - - case NV4_PBUS_PCI_NEXT_PTR: - ret = NV4_PBUS_PCI_CAP_PTR_POWER_MGMT; - break; - - case NV4_PBUS_PCIPOWER_NEXT_PTR: - ret = NV4_PBUS_PCI_CAP_PTR_AGP; - break; - - // AGP is the end of the chain - case NV4_PBUS_AGP_NEXT_PTR: - ret = 0x00; - break; - - case NV4_PBUS_PCI_MAX_LAT: - ret = NV4_PBUS_PCI_MAX_LAT_250NS; - break; - - // these map to the subsystem - // todo: port this bugfix to NV4 - case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE: - case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE + 1: - case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE: - case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE + 1: - ret = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_SUBSYSTEM_ID + (addr & 0x03)]; - break; - - case NV4_PBUS_AGP_CAPABILITIES: - ret = NV4_PBUS_AGP_CAPABILITY_AGP; // AGP capable device - break; - case NV4_PBUS_AGP_REV: - ret = (NV4_PBUS_AGP_REV_MAJOR_1 << NV4_PBUS_AGP_REV_MAJOR) | NV4_PBUS_AGP_REV_MINOR; - break; - case NV4_PBUS_AGP_STATUS_RATE: - ret = NV4_PBUS_AGP_STATUS_RATE_1X_AND_2X; - break; - case NV4_PBUS_AGP_STATUS_SBA: - ret = (NV4_PBUS_AGP_STATUS_SBA_STATUS_CAPABLE) << NV4_PBUS_AGP_STATUS_SBA_STATUS; // Sideband is supported on NV4 - break; - case NV4_PBUS_AGP_STATUS_RQ: - ret = NV4_PBUS_AGP_STATUS_RQ_16; - break; - case NV4_PBUS_AGP_COMMAND_2: - ret = (nv4->nvbase.agp_enabled) << NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED - | (nv4->nvbase.agp_sba_enabled) << NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED; - break; - default: // by default just return pci_config.pci_regs (default value for nonwritten registers is 0x00) - ret = nv4->nvbase.pci_config.pci_regs[addr]; - break; - - } - - nv_log("nv4_pci_read func=0x%04x addr=0x%04x ret=0x%04x\n", func, addr & 0xFF, ret); - return ret; -} - - -// nv4 pci/agp write -void nv4_pci_write(int32_t func, int32_t addr, uint8_t val, void* priv) -{ - // sanity check - if (!nv4) - return; - - // Convert to the MMIO addresses - if (addr <= 0xFF) - addr += 0x1800; - // some addresses are not writable so can't have any effect and can't be allowed to be modified using this code - // as an example, only the most significant byte of the PCI BARs can be modified - if (addr == NV4_PBUS_PCI_BAR0_UNUSED1 || addr == NV4_PBUS_PCI_BAR0_UNUSED2 - && addr == NV4_PBUS_PCI_BAR1_UNUSED1 || addr == NV4_PBUS_PCI_BAR1_UNUSED2) - return; - - nv_log("nv4_pci_write func=0x%04x addr=0x%04x val=0x%04x\n", func, addr & 0xFF, val); - - nv4->nvbase.pci_config.pci_regs[addr] = val; - - switch (addr) - { - // standard pci command stuff - case NV4_PBUS_PCI_COMMAND: - nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_L] = val; - // actually update the mappings - nv4_update_mappings(); - break; - case NV4_PBUS_PCI_COMMAND_H: - nv4->nvbase.pci_config.pci_regs[PCI_REG_COMMAND_H] = val; - // actually update the mappings - nv4_update_mappings(); - break; - // pci status register - case NV4_PBUS_PCI_STATUS: - nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_L] = val | (NV4_PBUS_PCI_STATUS_66MHZ_CAPABLE << NV4_PBUS_PCI_STATUS_66MHZ); - break; - case NV4_PBUS_PCI_STATUS_2: - nv4->nvbase.pci_config.pci_regs[PCI_REG_STATUS_H] = val | (NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING_FAST << NV4_PBUS_PCI_STATUS_2_DEVSEL_TIMING); - break; - //TODO: ACTUALLY REMAP THE MMIO AND NV_USER - case NV4_PBUS_PCI_BAR0_BASE_31_TO_24: - nv4->nvbase.bar0_mmio_base = val << 24; - nv4_update_mappings(); - break; - case NV4_PBUS_PCI_BAR1_BASE_31_TO_24: - nv4->nvbase.bar1_lfb_base = val << 24; - nv4_update_mappings(); - break; - - case NV4_PBUS_PCI_ROM: - case NV4_PBUS_PCI_ROM_BASE: - - // make sure we are actually toggling the vbios, not the rom base - if (addr == NV4_PBUS_PCI_ROM) - nv4->nvbase.pci_config.vbios_enabled = (val & 0x01); - - if (nv4->nvbase.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(&nv4->nvbase.vbios.mapping); - - if (addr != NV4_PBUS_PCI_ROM) - { - uint32_t old_addr = nv4->nvbase.vbios.mapping.base; - // 9bit register - uint32_t new_addr = nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 3] << 24 | - nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_ROM + 2] << 16; - - // only bits 31;22 matter - //new_addr &= 0xFFC00000; - - // move it - mem_mapping_set_addr(&nv4->nvbase.vbios.mapping, new_addr, 0x8000); - - nv_log("...i like to move it move it (VBIOS Relocation) 0x%x -> 0x%x\n", old_addr, new_addr); - - } - else - { - nv_log("...VBIOS Enable\n"); - } - } - else - { - nv_log("...VBIOS Disable\n"); - mem_mapping_disable(&nv4->nvbase.vbios.mapping); - - } - break; - case NV4_PBUS_PCI_INTR_LINE: - nv4->nvbase.pci_config.int_line = val; - break; - // these are mirrored to the subsystem id and also stored in the ROMBIOS - //todo: port to pci - case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE: - case NV4_PBUS_PCI_SUBSYSTEM_ID_WRITABLE + 1: - case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE: - case NV4_PBUS_PCI_SUBSYSTEM_VENDOR_ID_WRITABLE + 1: - nv4->nvbase.pci_config.pci_regs[NV4_PBUS_PCI_SUBSYSTEM_ID + (addr & 0x03)] = val; - break; - case NV4_PBUS_AGP_COMMAND_2: - nv4->nvbase.agp_enabled = (val >> NV4_PBUS_AGP_COMMAND_2_AGP_ENABLED) & 0x01; - nv4->nvbase.agp_sba_enabled = (val >> NV4_PBUS_AGP_COMMAND_2_SBA_ENABLED) & 0x01; - break; - default: - break; - } -} - - -void nv4_speed_changed(void* priv) -{ - // sanity check - if (!nv4) - return; - - nv4_recalc_timings(&nv4->nvbase.svga); -} - -// Force Redraw -// Reset etc. -void nv4_force_redraw(void* priv) -{ - // sanity check - if (!nv4) - return; - - nv4->nvbase.svga.fullchange = changeframecount; -} - -// CHECK that ramin is the smae as nv4 - -// Read 8-bit ramin -uint8_t nv4_ramin_read8(uint32_t addr, void* priv) -{ - if (!nv4) return 0x00; - - addr &= (nv4->nvbase.svga.vram_max - 1); - uint32_t raw_addr = addr; // saved after and - - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - - uint32_t val = 0x00; - - //if (!nv4_ramin_arbitrate_read(addr, &val)) // Oh well - //{ - val = (uint8_t)nv4->nvbase.svga.vram[addr]; - nv_log_verbose_only("Read byte from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); - //} - - return (uint8_t)val; -} - -// Read 16-bit ramin -uint16_t nv4_ramin_read16(uint32_t addr, void* priv) -{ - if (!nv4) return 0x00; - - addr &= (nv4->nvbase.svga.vram_max - 1); - - // why does this not work in one line - svga_t* svga = &nv4->nvbase.svga; - uint16_t* vram_16bit = (uint16_t*)svga->vram; - uint32_t raw_addr = addr; // saved after and - - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - addr >>= 1; // what - - uint32_t val = 0x00; - - //if (!nv4_ramin_arbitrate_read(addr, &val)) - //{ - val = (uint16_t)vram_16bit[addr]; - nv_log_verbose_only("Read word from PRAMIN addr=0x%08x (raw address=0x%08x)\n", addr, raw_addr); - //} - - return val; -} - -// Read 32-bit ramin -uint32_t nv4_ramin_read32(uint32_t addr, void* priv) -{ - if (!nv4) - return 0x00; - - addr &= (nv4->nvbase.svga.vram_max - 1); - - // why does this not work in one line - uint32_t* vram_32bit = (uint32_t*)nv4->nvbase.svga.vram; - uint32_t raw_addr = addr; // saved after and logged - - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - addr >>= 2; // what - - uint32_t val = 0x00; - - //if (!nv4_ramin_arbitrate_read(addr, &val)) - //{ - val = vram_32bit[addr]; - nv_log_verbose_only("Read dword from PRAMIN 0x%08x <- 0x%08x (raw address=0x%08x)\n", val, addr, raw_addr); - //} - - return val; -} - -// Write 8-bit ramin -void nv4_ramin_write8(uint32_t addr, uint8_t val, void* priv) -{ - if (!nv4) return; - - addr &= (nv4->nvbase.svga.vram_max - 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/nv4t only and 2mb...i haven't found a 22mb card) - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - - uint32_t val32 = (uint32_t)val; - - //if (!nv4_ramin_arbitrate_write(addr, val32)) - //{ - nv4->nvbase.svga.vram[addr] = val; - nv_log_verbose_only("Write byte to PRAMIN addr=0x%08x val=0x%02x (raw address=0x%08x)\n", addr, val, raw_addr); - //} -} - -// Write 16-bit ramin -void nv4_ramin_write16(uint32_t addr, uint16_t val, void* priv) -{ - if (!nv4) return; - - addr &= (nv4->nvbase.svga.vram_max - 1); - - // why does this not work in one line - svga_t* svga = &nv4->nvbase.svga; - uint16_t* vram_16bit = (uint16_t*)svga->vram; - uint32_t raw_addr = addr; // saved after and - - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - addr >>= 1; // what - - uint32_t val32 = (uint32_t)val; - - //if (!nv4_ramin_arbitrate_write(addr, val32)) - //{ - vram_16bit[addr] = val; - nv_log_verbose_only("Write word to PRAMIN addr=0x%08x val=0x%04x (raw address=0x%08x)\n", addr, val, raw_addr); - //} - - -} - -// Write 32-bit ramin -void nv4_ramin_write32(uint32_t addr, uint32_t val, void* priv) -{ - if (!nv4) return; - - addr &= (nv4->nvbase.svga.vram_max - 1); - - // why does this not work in one line - svga_t* svga = &nv4->nvbase.svga; - uint32_t* vram_32bit = (uint32_t*)svga->vram; - uint32_t raw_addr = addr; // saved after and - - addr ^= (nv4->nvbase.svga.vram_max - 0x10); - addr >>= 2; // what - - //if (!nv4_ramin_arbitrate_write(addr, val)) - //{ - vram_32bit[addr] = val; - nv_log_verbose_only("Write dword to PRAMIN addr=0x%08x val=0x%08x (raw address=0x%08x)\n", addr, val, raw_addr); - //} -} - -// Read from SVGA core memory -uint8_t nv4_svga_read(uint16_t addr, void* priv) -{ - // CR = CRTC Controller - // CRE = CRTC Controller Extended (weitek) - uint8_t ret = 0x00; - - // sanity check - if (!nv4) - return ret; - - // If we need to RMA from GPU MMIO, go do that - if (addr >= NV4_RMA_REGISTER_START - && addr <= NV4_RMA_REGISTER_END) - { - if (!(nv4->pbus.rma.mode & 0x01)) - return ret; - - // must be dword aligned - uint32_t real_rma_read_addr = ((nv4->pbus.rma.mode & 0x0E) << 1) + (addr & 0x03); - ret = nv4_rma_read(real_rma_read_addr); - return ret; - } - - // mask off b0/d0 registers - if ((((addr & 0xFFF0) == 0x3D0 - || (addr & 0xFFF0) == 0x3B0) && addr < 0x3de) - && !(nv4->nvbase.svga.miscout & 1)) - addr ^= 0x60; - - switch (addr) - { - // Alias for "get current SVGA CRTC register ID" - case NV4_CIO_CRX_COLOR: - ret = nv4->nvbase.svga.crtcreg; - break; - case NV4_CIO_CR_COLOR: - // Support the extended NVIDIA CRTC register range - switch (nv4->nvbase.svga.crtcreg) - { - case NV4_CIO_CRE_RL0_INDEX: - ret = nv4->nvbase.svga.displine & 0xFF; - break; - /* Is rl1?*/ - case NV4_CIO_CRE_RL1_INDEX: - ret = (nv4->nvbase.svga.displine >> 8) & 7; - break; - case NV4_CIO_CRE_DDC_STATUS_INDEX: - ret = i2c_gpio_get_sda(nv4->nvbase.i2c) << 3 - | i2c_gpio_get_scl(nv4->nvbase.i2c) << 2; - - break; - default: - ret = nv4->nvbase.svga.crtc[nv4->nvbase.svga.crtcreg]; - } - break; - default: - ret = svga_in(addr, &nv4->nvbase.svga); - break; - } - - - return ret; //TEMP -} - -// Write to SVGA core memory -void nv4_svga_write(uint16_t addr, uint8_t val, void* priv) -{ - // sanity check - if (!nv4) - return; - - // If we need to RMA to GPU MMIO, go do that - if (addr >= NV4_RMA_REGISTER_START - && addr <= NV4_RMA_REGISTER_END) - { - // we don't need to store these registers... - nv4->pbus.rma.rma_regs[addr & 3] = val; - - if (!(nv4->pbus.rma.mode & 0x01)) // we are halfway through sending something - return; - - uint32_t real_rma_write_addr = ((nv4->pbus.rma.mode & (0x0E)) << 1) + (addr & 0x03); - - nv4_rma_write(real_rma_write_addr, nv4->pbus.rma.rma_regs[addr & 0x03]); - return; - } - - // mask off b0/d0 registers - if ((((addr & 0xFFF0) == 0x3D0 || (addr & 0xFFF0) == 0x3B0) - && addr < 0x3de) - && !(nv4->nvbase.svga.miscout & 1))//miscout bit 7 controls mappping - addr ^= 0x60; - - uint8_t crtcreg = nv4->nvbase.svga.crtcreg; - uint8_t old_val = 0x00; - - // todo: - // Pixel formats (8bit vs 555 vs 565) - // VBE 3.0? - - switch (addr) - { - case NV4_CIO_CRX_COLOR: - // real mode access to GPU MMIO space... - nv4->nvbase.svga.crtcreg = val; - break; - // support the extended crtc regs and debug this out - case NV4_CIO_CR_COLOR: - - // Implements the VGA Protect register - if ((nv4->nvbase.svga.crtcreg < NV4_CIO_CR_OVL_INDEX) && (nv4->nvbase.svga.crtc[NV4_CIO_CR_VRE_INDEX] & 0x80)) - return; - - // Ignore certain bits when VGA Protect register set and we are writing to CRTC register=07h - if ((nv4->nvbase.svga.crtcreg == NV4_CIO_CR_OVL_INDEX) && (nv4->nvbase.svga.crtc[NV4_CIO_CR_VRE_INDEX] & 0x80)) - val = (nv4->nvbase.svga.crtc[NV4_CIO_CR_OVL_INDEX] & ~0x10) | (val & 0x10); - - // set the register value... - old_val = nv4->nvbase.svga.crtc[crtcreg]; - - nv4->nvbase.svga.crtc[crtcreg] = val; - // ...now act on it - - // Handle nvidia extended Bank0/Bank1 IDs - switch (crtcreg) - { - case NV4_CIO_CRE_PAGE0_INDEX: - nv4->nvbase.cio_read_bank = val; - if (nv4->nvbase.svga.chain4) // chain4 addressing (planar?) - nv4->nvbase.svga.read_bank = nv4->nvbase.cio_read_bank << 15; - else - nv4->nvbase.svga.read_bank = nv4->nvbase.cio_read_bank << 13; // extended bank numbers - break; - case NV4_CIO_CRE_PAGE1_INDEX: - nv4->nvbase.cio_write_bank = val; - if (nv4->nvbase.svga.chain4) - nv4->nvbase.svga.write_bank = nv4->nvbase.cio_write_bank << 15; - else - nv4->nvbase.svga.write_bank = nv4->nvbase.cio_write_bank << 13; - break; - case NV4_CIO_CRE_RMA_INDEX: - nv4->pbus.rma.mode = val & NV4_PRMIO_RMA_MODE_MAX; - break; - /* Handle some large screen stuff */ - case NV4_CIO_CRE_PIXEL_INDEX: - if (val & 1 << (NV4_CIO_CRE_LSR_VDT_10)) - nv4->nvbase.svga.vtotal += 0x400; - if (val & 1 << (NV4_CIO_CRE_LSR_VRS_10)) - nv4->nvbase.svga.vblankstart += 0x400; - if (val & 1 << (NV4_CIO_CRE_LSR_VBS_10)) - nv4->nvbase.svga.vsyncstart += 0x400; - if (val & 1 << (NV4_CIO_CRE_LSR_HBE_6)) - nv4->nvbase.svga.hdisp += 0x400; - - /* Make sure dispend and vblankstart are right if we are displaying above 1024 vert */ - if (nv4->nvbase.svga.crtc[NV4_CIO_CRE_PIXEL_INDEX] & 1 << (NV4_CIO_CRE_LSR_VDE_10)) - nv4->nvbase.svga.dispend += 0x400; - - break; - case NV4_CIO_CRE_HEB_INDEX: // large screen bit - if (val & 0x01) - nv4->nvbase.svga.hdisp += 0x100; - break; - case NV4_CIO_CRE_DDC_WR_INDEX: - { - uint8_t scl = !!(val & 0x20); - uint8_t sda = !!(val & 0x10); - // Set an I2C GPIO register - i2c_gpio_set(nv4->nvbase.i2c, scl, sda); - break; - } - /* [6:0] contains cursorAddr [23:17] */ - case NV4_CIO_CRE_HCUR_ADDR0_INDEX: - nv4->pramdac.cursor_address |= (val & 0x7F) << 13; //bit7 technically ignored, but nv don't care, so neither do we - break; - /* [7:2] contains cursorAddr [16:11] */ - case NV4_CIO_CRE_HCUR_ADDR1_INDEX: - nv4->pramdac.cursor_address |= (val & 0xFC) << 5; // bit0 and 1 aren't part of the address - break; - - - } - - /* Recalculate the timings if we actually changed them - Additionally only do it if the value actually changed*/ - if (old_val != val) - { - // Thx to Fuel who basically wrote most of the SVGA compatibility code already (although I fixed some issues), because VGA is boring - // and in the words of an ex-Rendition/3dfx/NVIDIA engineer, "VGA was basically an undocumented bundle of steaming you-know-what. - // And it was essential that any cores the PC 3D startups acquired had to work with all the undocumented modes and timing tweaks (mode X, etc.)" - if (nv4->nvbase.svga.crtcreg < 0xE - || nv4->nvbase.svga.crtcreg > 0x10) - { - nv4->nvbase.svga.fullchange = changeframecount; - nv4_recalc_timings(&nv4->nvbase.svga); - } - } - - break; - default: - svga_out(addr, val, &nv4->nvbase.svga); - break; - } - -} - -/* DFB, sets up a dumb framebuffer */ -uint8_t nv4_dfb_read8(uint32_t addr, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - return nv4->nvbase.svga.vram[addr]; -} - -uint16_t nv4_dfb_read16(uint32_t addr, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - return (nv4->nvbase.svga.vram[addr + 1] << 8) | nv4->nvbase.svga.vram[addr]; -} - -uint32_t nv4_dfb_read32(uint32_t addr, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - return (nv4->nvbase.svga.vram[addr + 3] << 24) | (nv4->nvbase.svga.vram[addr + 2] << 16) + - (nv4->nvbase.svga.vram[addr + 1] << 8) | nv4->nvbase.svga.vram[addr]; -} - -void nv4_dfb_write8(uint32_t addr, uint8_t val, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - nv4->nvbase.svga.vram[addr] = val; - nv4->nvbase.svga.changedvram[addr >> 12] = val; - //nv4_render_current_bpp_dfb_8(addr); -} - -void nv4_dfb_write16(uint32_t addr, uint16_t val, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - nv4->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; - nv4->nvbase.svga.vram[addr] = (val) & 0xFF; - nv4->nvbase.svga.changedvram[addr >> 12] = val; - - //nv4_render_current_bpp_dfb_16(addr); - -} - -void nv4_dfb_write32(uint32_t addr, uint32_t val, void* priv) -{ - addr &= (nv4->nvbase.svga.vram_mask); - nv4->nvbase.svga.vram[addr + 3] = (val >> 24) & 0xFF; - nv4->nvbase.svga.vram[addr + 2] = (val >> 16) & 0xFF; - nv4->nvbase.svga.vram[addr + 1] = (val >> 8) & 0xFF; - nv4->nvbase.svga.vram[addr] = (val) & 0xFF; - nv4->nvbase.svga.changedvram[addr >> 12] = val; - - //removed until there is a render pipeline - //nv4_render_current_bpp_dfb_32(addr); - -} - -/* Cursor shit */ -void nv4_draw_cursor(svga_t* svga, int32_t drawline) -{ - // sanity check - if (!nv4) - return; - - /* - Commented out until we have a real graphics pipeline going here - - // if cursor disabled is set, return - if ((nv4->nvbase.svga.crtc[NV4_CRTC_REGISTER_CURSOR_START] >> NV4_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01) - return; - - // NT GDI drivers: Load cursor using NV_IMAGE_FROM_MEMORY ("NV4LCD") - // 9x GDI drivers: Use H/W cursor in RAMIN - - // Do we need to emulate it? - - // THIS IS CORRECT. BUT HOW DO WE FIND IT? - uint32_t ramin_cursor_position = NV4_RAMIN_OFFSET_CURSOR; - - /* let's just assume buffer 0 here...that code needs to be totally rewritten - nv4_coord_16_t start_position = nv4->pramdac.cursor_start; - - /* refuse to draw if thge cursor is offscreen - if (start_position.x >= nv4->nvbase.svga.hdisp - || start_position.y >= nv4->nvbase.svga.dispend) - { - return; - } - - nv_log("nv4_draw_cursor start=0x%04x,0x%04x", start_position.x, start_position.y); - - uint32_t final_position = nv4_render_get_vram_address_for_buffer(start_position, 0); - - uint16_t* vram_16 = (uint16_t*)nv4->nvbase.svga.vram; - uint32_t* vram_32 = (uint32_t*)nv4->nvbase.svga.vram; - - /* - We have to get a 32x32, "A"1R5G5B5-format cursor - out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1 - - These are expanded to RGB10 only if they are XORed. We don't do this (we don't really need to + there is no grobj specified here so special casing - would be needed) so we just xor it with the current pixel format - - for (int32_t y = 0; y < NV4_PRAMDAC_CURSOR_SIZE_Y; y++) - { - for (int32_t x = 0; x < NV4_PRAMDAC_CURSOR_SIZE_X; x++) - { - uint16_t current_pixel = nv4_ramin_read16(ramin_cursor_position, nv4); - - // 0000 = transparent, so skip drawing - if (current_pixel) - { - bool replace_bit = (current_pixel & 0x8000); - - // use buffer 0 BPIXEL - uint32_t bpixel_format = (nv4->pgraph.bpixel[0]) & 0x03; - - switch (bpixel_format) - { - case bpixel_fmt_8bit: - if (replace_bit) - nv4->nvbase.svga.vram[final_position] = current_pixel; - else //xor - { - // not sure what to do here. we'd have to search through the palette to find the closest possible colour. - uint8_t final = current_pixel ^ nv4->nvbase.svga.vram[final_position]; - nv4->nvbase.svga.vram[final_position] = final; - } - case bpixel_fmt_16bit: // easy case (our cursor is 15bpp format) - uint32_t index_16 = final_position >> 1; - - if (replace_bit) // just replace - vram_16[index_16] = current_pixel; - else // xor - { - current_pixel &= ~0x8000; // mask off the xor bit - uint16_t final = current_pixel ^ vram_16[index_16]; - vram_16[index_16] = final; - } - case bpixel_fmt_32bit: - uint32_t index_32 = final_position >> 2; - - if (replace_bit) // just replace - vram_32[index_32] = nv4->nvbase.svga.conv_16to32(&nv4->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here - else //xor - { - current_pixel &= ~0x8000; // mask off the xor bit - uint32_t current_pixel_32 = nv4->nvbase.svga.conv_16to32(&nv4->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here - - uint32_t final = current_pixel_32 ^ vram_32[index_32]; - vram_32[index_32] = final; - } - break; - } - } - - // increment vram position - ramin_cursor_position += 2; - - // go - switch (nv4->nvbase.svga.bpp) - { - case 8: - final_position++; - case 15 ... 16: - final_position += 2; - break; - case 32: - final_position += 4; - break; - } - - start_position.x++; - } - - - start_position.y++; - start_position.x = nv4->pramdac.cursor_start.x; - - // reset at the end of each line so we "jump" to the start x - final_position = nv4_render_get_vram_address_for_buffer(start_position, 0); - }*/ -} - -// MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS. -// Note this area is 64kb and the vbios is only 32kb. See below.. - -uint8_t nv4_prom_read(uint32_t address) -{ - // prom area is 64k, so... - // first see if we even have a rom of 64kb in size - uint32_t max_rom_size = NV4_PROM_END - NV4_PROM_START; - uint32_t real_rom_size = max_rom_size; - - // set it - if (nv4->nvbase.vbios.sz < max_rom_size) - real_rom_size = nv4->nvbase.vbios.sz; - - //get our real address - uint8_t rom_address = address & max_rom_size; - - // Does this mirror on real hardware? - if (rom_address >= real_rom_size) - { - nv_log("PROM VBIOS Read to INVALID address 0x%05x, returning 0xFF", rom_address); - return 0xFF; - } - else - { - uint8_t val = nv4->nvbase.vbios.rom[rom_address]; - nv_log("PROM VBIOS Read 0x%05x <- 0x%05x", val, rom_address); - return val; - } -} - -void nv4_prom_write(uint32_t address, uint32_t value) -{ - uint32_t real_addr = address & 0x1FFFF; - nv_log("What's going on here? Tried to write to the Video BIOS ROM? (Address=0x%05x, value=0x%02x)", real_addr, value); -} diff --git a/src/video/nv/nv4/nv4_debug_register_list.c b/src/video/nv/nv4/nv4_debug_register_list.c deleted file mode 100644 index 32516be0c..000000000 --- a/src/video/nv/nv4/nv4_debug_register_list.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. - * - * This file is part of the 86Box distribution. - * - * NV4 debug register list - * - * - * Authors: Connor Hyde, I need a better email address ;^) - * - * Copyright 2024-2025 starfrost - */ -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/io.h> -#include <86box/pci.h> -#include <86box/rom.h> -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - -#ifdef NV_LOG - -nv_register_t nv4_registers[] = { - { NV4_PTIMER_INTR, "NV4 PTIMER - Interrupt Status", NULL, NULL}, - { NV4_PTIMER_INTR_EN, "NV4 PTIMER - Interrupt Enable", NULL, NULL,}, - { NV4_PTIMER_NUMERATOR, "NV4 PTIMER - Numerator", NULL, NULL, }, - { NV4_PTIMER_DENOMINATOR, "NV4 PTIMER - Denominator", NULL, NULL, }, - { NV4_PTIMER_TIME_0_NSEC, "NV4 PTIMER - Time0", NULL, NULL, }, - { NV4_PTIMER_TIME_1_NSEC, "NV4 PTIMER - Time1", NULL, NULL, }, - { NV4_PTIMER_ALARM_NSEC, "NV4 PTIMER - Alarm", NULL, NULL, }, - { NV4_PRAMDAC_VPLL_COEFF, "NV4 PRAMDAC - Pixel Clock Coefficient", NULL, NULL, }, - { NV4_PRAMDAC_NVPLL_COEFF, "NV4 PRAMDAC - GPU Core Clock Coefficient", NULL, NULL, }, - { NV4_PRAMDAC_MPLL_COEFF, "NV4 PRAMDAC - VRAM Clock Coefficient", NULL, NULL, }, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -#endif \ No newline at end of file diff --git a/src/video/nv/nv4/subsystems/nv4_pramdac.c b/src/video/nv/nv4/subsystems/nv4_pramdac.c deleted file mode 100644 index 0b8292796..000000000 --- a/src/video/nv/nv4/subsystems/nv4_pramdac.c +++ /dev/null @@ -1,84 +0,0 @@ -/* -* 86Box A hypervisor and IBM PC system emulator that specializes in -* running old operating systems and software designed for IBM -* PC systems and compatibles from 1981 through fairly recent -* system designs based on the PCI bus. -* -* This file is part of the 86Box distribution. -* -* NV4/Riva TNT - RAMDAC -* -* -* Authors: Connor Hyde, I need a better email address ;^) -* -* Copyright 2024-2025 starfrost -*/ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - - -uint64_t nv4_pramdac_get_hz(uint32_t coeff, bool apply_divider) -{ - uint32_t m = coeff & 0xFF; - uint32_t n = (coeff >> 8) & 0xFF; - uint32_t p = (coeff >> 16) & 0x07; - - // Check clock base - uint32_t hz_base = (nv4->straps & (1 << NV4_STRAP_CRYSTAL)) ? 14318180 : 13500000; - uint32_t final_hz = (hz_base * n) / (m << p); - - // Check VCLK divider - - if (apply_divider) - { - if (nv4->pramdac.clk_coeff_select & (1 << NV4_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO)) - final_hz >>= 1; - } - - return final_hz; -} - -void nv4_pramdac_set_vclk() -{ - uint64_t final_hz = nv4_pramdac_get_hz(nv4->pramdac.nvclk, false); - - //TODO: Everything - if (nv4->nvbase.nv4_vclk_timer) - timer_set_delay_u64(nv4->nvbase.nv4_vclk_timer, final_hz / TIMER_USEC); - -} - -uint32_t nv4_pramdac_read(uint32_t address) -{ - uint32_t ret = 0x00; - - switch (address) - { - case NV4_PRAMDAC_VPLL_COEFF: // Pixel clock - ret = nv4->pramdac.vclk; - break; - case NV4_PRAMDAC_NVPLL_COEFF: // System clock - ret = nv4->pramdac.nvclk; - break; - case NV4_PRAMDAC_MPLL_COEFF: // Memory clock - ret = nv4->pramdac.mclk; - break; - - } -} - -void nv4_pramdac_write(uint32_t address, uint32_t data) -{ - -} diff --git a/src/video/nv/nv4/subsystems/nv4_ptimer.c b/src/video/nv/nv4/subsystems/nv4_ptimer.c deleted file mode 100644 index 29df0168b..000000000 --- a/src/video/nv/nv4/subsystems/nv4_ptimer.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 86Box A hypervisor and IBM PC system emulator that specializes in - * running old operating systems and software designed for IBM - * PC systems and compatibles from 1981 through fairly recent - * system designs based on the PCI bus. - * - * This file is part of the 86Box distribution. - * - * Nvidia RIVA TNT (NV4 architecture) - Timer emulation - * - * - * - * Authors: Connor Hyde, - * - * Copyright 2024-2025 starfrost - */ - -#include -#include -#include -#include -#include <86box/86box.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/pci.h> -#include <86box/rom.h> // DEPENDENT!!! -#include <86box/video.h> -#include <86box/nv/vid_nv.h> -#include <86box/nv/vid_nv4.h> - -nv_register_t ptimer_registers[] = { - { NV4_PTIMER_INTR, "NV4 PTIMER - Interrupt Status", NULL, NULL}, - { NV4_PTIMER_INTR_EN, "NV4 PTIMER - Interrupt Enable", NULL, NULL,}, - { NV4_PTIMER_NUMERATOR, "NV4 PTIMER - Numerator", NULL, NULL, }, - { NV4_PTIMER_DENOMINATOR, "NV4 PTIMER - Denominator", NULL, NULL, }, - { NV4_PTIMER_TIME_0_NSEC, "NV4 PTIMER - Time0", NULL, NULL, }, - { NV4_PTIMER_TIME_1_NSEC, "NV4 PTIMER - Time1", NULL, NULL, }, - { NV4_PTIMER_ALARM_NSEC, "NV4 PTIMER - Alarm", NULL, NULL, }, - { NV_REG_LIST_END, NULL, NULL, NULL}, // sentinel value -}; - -// ptimer init code -void nv4_ptimer_init(void) -{ - nv_log("Initialising PTIMER..."); - - nv_log("Done!\n"); -} - -// Handles the PTIMER alarm interrupt -void nv4_ptimer_interrupt(uint32_t num) -{ - nv4->ptimer.interrupt_status |= (1 << num); - - //todo - //nv4_pmc_handle_interrupts(true); -} - -// Ticks the timer. -void nv4_ptimer_tick(double real_time) -{ - // prevent a divide by zero - if (nv4->ptimer.clock_numerator == 0 - || nv4->ptimer.clock_denominator == 0) - return; - - // get the current time - - // See Envytools. We need to use the frequency as a source. - // We need to figure out how many cycles actually occurred because this counts up every cycle... - // However it seems that their formula is wrong. I can't be bothered to figure out what's going on and, based on documentation from NVIDIA, - // timer_0 is meant to roll over every 4 seconds. Multiplying by 10 basically does the job. - - // Convert to microseconds - double freq_base = (real_time / 1000000.0f) / ((double)1.0 / nv4->nvbase.memory_clock_frequency) * 10.0f; - double current_time = freq_base * ((double)nv4->ptimer.clock_numerator) / (double)nv4->ptimer.clock_denominator; // *10.0? - - // truncate it - nv4->ptimer.time += (uint64_t)current_time; - - // Check if the alarm has actually triggered.. - // Only log on ptimer alarm. Otherwise, it's too much spam. - if (nv4->ptimer.time >= nv4->ptimer.alarm) - { - nv_log_verbose_only("PTIMER alarm interrupt fired (if interrupts enabled) because we reached TIME value 0x%08x\n", nv4->ptimer.alarm); - nv4_ptimer_interrupt(NV4_PTIMER_INTR_ALARM); - } -} - -uint32_t nv4_ptimer_read(uint32_t address) -{ - // always enabled - - nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); - - // Only log these when tehy actually tick - if (address != NV4_PTIMER_TIME_0_NSEC - && address != NV4_PTIMER_TIME_1_NSEC) - { - nv_log_verbose_only("PTIMER Read from 0x%08x", address); - } - - uint32_t ret = 0x00; - - // if the register actually exists - if (reg) - { - // on-read function - if (reg->on_read) - ret = reg->on_read(); - else - { - // Interrupt state: - // Bit 0: Alarm - - switch (reg->address) - { - case NV4_PTIMER_INTR: - ret = nv4->ptimer.interrupt_status; - break; - case NV4_PTIMER_INTR_EN: - ret = nv4->ptimer.interrupt_enable; - break; - case NV4_PTIMER_NUMERATOR: - ret = nv4->ptimer.clock_numerator; // 15:0 - break; - case NV4_PTIMER_DENOMINATOR: - ret = nv4->ptimer.clock_denominator ; //15:0 - break; - // 64-bit value - // High part - case NV4_PTIMER_TIME_0: - ret = nv4->ptimer.time & 0xFFFFFFFF; //28:0 - break; - // Low part - case NV4_PTIMER_TIME_1: - ret = nv4->ptimer.time >> 32; // 31:5 - break; - case NV4_PTIMER_ALARM: - ret = nv4->ptimer.alarm; // 31:5 - break; - } - - } - //TIME0 and TIME1 produce too much log spam that slows everything down - if (reg->address != NV4_PTIMER_TIME_0_NSEC - && reg->address != NV4_PTIMER_TIME_1_NSEC) - { - if (reg->friendly_name) - nv_log_verbose_only(": 0x%08x <- %s\n", ret, reg->friendly_name); - else - nv_log_verbose_only("\n"); - } - } - else - { - nv_log(": Unknown register read (address=0x%08x), returning 0x00\n", address); - } - - return ret; -} - -void nv4_ptimer_write(uint32_t address, uint32_t value) -{ - // before doing anything, check the subsystem enablement - nv_register_t* reg = nv_get_register(address, ptimer_registers, sizeof(ptimer_registers)/sizeof(ptimer_registers[0])); - - nv_log_verbose_only("PTIMER Write 0x%08x -> 0x%08x", value, address); - - // if the register actually exists - if (reg) - { - if (reg->friendly_name) - nv_log_verbose_only(": %s\n", reg->friendly_name); - else - nv_log_verbose_only("\n"); - - // on-read function - if (reg->on_write) - reg->on_write(value); - else - { - switch (reg->address) - { - // Interrupt state: - // Bit 0 - Alarm - - case NV4_PTIMER_INTR: - nv4->ptimer.interrupt_status &= ~value; - //TODO nv4_pmc_clear_interrupts(); - break; - - // Interrupt enablement state - case NV4_PTIMER_INTR_EN: - nv4->ptimer.interrupt_enable = value & 0x1; - break; - // nUMERATOR - case NV4_PTIMER_NUMERATOR: - nv4->ptimer.clock_numerator = value & 0xFFFF; // 15:0 - break; - case NV4_PTIMER_DENOMINATOR: - // prevent Div0 - if (!value) - value = 1; - - nv4->ptimer.clock_denominator = value & 0xFFFF; //15:0 - break; - // 64-bit value - // High part - case NV4_PTIMER_TIME_0: - nv4->ptimer.time |= (value) & 0xFFFFFFE0; //28:0 - break; - // Low part - case NV4_PTIMER_TIME_1: - nv4->ptimer.time |= ((uint64_t)(value & 0xFFFFFFE0) << 32); // 31:5 - break; - case NV4_PTIMER_ALARM: - nv4->ptimer.alarm = value & 0xFFFFFFE0; // 31:5 - break; - } - } - } - else /* Completely unknown */ - { - nv_log(": Unknown register write (address=0x%08x)\n", address); - } -} \ No newline at end of file diff --git a/src/video/nv/nv_base.c b/src/video/nv/nv_base.c deleted file mode 100644 index c7a83d5ad..000000000 --- a/src/video/nv/nv_base.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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-2025 starfrost - */ - -// Common NV1/3/4... init -#define HAVE_STDARG_H // wtf is this crap -#include -#include -#include -#include -#include <86box/86box.h> -#ifndef RELEASE_BUILD -#include <86box/device.h> -#endif -#include <86box/log.h> - - -// Common logging -#ifdef ENABLE_NV_LOG -int nv_do_log = ENABLE_NV_LOG; - -// A bit of kludge so that in the future we can abstract this function acorss multiple generations of Nvidia GPUs -void* nv_log_device; -bool nv_log_full = false; - -void nv_log_set_device(void* device) -{ - // in case the cyclical logger doesn't show you the full context of what went on, you can enable this debug feature - #ifndef RELEASE_BUILD - if (device - && device_get_config_int("nv_debug_fulllog")) - { - nv_log_full = true; - } - #endif - - nv_log_device = device; -} - -void nv_log_internal(const char* fmt, va_list arg) -{ - if (!nv_log_device) - return; - - // If our debug config option is configured, full log. Otherwise log with cyclical detection. - if (nv_log_full) - log_out(nv_log_device, fmt, arg); - else - log_out_cyclic(nv_log_device, fmt, arg); - -} - -void nv_log(const char *fmt, ...) -{ - va_list arg; - - if (!nv_do_log) - return; - - va_start(arg, fmt); - nv_log_internal(fmt, arg); - va_end(arg); -} - -void nv_log_verbose_only(const char *fmt, ...) -{ - #ifdef ENABLE_NV_LOG_ULTRA - va_list arg; - - if (!nv_do_log) - return; - - va_start(arg, fmt); - nv_log_internal(fmt, arg); - va_end(arg); - #endif -} - -#else -void nv_log(const char *fmt, ...) -{ - -} - -void nv_log_verbose_only(const char *fmt, ...) -{ - -} - -void nv_log_set_device(void* device) -{ - -} -#endif \ No newline at end of file diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 3ea8a0358..4e76e150d 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -262,13 +262,6 @@ video_cards[] = { { .device = &compaq_voodoo_3_3500_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &voodoo_3_3500_se_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &voodoo_3_3500_si_agp_device, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv1_device_edge2k, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv1_device_edge3k, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv3_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv3_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv3t_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv3t_device_pci, .flags = VIDEO_FLAG_TYPE_NONE }, - { .device = &nv4_device_agp, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = NULL, .flags = VIDEO_FLAG_TYPE_NONE } // clang-format on }; diff --git a/src/video/vid_voodoo.c b/src/video/vid_voodoo.c index 7ee3d9ae7..fe02b7811 100644 --- a/src/video/vid_voodoo.c +++ b/src/video/vid_voodoo.c @@ -675,7 +675,7 @@ voodoo_writel(uint32_t addr, uint32_t val, void *priv) default: if (voodoo->fbiInit7 & FBIINIT7_CMDFIFO_ENABLE) { - voodoo_log(": Unknown register write in CMDFIFO mode %08x %08x\n", addr, val); + voodoo_log("Unknown register write in CMDFIFO mode %08x %08x\n", addr, val); } else { voodoo_queue_command(voodoo, addr | FIFO_WRITEL_REG, val); } From c1235e9ee7101d35a4259abd0a889118dfd4fa6b Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 01:08:51 +0200 Subject: [PATCH 246/274] RTL8019AS: Re-add the commented out CONFIG1 stuff, fixes PNPODI.COM. --- src/network/net_ne2000.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 2a9f3f551..5bb5fe7ad 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -583,11 +583,9 @@ static void nic_ioremove(nic_t *dev, uint16_t addr); static void nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) { -#if 0 uint8_t irq_map[16] = { 0x00, 0x00, 0x00, 0x10, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x60, 0x00, 0x00, 0x70 }; uint8_t ios = 0x00; -#endif if (ld) return; @@ -603,22 +601,18 @@ nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) nic_interrupt(dev, 0); dev->base_irq = config->irq[0].irq; -#if 0 if ((dev->base_irq >= 0x00) && (dev->base_irq <= 0x0f)) dev->config1 = (dev->config1 & 0x8f) | irq_map[dev->base_irq]; else dev->config1 = (dev->config1 & 0x8f); -#endif if (config->activate && (dev->base_address != ISAPNP_IO_DISABLED)) { nic_ioset(dev, dev->base_address); -#if 0 ios |= (dev->base_address & 0x0100) ? 0x00 : 0x04; ios |= (dev->base_address & 0x0080) ? 0x08 : 0x00; ios |= (dev->base_address & 0x0040) ? 0x02 : 0x00; ios |= (dev->base_address & 0x0020) ? 0x01 : 0x00; dev->config1 = (dev->config1 & 0xf0) | ios; -#endif } } From 58e397247d866d19b5b93990d48fc083b6fa6b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miran=20Gr=C4=8Da?= Date: Thu, 25 Sep 2025 01:21:42 +0200 Subject: [PATCH 247/274] QT Renderer: current->setAttribute(Qt::WA_AlwaysStackOnTop); --- src/qt/qt_rendererstack.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 32b017b92..4a2e39bbe 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -441,6 +441,7 @@ RendererStack::createRenderer(Renderer renderer) current->setFocusProxy(this); current->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); current->setStyleSheet("background-color: black"); + current->setAttribute(Qt::WA_AlwaysStackOnTop); addWidget(current.get()); this->setStyleSheet("background-color: black"); From 4a84c0012c5290fa4f6914de539df42407c9aec8 Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 03:03:58 +0200 Subject: [PATCH 248/274] ISA PnP: Improve I/O range check handling code. --- src/device/isapnp.c | 79 +++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 46cd52c24..d492957ed 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -465,9 +465,40 @@ isapnp_write_addr(UNUSED(uint16_t addr), uint8_t val, void *priv) } static void -isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg, uint8_t val) +isapnp_ld_io_remove(isapnp_device_t *ld) { uint16_t io_addr; + + for (uint8_t i = 0; i < 8; i++) { + if (!ld->io_len[i]) + continue; + + io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; + + io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); + } +} + +static void +isapnp_ld_io_set(isapnp_device_t *ld) +{ + uint16_t io_addr; + + for (uint8_t i = 0; i < 8; i++) { + if (!ld->io_len[i]) + continue; + + io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; + int ior = !(ld->regs[0x30] & 0x01) && (ld->regs[0x31] & 0x02); + + if (ior) + io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); + } +} + +static void +isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uint8_t reg, uint8_t val) +{ uint16_t reset_cards = 0; isapnp_log("ISAPnP: write_common(%02X, %02X)\n", reg, val); @@ -591,46 +622,22 @@ isapnp_write_common(isapnp_t *dev, isapnp_card_t *card, isapnp_device_t *ld, uin break; case 0x30: /* Activate */ - CHECK_CURRENT_LD(); - - isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (val & 0x01) ? "A" : "Dea", card->csn, ld->number); - - ld->regs[reg] = val & 0x01; - isapnp_device_config_changed(card, ld); - - for (uint8_t i = 0; i < 8; i++) { - if (!ld->io_len[i]) - continue; - - io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; - if (val & 0x01) { - if (ld->regs[0x31] & 0x02) - io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); - } else { - if (ld->regs[0x31] & 0x02) - io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); - } - } - - break; - case 0x31: /* I/O Range Check */ CHECK_CURRENT_LD(); - for (uint8_t i = 0; i < 8; i++) { - if (!ld->io_len[i]) - continue; + isapnp_log("ISAPnP: %sctivate CSN %02X device %02X\n", (ld->regs[0x30] & 0x01) ? "A" : "Dea", + card->csn, ld->number); - io_addr = (ld->regs[0x60 + (2 * i)] << 8) | ld->regs[0x61 + (2 * i)]; - if (ld->regs[reg] & 0x02) - io_removehandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); - if ((val & 0x02) && !(ld->regs[0x30] & 0x01)) - io_sethandler(io_addr, ld->io_len[i], isapnp_read_rangecheck, NULL, NULL, NULL, NULL, NULL, ld); - } + isapnp_ld_io_remove(ld); + + if (reg == 0x30) + ld->regs[reg] = val & 0x01; + else + ld->regs[reg] = val & 0x03; + + isapnp_ld_io_set(ld); - ld->regs[reg] = val & 0x03; isapnp_device_config_changed(card, ld); - break; case 0x20 ... 0x2f: @@ -696,7 +703,9 @@ vendor_defined: break; } + isapnp_ld_io_remove(ld); ld->regs[reg] = val; + isapnp_ld_io_set(ld); isapnp_device_config_changed(card, ld); } break; From f0b20fddd5bee939cfc094269ec381a49ce70995 Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 03:36:09 +0200 Subject: [PATCH 249/274] RTL8019AS: Fix base address when active at initialization. --- src/network/net_ne2000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 5bb5fe7ad..25ba6a17a 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -1360,15 +1360,15 @@ nic_init(const device_t *info) if (!(dev->config3 & 0x01)) { uint8_t irq_map[8] = { 9, 3, 4, 5, 10, 11, 12, 15 }; - dev->base_address = 0x0000; - dev->base_irq = irq_map[(dev->config1 >> 4) & 0x07]; - - dev->base_address = (dev->config1 & 0x01) ? 0x0020 : 0x0000; + dev->base_address = 0x0200; + dev->base_address |= (dev->config1 & 0x01) ? 0x0020 : 0x0000; dev->base_address |= (dev->config1 & 0x02) ? 0x0040 : 0x0000; dev->base_address |= (dev->config1 & 0x04) ? 0x0000 : 0x0100; dev->base_address |= (dev->config1 & 0x08) ? 0x0080 : 0x0000; + dev->base_irq = irq_map[(dev->config1 >> 4) & 0x07]; + nic_ioset(dev, dev->base_address); isapnp_activate(dev->pnp_card, dev->base_address, dev->base_irq); From e7645e5c39135eecbf08c1440033d9c30fc2fb2a Mon Sep 17 00:00:00 2001 From: OBattler Date: Thu, 25 Sep 2025 05:45:14 +0200 Subject: [PATCH 250/274] ISA PnP and RTL8019AS: More fixes - fixes I/O range check errors in jumperless mode. --- src/device/isapnp.c | 25 ++++++++++++++++++++----- src/network/net_ne2000.c | 27 +++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index d492957ed..bc022ec43 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -77,6 +77,7 @@ enum { typedef struct _isapnp_device_ { uint8_t number; + uint8_t defs[256]; uint8_t regs[256]; uint8_t mem_upperlimit; uint8_t irq_types; @@ -278,6 +279,18 @@ isapnp_reset_ld_regs(isapnp_device_t *ld) /* Reset configuration registers to match the default configuration. */ isapnp_reset_ld_config(ld); + + if (ld->defs[0x30] != 0x00) + ld->regs[0x30] = ld->defs[0x30]; + + if (ld->defs[0x60] != 0x00) + ld->regs[0x60] = ld->defs[0x60]; + + if (ld->defs[0x61] != 0x00) + ld->regs[0x61] = ld->defs[0x61]; + + if (ld->defs[0x70] != 0x00) + ld->regs[0x70] = ld->defs[0x70]; } static uint8_t @@ -1251,12 +1264,14 @@ isapnp_activate(void *priv, uint16_t base, uint8_t irq) } if (ld != NULL) { - ld->regs[0x30] = 0x01; - ld->regs[0x60] = base >> 4; + ld->defs[0x30] = 0x01; + ld->defs[0x60] = base >> 8; if (!(ld->io_16bit & (1 << ((0x60 >> 1) & 0x07)))) - ld->regs[0x60] &= 0x03; - ld->regs[0x61] = base & 0x0f; - ld->regs[0x70] = irq; + ld->defs[0x60] &= 0x03; + ld->defs[0x61] = base & 0xff; + ld->defs[0x70] = irq; + + isapnp_reset_ld_regs(ld); } } diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 25ba6a17a..8f45c6dc1 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -172,6 +172,19 @@ nic_interrupt(void *priv, int set) } } +static void +nic_config_reset(void *priv) +{ + nic_t *dev = (nic_t *) priv; + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); + + dev->config1 = (data[0x00] & 0x7f) | 0x80; + dev->config2 = (data[0x01] & 0xdf); + dev->config3 = (data[0x02] & 0xf7); + + isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); +} + /* reset - restore state to power-up, cancelling all i/o */ static void nic_reset(void *priv) @@ -181,6 +194,9 @@ nic_reset(void *priv) nelog(1, "%s: reset\n", dev->name); dp8390_reset(dev->dp8390); + + if (dev->board >= NE2K_RTL8019AS_PNP) + nic_config_reset(priv); } static void @@ -189,6 +205,9 @@ nic_soft_reset(void *priv) nic_t *dev = (nic_t *) priv; dp8390_soft_reset(dev->dp8390); + + if (dev->board >= NE2K_RTL8019AS_PNP) + nic_config_reset(priv); } /* @@ -423,7 +442,7 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) dev->config1 = (data[0x00] & 0x7f) | 0x80; dev->config2 = (data[0x01] & 0xdf); - dev->config3 = (data[0x02] & 0x77) | 0x80; + dev->config3 = (data[0x02] & 0xf7); dev->_9346cr = 0x21; isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); @@ -1957,7 +1976,7 @@ const device_t rtl8019as_pnp_device = { .local = NE2K_RTL8019AS_PNP, .init = nic_init, .close = nic_close, - .reset = NULL, + .reset = nic_config_reset, .available = rtl8019as_available, .speed_changed = NULL, .force_redraw = NULL, @@ -1971,7 +1990,7 @@ const device_t de220p_device = { .local = NE2K_DE220P, .init = nic_init, .close = nic_close, - .reset = NULL, + .reset = nic_config_reset, .available = de220p_available, .speed_changed = NULL, .force_redraw = NULL, @@ -1985,7 +2004,7 @@ const device_t rtl8029as_device = { .local = NE2K_RTL8029AS, .init = nic_init, .close = nic_close, - .reset = NULL, + .reset = nic_config_reset, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, From 6cd9395d2c5277b90a146bf4961f1fb678305467 Mon Sep 17 00:00:00 2001 From: Toni Riikonen Date: Thu, 25 Sep 2025 20:23:08 +0300 Subject: [PATCH 251/274] Fixes merge problem --- src/floppy/fdd_audio.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 84fcee5a6..04ae9ba68 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -180,30 +180,13 @@ load_wav(const char *filename, int *sample_count) if ((filename == NULL) || (strlen(filename) == 0)) return NULL; - } if (strstr(filename, "..") != NULL) return NULL; - } f = rom_fopen(filename, "rb"); if (f == NULL) return NULL; - } - } - - path_append_filename(full_path, exe_path, "roms"); - path_append_filename(full_path, full_path, "samples"); - path_append_filename(full_path, full_path, "fdd"); - path_append_filename(full_path, full_path, filename); - f = fopen(full_path, "rb"); - if (!f) { - path_append_filename(full_path, full_path, "samples"); - path_append_filename(full_path, exe_path, filename); - f = fopen(full_path, "rb"); - if (!f) - return NULL; - } wav_header_t hdr; if (fread(&hdr, sizeof(hdr), 1, f) != 1) { From 9e1ec19860c4b93a69f8bcf51aa1719b1e15fb7d Mon Sep 17 00:00:00 2001 From: Alexander Babikov Date: Thu, 25 Sep 2025 22:54:16 +0500 Subject: [PATCH 252/274] Manager: Indicate Voodoo SLI in the details pane (#6223) --- src/qt/qt_vmmanager_system.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/qt/qt_vmmanager_system.cpp b/src/qt/qt_vmmanager_system.cpp index 988d3ed5e..dec503fce 100644 --- a/src/qt/qt_vmmanager_system.cpp +++ b/src/qt/qt_vmmanager_system.cpp @@ -654,6 +654,9 @@ VMManagerSystem::setupVars() { voodoo_name = tr("3Dfx Voodoo 2"); break; } + + if (voodoo_config["sli"].toInt() == 1) + voodoo_name.append(" (SLI)"); } display_table[VMManager::Display::Name::Voodoo] = voodoo_name; From 5ac9d65a8bc7c582d5cf098aa5eda3380938a831 Mon Sep 17 00:00:00 2001 From: OBattler Date: Fri, 26 Sep 2025 01:14:07 +0200 Subject: [PATCH 253/274] RTL8029AS: Do not call isapnp_set_normal() in nic_config_reset() on this card, fixes segmentation fault. --- src/network/net_ne2000.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 8f45c6dc1..bb3d82ad5 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -92,11 +92,12 @@ typedef struct nic_t { uint8_t csnsav; /* RTL8019AS/RTL8029AS registers */ + uint8_t _9346cr; uint8_t config0; uint8_t config1; uint8_t config2; uint8_t config3; - uint8_t _9346cr; + uint8_t res; uint8_t pci_regs[PCI_REGSIZE]; @@ -176,13 +177,15 @@ static void nic_config_reset(void *priv) { nic_t *dev = (nic_t *) priv; + uint8_t *data = (uint8_t *) nmc93cxx_eeprom_data(dev->eeprom); dev->config1 = (data[0x00] & 0x7f) | 0x80; dev->config2 = (data[0x01] & 0xdf); dev->config3 = (data[0x02] & 0xf7); - isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); + if (dev->pnp_card != NULL) + isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); } /* reset - restore state to power-up, cancelling all i/o */ @@ -384,10 +387,20 @@ page3_read(nic_t *dev, uint32_t off, UNUSED(unsigned int len)) ret = (dev->config3 & 0x46); break; + case 0x7: /* Reserved, Do not write */ + if (dev->board == NE2K_RTL8019AS_PNP) + ret = dev->res; + break; + case 0x8: /* CSNSAV */ ret = ((dev->board == NE2K_RTL8019AS_PNP) ? *dev->pnp_csnsav : 0x00); break; + case 0x9: case 0xa: + case 0xc: + ret = 0xff; + break; + case 0xb: /* INTR */ if (dev->board == NE2K_RTL8019AS_PNP) { ret = (pic2.irr & 0x02) ? 0x01 : 0x00; @@ -411,11 +424,15 @@ page3_read(nic_t *dev, uint32_t off, UNUSED(unsigned int len)) case 0xe: /* 8029ASID0 */ if (dev->board == NE2K_RTL8029AS) ret = 0x29; + else + ret = 0xff; break; case 0xf: /* 8029ASID1 */ if (dev->board == NE2K_RTL8029AS) ret = 0x80; + else + ret = 0xff; break; } @@ -477,6 +494,11 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) } break; + case 0x7: /* Reserved, Do not write */ + if (dev->board == NE2K_RTL8019AS_PNP) + dev->res = val; + break; + case 0x09: /* HLTCLK */ break; From 086f955e7e468c33e210c6e901ac6d9d92a5d5fa Mon Sep 17 00:00:00 2001 From: OBattler Date: Fri, 26 Sep 2025 05:06:06 +0200 Subject: [PATCH 254/274] RTL8019AS: Implement active low IRQ mode, fixes RSET8019 IRQ detection. --- src/network/net_ne2000.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index bb3d82ad5..3b63c4db8 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -113,6 +113,7 @@ typedef struct nic_t { int is_mca; int is_8bit; int base_irq; + int irq_level; int has_bios; uint32_t base_address; @@ -154,6 +155,9 @@ nic_interrupt(void *priv, int set) nic_t *dev = (nic_t *) priv; int enabled = 1; + if (dev->irq_level) + set ^= 1; + if (dev->board == NE2K_RTL8019AS_PNP) enabled = dev->config1 & 0x80; @@ -183,6 +187,7 @@ nic_config_reset(void *priv) dev->config1 = (data[0x00] & 0x7f) | 0x80; dev->config2 = (data[0x01] & 0xdf); dev->config3 = (data[0x02] & 0xf7); + dev->irq_level = 0x02; if (dev->pnp_card != NULL) isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); @@ -200,6 +205,8 @@ nic_reset(void *priv) if (dev->board >= NE2K_RTL8019AS_PNP) nic_config_reset(priv); + else + dev->irq_level = 0x02; } static void @@ -211,6 +218,8 @@ nic_soft_reset(void *priv) if (dev->board >= NE2K_RTL8019AS_PNP) nic_config_reset(priv); + else + dev->irq_level = 0x02; } /* @@ -640,8 +649,10 @@ nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) dev->base_address = config->io[0].base; + dev->irq_level = 0x02; nic_interrupt(dev, 0); dev->base_irq = config->irq[0].irq; + dev->irq_level = config->irq[0].level; if ((dev->base_irq >= 0x00) && (dev->base_irq <= 0x0f)) dev->config1 = (dev->config1 & 0x8f) | irq_map[dev->base_irq]; else From 41809098406c5900724ffeb1124526862a00dff7 Mon Sep 17 00:00:00 2001 From: Verloren50000 <110334428+Verloren50000@users.noreply.github.com> Date: Fri, 26 Sep 2025 11:21:12 +0800 Subject: [PATCH 255/274] Add BIOS selector and the mid-1999 BIOS to the ABit AB-LX6 (#6224) * machine.h: Configuration added for ABit AB-LX6 * m_at_slot1.c: Configuration is almost there for ABit AB-LX6 * Congratulations! BIOS Selector for ABit AB-LX6 is complete. --- src/include/86box/machine.h | 3 +++ src/machine/m_at_slot1.c | 50 +++++++++++++++++++++++++++++++++---- src/machine/machine_table.c | 2 +- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 101ed2934..b88d8f492 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1117,6 +1117,9 @@ extern int machine_at_p65up5_cpknd_init(const machine_t *); extern int machine_at_kn97_init(const machine_t *); /* i440LX */ +#ifdef EMU_DEVICE_H +extern const device_t lx6_device; +#endif extern int machine_at_lx6_init(const machine_t *); extern int machine_at_optiplexgxa_init(const machine_t *); extern int machine_at_spitfire_init(const machine_t *); diff --git a/src/machine/m_at_slot1.c b/src/machine/m_at_slot1.c index 4c25799f6..22d6baac8 100644 --- a/src/machine/m_at_slot1.c +++ b/src/machine/m_at_slot1.c @@ -151,17 +151,57 @@ machine_at_kn97_init(const machine_t *model) } /* i440LX */ +static const device_config_t lx6_config[] = { + // clang-format off + { + .name = "bios", + .description = "BIOS Version", + .type = CONFIG_BIOS, + .default_string = "lx6", + .default_int = 0, + .file_filter = "", + .spinner = { 0 }, + .bios = { + { .name = "Award Modular BIOS v4.51PG - Revision LY", .internal_name = "lx6", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/lx6/LX6C_LY.BIN", "" } }, + { .name = "Award Modular BIOS v4.51PG - Revision PZ Beta", .internal_name = "lx6_beta", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 131072, .files = { "roms/machines/lx6/LX6C_PZ.B00", "" } }, + { .files_no = 0 } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } + // clang-format on +}; + +const device_t lx6_device = { + .name = "ABIT AB-LX6", + .internal_name = "lx6_device", + .flags = 0, + .local = 0, + .init = NULL, + .close = NULL, + .reset = NULL, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = lx6_config +}; + int machine_at_lx6_init(const machine_t *model) { - int ret; + int ret = 0; + const char* fn; - ret = bios_load_linear("roms/machines/lx6/LX6C_PZ.B00", - 0x000e0000, 131072, 0); - - if (bios_only || !ret) + /* No ROMs available */ + if (!device_available(model->device)) return ret; + device_context(model->device); + fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); + ret = bios_load_linear(fn, 0x000e0000, 131072, 0); + device_context_restore(); + machine_at_common_init_ex(model, 2); pci_init(PCI_CONFIG_TYPE_1); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index cbc48db1d..8c9055c9b 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -17217,7 +17217,7 @@ const machine_t machines[] = { .kbc_p1 = 0x00000cf0, .gpio = 0xffffffff, .gpio_acpi = 0xffffffff, - .device = NULL, + .device = &lx6_device, .kbd_device = NULL, .fdc_device = NULL, .sio_device = NULL, From 60bef8bb48b37286148e910c608e9327f541cb0c Mon Sep 17 00:00:00 2001 From: OBattler Date: Fri, 26 Sep 2025 18:20:06 +0200 Subject: [PATCH 256/274] Ne2000-compatibles: Fix IRQ's (this will break RSET8019 again, I'm trying to understand now to fix it). --- src/device/isapnp.c | 10 +++++----- src/network/net_ne2000.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index bc022ec43..34703820b 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -186,7 +186,7 @@ isapnp_device_config_changed(isapnp_card_t *card, isapnp_device_t *ld) for (uint8_t i = 0; i < 2; i++) { reg_base = 0x70 + (2 * i); card->config.irq[i].irq = ld->regs[reg_base]; - card->config.irq[i].level = ld->regs[reg_base + 1] & 0x02; + card->config.irq[i].level = !!(ld->regs[reg_base + 1] & 0x02); card->config.irq[i].type = ld->regs[reg_base + 1] & 0x01; } for (uint8_t i = 0; i < 2; i++) { @@ -268,13 +268,13 @@ isapnp_reset_ld_regs(isapnp_device_t *ld) /* Set the default IRQ type bits. */ for (uint8_t i = 0; i < 2; i++) { if (ld->irq_types & (0x1 << (4 * i))) - ld->regs[0x70 + (2 * i)] = 0x02; + ld->regs[0x71 + (2 * i)] = 0x02; else if (ld->irq_types & (0x2 << (4 * i))) - ld->regs[0x70 + (2 * i)] = 0x00; + ld->regs[0x71 + (2 * i)] = 0x00; else if (ld->irq_types & (0x4 << (4 * i))) - ld->regs[0x70 + (2 * i)] = 0x03; + ld->regs[0x71 + (2 * i)] = 0x03; else if (ld->irq_types & (0x8 << (4 * i))) - ld->regs[0x70 + (2 * i)] = 0x01; + ld->regs[0x71 + (2 * i)] = 0x01; } /* Reset configuration registers to match the default configuration. */ diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index 3b63c4db8..c91fd9641 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -155,7 +155,7 @@ nic_interrupt(void *priv, int set) nic_t *dev = (nic_t *) priv; int enabled = 1; - if (dev->irq_level) + if (!dev->irq_level) set ^= 1; if (dev->board == NE2K_RTL8019AS_PNP) From edb7a040bca7fab126798e736a95f2a592deb421 Mon Sep 17 00:00:00 2001 From: TC1995 Date: Fri, 26 Sep 2025 21:05:53 +0200 Subject: [PATCH 257/274] Some fixes for the AdLib Gold of the day (September 26th, 2025) (#6227) * AdLib Gold changes of the day (September 23rd, 2025) 1. Make sure the check to the Surround module is properly placed when disabled/enabled. 2. Replace local adgold_buffer with opl_buffer from its struct to improve the audio output and less clipping. * Some fixes for the AdLib Gold of the day (September 26th, 2025) 1. Revert the sampling DMA int functions back to void but with a check that monitors the DMA FIFO whenever it's within the range or not. 2. Actually clear the IRQ properly. --- src/sound/snd_adlibgold.c | 71 ++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/src/sound/snd_adlibgold.c b/src/sound/snd_adlibgold.c index 870e473b0..f8c89c554 100644 --- a/src/sound/snd_adlibgold.c +++ b/src/sound/snd_adlibgold.c @@ -91,6 +91,8 @@ typedef struct adgold_t { int gameport_enabled; int surround_enabled; + + int finish_dma; } adgold_t; static int attenuation[0x40]; @@ -174,33 +176,41 @@ adgold_update_irq_status(adgold_t *adgold) temp &= ~2; if ((adgold->adgold_mma_status & 0x02) && !(adgold->adgold_mma_regs[1][0xc] & 2)) temp &= ~2; + adgold->adgold_status = temp; - if ((adgold->adgold_status ^ 0xf) && !adgold->adgold_irq_status) { + if ((adgold->adgold_status ^ 0xf) && !adgold->adgold_irq_status) picint(1 << adgold->irq); - } + else if (!(adgold->adgold_status ^ 0xf) && adgold->adgold_irq_status) + picintc(1 << adgold->irq); adgold->adgold_irq_status = adgold->adgold_status ^ 0xf; } -int +void adgold_getsamp_dma(adgold_t *adgold, int channel) { - int temp; - dma_set_drq(adgold->dma, 1); + int dma_dat; - if ((adgold->adgold_mma_regs[channel][0xc] & 0x60) && (((adgold->adgold_mma_fifo_end[channel] - adgold->adgold_mma_fifo_start[channel]) & 255) >= 127)) - return 2; + adgold->finish_dma = 0; - temp = dma_channel_read(adgold->dma); - if (temp == DMA_NODATA) { - return 1; + if ((adgold->adgold_mma_regs[channel][0xc] & 0x60) && (((adgold->adgold_mma_fifo_end[channel] - adgold->adgold_mma_fifo_start[channel]) & 255) >= 127)) { + adgold->finish_dma = 1; + return; } - adgold->adgold_mma_fifo[channel][adgold->adgold_mma_fifo_end[channel]] = temp; - adgold->adgold_mma_fifo_end[channel] = (adgold->adgold_mma_fifo_end[channel] + 1) & 255; + + dma_set_drq(adgold->dma, 1); + dma_dat = dma_channel_read(adgold->dma); + if (dma_dat == DMA_NODATA) + return; + + adgold->adgold_mma_fifo[channel][adgold->adgold_mma_fifo_end[channel]] = dma_dat; + adgold->adgold_mma_fifo_end[channel] = (adgold->adgold_mma_fifo_end[channel] + 1) & 255; if (adgold->adgold_mma_regs[channel][0xc] & 0x60) { - temp = dma_channel_read(adgold->dma); - adgold->adgold_mma_fifo[channel][adgold->adgold_mma_fifo_end[channel]] = temp; + dma_dat = dma_channel_read(adgold->dma); + if (dma_dat == DMA_NODATA) + return; + adgold->adgold_mma_fifo[channel][adgold->adgold_mma_fifo_end[channel]] = dma_dat; adgold->adgold_mma_fifo_end[channel] = (adgold->adgold_mma_fifo_end[channel] + 1) & 255; } if (((adgold->adgold_mma_fifo_end[channel] - adgold->adgold_mma_fifo_start[channel]) & 255) >= adgold->adgold_mma_intpos[channel]) { @@ -208,8 +218,6 @@ adgold_getsamp_dma(adgold_t *adgold, int channel) adgold_update_irq_status(adgold); dma_set_drq(adgold->dma, 0); } - - return 0; } void @@ -381,16 +389,10 @@ adgold_write(uint16_t addr, uint8_t val, void *priv) adgold->adgold_mma.voice_count[1] = adgold->adgold_mma.voice_latch[1]; while (((adgold->adgold_mma_fifo_end[0] - adgold->adgold_mma_fifo_start[0]) & 255) < 128) { - if (adgold_getsamp_dma(adgold, 0)) { - adgold->adgold_mma_fifo_end[0] = 0; - adgold->adgold_mma_fifo_start[0] = 0; + adgold_getsamp_dma(adgold, 0); + adgold_getsamp_dma(adgold, 1); + if (adgold->finish_dma) break; - } - if (adgold_getsamp_dma(adgold, 1)) { - adgold->adgold_mma_fifo_end[1] = 0; - adgold->adgold_mma_fifo_start[1] = 0; - break; - } } if (((adgold->adgold_mma_fifo_end[0] - adgold->adgold_mma_fifo_start[0]) & 255) >= adgold->adgold_mma_intpos[0]) { adgold->adgold_mma_status &= ~0x01; @@ -404,11 +406,9 @@ adgold_write(uint16_t addr, uint8_t val, void *priv) } } else { while (((adgold->adgold_mma_fifo_end[0] - adgold->adgold_mma_fifo_start[0]) & 255) < 128) { - if (adgold_getsamp_dma(adgold, 0)) { - adgold->adgold_mma_fifo_end[0] = 0; - adgold->adgold_mma_fifo_start[0] = 0; + adgold_getsamp_dma(adgold, 0); + if (adgold->finish_dma) break; - } } if (((adgold->adgold_mma_fifo_end[0] - adgold->adgold_mma_fifo_start[0]) & 255) >= adgold->adgold_mma_intpos[0]) { adgold->adgold_mma_status &= ~0x01; @@ -518,11 +518,9 @@ adgold_write(uint16_t addr, uint8_t val, void *priv) if (adgold->adgold_mma_regs[1][0xc] & 1) { while (((adgold->adgold_mma_fifo_end[1] - adgold->adgold_mma_fifo_start[1]) & 255) < 128) { - if (adgold_getsamp_dma(adgold, 1)) { - adgold->adgold_mma_fifo_end[1] = 0; - adgold->adgold_mma_fifo_start[1] = 0; + adgold_getsamp_dma(adgold, 1); + if (adgold->finish_dma) break; - } } } } @@ -600,7 +598,6 @@ adgold_read(uint16_t addr, void *priv) temp = adgold->adgold_mma_status; adgold->adgold_mma_status &= ~0xf3; /*JUKEGOLD expects timer status flags to auto-clear*/ adgold_update_irq_status(adgold); - picintc(1 << adgold->irq); break; case 5: if (adgold->adgold_mma_addr >= 0xf) @@ -695,11 +692,9 @@ adgold_mma_poll(adgold_t *adgold, int channel) } if (adgold->adgold_mma_regs[channel][0xc] & 1) { - if (adgold_getsamp_dma(adgold, channel)) { - adgold->adgold_mma_fifo_end[channel] = 0; - adgold->adgold_mma_fifo_start[channel] = 0; + adgold_getsamp_dma(adgold, channel); + if (adgold->finish_dma) return; - } } if (((adgold->adgold_mma_fifo_end[channel] - adgold->adgold_mma_fifo_start[channel]) & 255) < adgold->adgold_mma_intpos[channel] && !(adgold->adgold_mma_status & 0x01)) { adgold->adgold_mma_status |= (1 << channel); From 16c0460d61e6dc192444cd813559501422c89b33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 21:06:45 +0200 Subject: [PATCH 258/274] Bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows (#6225) Bumps [SonarSource/sonarqube-scan-action](https://github.com/sonarsource/sonarqube-scan-action) from 5 to 6. - [Release notes](https://github.com/sonarsource/sonarqube-scan-action/releases) - [Commits](https://github.com/sonarsource/sonarqube-scan-action/compare/v5...v6) --- updated-dependencies: - dependency-name: SonarSource/sonarqube-scan-action dependency-version: '6' dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql_linux.yml | 4 ++-- .github/workflows/codeql_macos.yml | 4 ++-- .github/workflows/codeql_windows_msys2.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/codeql_linux.yml b/.github/workflows/codeql_linux.yml index 957c7090b..855571782 100644 --- a/.github/workflows/codeql_linux.yml +++ b/.github/workflows/codeql_linux.yml @@ -121,7 +121,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install Build Wrapper - uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v5 + uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -149,7 +149,7 @@ jobs: - name: SonarQube Scan if: matrix.build.preset == 'dev_debug' && matrix.dynarec.new == 'on' && matrix.ui.qt == 'on' && env.SONAR_TOKEN != '' # if: 0 - uses: SonarSource/sonarqube-scan-action@v5 + uses: SonarSource/sonarqube-scan-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/codeql_macos.yml b/.github/workflows/codeql_macos.yml index 488cbfd49..74365cd56 100644 --- a/.github/workflows/codeql_macos.yml +++ b/.github/workflows/codeql_macos.yml @@ -109,7 +109,7 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install Build Wrapper - uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v5 + uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -140,7 +140,7 @@ jobs: - name: SonarQube Scan # if: matrix.build.preset == 'dev_debug' && matrix.dynarec.new == 'on' && matrix.ui.qt == 'on' && env.SONAR_TOKEN != '' if: 0 - uses: SonarSource/sonarqube-scan-action@v5 + uses: SonarSource/sonarqube-scan-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/codeql_windows_msys2.yml b/.github/workflows/codeql_windows_msys2.yml index d70c6fd5f..9673ab650 100644 --- a/.github/workflows/codeql_windows_msys2.yml +++ b/.github/workflows/codeql_windows_msys2.yml @@ -169,7 +169,7 @@ jobs: - name: SonarQube Scan # if: matrix.build.preset == 'dev_debug' && matrix.dynarec.new == 'on' && matrix.ui.qt == 'on' && env.SONAR_TOKEN != '' if: 0 - uses: SonarSource/sonarqube-scan-action@v5 + uses: SonarSource/sonarqube-scan-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From ca1005817ab9f0a5614def2bbf73b42623755d86 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 00:04:42 +0200 Subject: [PATCH 259/274] RTL8019AS: Properly fix the RSET8019 IRQ check (writing 0x00 to IMR clears IRQ, writing 0x80 to CONFIG1 sets IRQ, changing IRQ does not clear it). --- src/network/net_ne2000.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index c91fd9641..e9732fba8 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -481,8 +481,11 @@ page3_write(nic_t *dev, uint32_t off, uint32_t val, UNUSED(unsigned len)) break; case 0x04: /* CONFIG1 */ - if (cfg_write_enable && (dev->board == NE2K_RTL8019AS_PNP)) + if (cfg_write_enable && (dev->board == NE2K_RTL8019AS_PNP)) { dev->config1 = (dev->config1 & 0x7f) | (val & 0x80); + if (val & 0x80) + nic_interrupt(dev, 1); + } break; case 0x05: /* CONFIG2 */ @@ -649,8 +652,6 @@ nic_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) dev->base_address = config->io[0].base; - dev->irq_level = 0x02; - nic_interrupt(dev, 0); dev->base_irq = config->irq[0].irq; dev->irq_level = config->irq[0].level; if ((dev->base_irq >= 0x00) && (dev->base_irq <= 0x0f)) From b8b8efd454d2d01d7b596cd753bb66d445bac752 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 00:56:50 +0200 Subject: [PATCH 260/274] RTL8019AS: Inform ISA PnP of the I/O base address, configured in the EEPROM even if it's programmed to start inactive, fixes the remaining I/O range check errors in RSET8019. --- src/network/net_ne2000.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/network/net_ne2000.c b/src/network/net_ne2000.c index e9732fba8..3e2e7dd38 100644 --- a/src/network/net_ne2000.c +++ b/src/network/net_ne2000.c @@ -1411,21 +1411,20 @@ nic_init(const device_t *info) isapnp_set_normal(dev->pnp_card, !!(dev->config3 & 0x80)); isapnp_set_single_ld(dev->pnp_card); - if (!(dev->config3 & 0x01)) { - uint8_t irq_map[8] = { 9, 3, 4, 5, 10, 11, 12, 15 }; + uint8_t irq_map[8] = { 9, 3, 4, 5, 10, 11, 12, 15 }; - dev->base_address = 0x0200; - dev->base_address |= (dev->config1 & 0x01) ? 0x0020 : 0x0000; - dev->base_address |= (dev->config1 & 0x02) ? 0x0040 : 0x0000; - dev->base_address |= (dev->config1 & 0x04) ? 0x0000 : 0x0100; - dev->base_address |= (dev->config1 & 0x08) ? 0x0080 : 0x0000; + dev->base_address = 0x0200; + dev->base_address |= (dev->config1 & 0x01) ? 0x0020 : 0x0000; + dev->base_address |= (dev->config1 & 0x02) ? 0x0040 : 0x0000; + dev->base_address |= (dev->config1 & 0x04) ? 0x0000 : 0x0100; + dev->base_address |= (dev->config1 & 0x08) ? 0x0080 : 0x0000; - dev->base_irq = irq_map[(dev->config1 >> 4) & 0x07]; + dev->base_irq = irq_map[(dev->config1 >> 4) & 0x07]; + if (!(dev->config3 & 0x01)) nic_ioset(dev, dev->base_address); - isapnp_activate(dev->pnp_card, dev->base_address, dev->base_irq); - } + isapnp_activate(dev->pnp_card, dev->base_address, dev->base_irq, !(dev->config3 & 0x01)); } } From 1dbc304b2f6c19a82d7172e09cfcde7c391417b3 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 01:28:43 +0200 Subject: [PATCH 261/274] The forgotten isapnp.c and .h. --- src/device/isapnp.c | 4 ++-- src/include/86box/isapnp.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/device/isapnp.c b/src/device/isapnp.c index 34703820b..3129cd1c8 100644 --- a/src/device/isapnp.c +++ b/src/device/isapnp.c @@ -1252,7 +1252,7 @@ isapnp_set_normal(void *priv, uint8_t normal) } void -isapnp_activate(void *priv, uint16_t base, uint8_t irq) +isapnp_activate(void *priv, uint16_t base, uint8_t irq, int active) { isapnp_card_t *card = (isapnp_card_t *) priv; isapnp_device_t *ld = card->first_ld; @@ -1264,7 +1264,7 @@ isapnp_activate(void *priv, uint16_t base, uint8_t irq) } if (ld != NULL) { - ld->defs[0x30] = 0x01; + ld->defs[0x30] = active; ld->defs[0x60] = base >> 8; if (!(ld->io_16bit & (1 << ((0x60 >> 1) & 0x07)))) ld->defs[0x60] &= 0x03; diff --git a/src/include/86box/isapnp.h b/src/include/86box/isapnp.h index 7d730564a..9fedd656c 100644 --- a/src/include/86box/isapnp.h +++ b/src/include/86box/isapnp.h @@ -73,7 +73,7 @@ extern void isapnp_reset_card(void *priv); extern void isapnp_reset_device(void *priv, uint8_t ld); extern void isapnp_set_rt(void *priv, uint8_t is_rt); extern void isapnp_set_normal(void *priv, uint8_t normal); -extern void isapnp_activate(void *priv, uint16_t base, uint8_t irq); +extern void isapnp_activate(void *priv, uint16_t base, uint8_t irq, int active); extern void isapnp_set_single_ld(void *priv); extern uint8_t *isapnp_get_csnsav(void *priv); From 809e1f3efbb2695e9fd5d0f3107a1c32c3ee74fb Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 04:16:01 +0200 Subject: [PATCH 262/274] Give the Jetway J-403TG with AMI BIOS the correct NVR device, fixes #6230. --- src/machine/m_at_socket3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/m_at_socket3.c b/src/machine/m_at_socket3.c index 8d460186c..d882dbd4d 100644 --- a/src/machine/m_at_socket3.c +++ b/src/machine/m_at_socket3.c @@ -216,7 +216,7 @@ machine_at_403tg_init(const machine_t *model) return ret; device_context(model->device); - int nvr_hack = !strcmp(device_get_config_bios("bios"), "403tg_d"); + int nvr_hack = !strcmp(device_get_config_bios("bios"), "403tg"); fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); ret = bios_load_linear(fn, 0x000f0000, 65536, 0); From b1dfe5320c25cf21899d16af3d3459c64f032aa6 Mon Sep 17 00:00:00 2001 From: Toni Riikonen Date: Sat, 27 Sep 2025 09:54:41 +0300 Subject: [PATCH 263/274] Fixes #6220: OS/2 Warp 3.0 install disk issue and NT 3.1 floppy disk issues. Changed the fdc->stat bit to 0x10 which states fdc busy. Previous implementation with seek time 0, set the ready flag 0x80 immediadely - which was correct for that time, but now as the drive is busy during seek, the value should 0x10. Implemented a backup for fdd commands during fdd seek to be processes after the seek is completed. --- src/floppy/fdc.c | 1522 +++++++++++++++++++++++----------------------- src/floppy/fdd.c | 151 ++++- 2 files changed, 913 insertions(+), 760 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index bcf4a1299..212bf9ce0 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -184,7 +184,7 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x80; + fdc->stat = 0x10; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->head = 0; @@ -470,9 +470,9 @@ uint8_t fdc_get_shadow(fdc_t *fdc) { uint8_t ret = (fdc->rate & 0x03) | - ((fdc->pretrk & 0x07) << 2) | - (fdc->power_down ? 0x40 : 0x00) | - ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); + ((fdc->pretrk & 0x07) << 2) | + (fdc->power_down ? 0x40 : 0x00) | + ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); return ret; } @@ -532,36 +532,36 @@ fdc_update_rate(fdc_t *fdc, int drive) fdc->enh_mode && !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 500; else if ((fdc->rwc[drive] == 3) && fdc->enh_mode && - !(fdc->flags & FDC_FLAG_SMC661)) + !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 250; else switch (fdc->rate) { - default: - break; - case 0: /*High density*/ - fdc->bit_rate = 500; - break; - case 1: /*Double density (360 rpm)*/ - switch (fdc->drvrate[drive]) { - default: - break; - case 0: - fdc->bit_rate = 300; - break; - case 1: - fdc->bit_rate = 500; - break; - case 2: - fdc->bit_rate = 2000; - break; - } - break; - case 2: /*Double density*/ - fdc->bit_rate = 250; - break; - case 3: /*Extended density*/ - fdc->bit_rate = 1000; - break; - } + default: + break; + case 0: /*High density*/ + fdc->bit_rate = 500; + break; + case 1: /*Double density (360 rpm)*/ + switch (fdc->drvrate[drive]) { + default: + break; + case 0: + fdc->bit_rate = 300; + break; + case 1: + fdc->bit_rate = 500; + break; + case 2: + fdc->bit_rate = 2000; + break; + } + break; + case 2: /*Double density*/ + fdc->bit_rate = 250; + break; + case 3: /*Extended density*/ + fdc->bit_rate = 1000; + break; + } fdc->bitcell_period = (1000000 / fdc->bit_rate) * 2; /*Bitcell period in ns*/ } @@ -695,7 +695,7 @@ void fdc_seek(fdc_t *fdc, int drive, int params) { fdd_seek(real_drive(fdc, drive), params); - fdc->stat |= (1 << fdc->drive); + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << fdc->drive); } static void @@ -719,8 +719,10 @@ fdc_io_command_phase1(fdc_t *fdc, int out) fdc->dtl = fdc->params[7]; fdc->rw_track = fdc->params[1]; + int implied_seek = 0; if (fdc->config & 0x40) { if (fdc->rw_track != fdc->pcn[fdc->params[0] & 3]) { + implied_seek = 1; fdc_seek(fdc, fdc->drive, ((int) fdc->rw_track) - ((int) fdc->pcn[fdc->params[0] & 3])); fdc->pcn[fdc->params[0] & 3] = fdc->rw_track; } @@ -730,6 +732,12 @@ fdc_io_command_phase1(fdc_t *fdc, int out) ui_sb_update_icon_write(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); else ui_sb_update_icon(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); + + if (implied_seek) { + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << real_drive(fdc, fdc->drive)); /* CB=1, per-drive busy */ + return; + } + fdc->stat = out ? 0x10 : 0x50; if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) { fdc->stat |= 0x20; @@ -791,7 +799,7 @@ fdc_soft_reset(fdc_t *fdc) } fdc_ctrl_reset(fdc); - } + } } static void @@ -806,565 +814,566 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) switch (addr & 7) { - case 0: - return; - case 1: - return; - case 2: /*DOR*/ - if (fdc->flags & FDC_FLAG_PCJR) { - if ((fdc->dor & 0x40) && !(val & 0x40)) { - timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); - fdc->watchdog_count = 1000; - picintc(1 << fdc->irq); - } - if ((val & 0x80) && !(fdc->dor & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -1; - ui_sb_update_icon(SB_FLOPPY | 0, 0); - ui_sb_update_icon_write(SB_FLOPPY | 0, 0); - fdc_ctrl_reset(fdc); - } - if (!fdd_get_flags(0)) - val &= 0xfe; - fdd_set_motor_enable(0, val & 0x01); - fdc->st0 &= ~0x07; - fdc->st0 |= (fdd_get_head(0) ? 4 : 0); - } else { - /* - Writing this bit to logic "1" will enable the DRQ, - nDACK, TC and FINTR outputs. This bit being a - logic "0" will disable the nDACK and TC inputs, and - hold the DRQ and FINTR outputs in a high - impedance state. - */ - if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { - fdc->tc = 1; - fdc->fintr = 0; - picintc(1 << fdc->irq); - } - if (!(val & 4)) { - fdd_stop(real_drive(fdc, val & 3)); - fdc->stat = 0x00; - fdc->pnum = fdc->ptot = 0; - } - if ((val & 4) && !(fdc->dor & 4)) - fdc_soft_reset(fdc); - /* We can now simplify this since each motor now spins separately. */ - for (int i = 0; i < FDD_NUM; i++) { - drive_num = real_drive(fdc, i); - if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) - val &= ~(0x10 << drive_num); - else - fdd_set_motor_enable(i, (val & (0x10 << drive_num))); - } - drive_num = real_drive(fdc, val & 0x03); - current_drive = drive_num; - fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); - } - fdc->dor = val; - return; - case 3: /* TDR */ - if (fdc->enh_mode) { - if (fdc->flags & FDC_FLAG_SMC661) { - fdc_set_swap(fdc, !!(val & 0x20)); - fdc_update_densel_force(fdc, (val & 0x18) >> 3); + if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) + switch (addr & 7) { + case 0: + return; + case 1: + return; + case 2: /*DOR*/ + if (fdc->flags & FDC_FLAG_PCJR) { + if ((fdc->dor & 0x40) && !(val & 0x40)) { + timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); + fdc->watchdog_count = 1000; + picintc(1 << fdc->irq); + } + if ((val & 0x80) && !(fdc->dor & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -1; + ui_sb_update_icon(SB_FLOPPY | 0, 0); + ui_sb_update_icon_write(SB_FLOPPY | 0, 0); + fdc_ctrl_reset(fdc); + } + if (!fdd_get_flags(0)) + val &= 0xfe; + fdd_set_motor_enable(0, val & 0x01); + fdc->st0 &= ~0x07; + fdc->st0 |= (fdd_get_head(0) ? 4 : 0); } else { - drive = real_drive(fdc, fdc->dor & 3); - fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + /* + Writing this bit to logic "1" will enable the DRQ, + nDACK, TC and FINTR outputs. This bit being a + logic "0" will disable the nDACK and TC inputs, and + hold the DRQ and FINTR outputs in a high + impedance state. + */ + if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { + fdc->tc = 1; + fdc->fintr = 0; + picintc(1 << fdc->irq); + } + if (!(val & 4)) { + fdd_stop(real_drive(fdc, val & 3)); + fdc->stat = 0x00; + fdc->pnum = fdc->ptot = 0; + } + if ((val & 4) && !(fdc->dor & 4)) + fdc_soft_reset(fdc); + /* We can now simplify this since each motor now spins separately. */ + for (int i = 0; i < FDD_NUM; i++) { + drive_num = real_drive(fdc, i); + if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) + val &= ~(0x10 << drive_num); + else + fdd_set_motor_enable(i, (val & (0x10 << drive_num))); + } + drive_num = real_drive(fdc, val & 0x03); + current_drive = drive_num; + fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); } - } - /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) - The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. - If it fails, then floppy drives will be treated as DD drives. */ - if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (val & 0x04) { - fdc->tfifo = 8; - fdc->fifointest = 1; - } else { - fdc->tfifo = 1; - fdc->fifointest = 0; + fdc->dor = val; + return; + case 3: /* TDR */ + if (fdc->enh_mode) { + if (fdc->flags & FDC_FLAG_SMC661) { + fdc_set_swap(fdc, !!(val & 0x20)); + fdc_update_densel_force(fdc, (val & 0x18) >> 3); + } else { + drive = real_drive(fdc, fdc->dor & 3); + fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + } } - fifo_reset(fdc->fifo_p); - fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); - fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); - } - return; - case 4: /* DSR */ - if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { - if (!(val & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -6; + /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) + The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. + If it fails, then floppy drives will be treated as DD drives. */ + if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (val & 0x04) { + fdc->tfifo = 8; + fdc->fifointest = 1; + } else { + fdc->tfifo = 1; + fdc->fifointest = 0; + } + fifo_reset(fdc->fifo_p); + fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); + fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); } - if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) - fdc_soft_reset(fdc); - } - fdc->dsr = val; - return; - case 5: /*Command register*/ - if (fdc->fifointest) { - /* Write FIFO buffer in the test mode (PS/55) */ - fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); - fifo_write(val, fdc->fifo_p); - if (fifo_get_full(fdc->fifo_p)) - fdc->stat &= ~0x80; - break; - } - if ((fdc->stat & 0xf0) == 0xb0) { - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->dat = val; - fdc->stat &= ~0x80; - } else { + return; + case 4: /* DSR */ + if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { + if (!(val & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -6; + } + if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) + fdc_soft_reset(fdc); + } + fdc->dsr = val; + return; + case 5: /*Command register*/ + if (fdc->fifointest) { + /* Write FIFO buffer in the test mode (PS/55) */ + fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); fifo_write(val, fdc->fifo_p); if (fifo_get_full(fdc->fifo_p)) fdc->stat &= ~0x80; + break; } - break; - } - if (fdc->pnum == fdc->ptot) { - if ((fdc->stat & 0xf0) != 0x80) { - /* If bit 4 of the MSR is set, or the MSR is 0x00, - the fdc_t is NOT in the command phase, therefore - do NOT accept commands. */ - return; + if ((fdc->stat & 0xf0) == 0xb0) { + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->dat = val; + fdc->stat &= ~0x80; + } else { + fifo_write(val, fdc->fifo_p); + if (fifo_get_full(fdc->fifo_p)) + fdc->stat &= ~0x80; + } + break; } + if (fdc->pnum == fdc->ptot) { + if ((fdc->stat & 0xf0) != 0x80) { + /* If bit 4 of the MSR is set, or the MSR is 0x00, + the fdc_t is NOT in the command phase, therefore + do NOT accept commands. */ + return; + } - fdc->stat &= 0xf; + fdc->stat &= 0xf; - fdc->tc = 0; - fdc->data_ready = 0; + fdc->tc = 0; + fdc->data_ready = 0; - fdc->command = val; - fdc->stat |= 0x10; - fdc_log("Starting FDC command %02X\n", fdc->command); - fdc->error = 0; + fdc->command = val; + fdc->stat |= 0x10; + fdc_log("Starting FDC command %02X\n", fdc->command); + fdc->error = 0; - if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || - ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || - ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || - ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || - ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) - fdc->processed_cmd = fdc->command & 0x1f; - else - fdc->processed_cmd = fdc->command; + if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || + ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || + ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || + ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || + ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) + fdc->processed_cmd = fdc->command & 0x1f; + else + fdc->processed_cmd = fdc->command; - switch (fdc->processed_cmd) { - case 0x01: /*Mode*/ - if (fdc->flags & FDC_FLAG_NSC) { - fdc->pnum = 0; - fdc->ptot = 4; + switch (fdc->processed_cmd) { + case 0x01: /*Mode*/ + if (fdc->flags & FDC_FLAG_NSC) { + fdc->pnum = 0; + fdc->ptot = 4; + fdc->stat |= 0x90; + fdc->format_state = 0; + } else + fdc_bad_command(fdc); + break; + case 0x02: /*Read track*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->pnum = 0; + fdc->ptot = 8; fdc->stat |= 0x90; - fdc->format_state = 0; - } else - fdc_bad_command(fdc); - break; - case 0x02: /*Read track*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x03: /*Specify*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x04: /*Sense drive status*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x05: /*Write data*/ - case 0x09: /*Write deleted data*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x06: /*Read data*/ - case 0x0c: /*Read deleted data*/ - case 0x11: /*Scan equal*/ - case 0x19: /*Scan low or equal*/ - case 0x16: /*Verify*/ - case 0x1d: /*Scan high or equal*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; - if ((fdc->command & 0x1F) == 0x16) - fdc->deleted = 2; - fdc->deleted |= (fdc->command & 0x20); - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x17: /*Powerdown mode*/ - if (!(fdc->flags & FDC_FLAG_ALI)) { - fdc_bad_command(fdc); + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - } - fallthrough; - case 0x07: /*Recalibrate*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x08: /*Sense interrupt status*/ - fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); - fdc->lastdrive = fdc->drive; - fdc_sis(fdc); - break; - case 0x0a: /*Read sector ID*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x0d: /*Format track*/ - fdc->pnum = 0; - fdc->ptot = 5; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - fdc->format_state = 0; - break; - case 0x0e: /*Dump registers*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); + case 0x03: /*Specify*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = 0x0e; - fdc_callback(fdc); - break; - case 0x0f: /*Seek*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x18: /*NSC*/ - if (!(fdc->flags & FDC_FLAG_NSC)) { - fdc_bad_command(fdc); - break; - } - fallthrough; - case 0x10: /*Get version*/ - case 0x14: /*Unlock*/ - case 0x94: /*Lock*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); - break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = fdc->command; - fdc_callback(fdc); - break; - case 0x12: /*Set perpendicular mode*/ - if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + case 0x04: /*Sense drive status*/ fdc->pnum = 0; fdc->ptot = 1; fdc->stat |= 0x90; - } else - fdc_bad_command(fdc); - break; - case 0x13: /*Configure*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); break; - } - fdc->pnum = 0; - fdc->ptot = 3; - fdc->stat |= 0x90; - break; - default: - fdc_bad_command(fdc); - break; - } - } else { - fdc->stat = 0x10 | (fdc->stat & 0xf); - fdc->params[fdc->pnum++] = val; - if (fdc->pnum == 1) { - if (command_has_drivesel[fdc->command & 0x1F]) { - if (fdc->flags & FDC_FLAG_PCJR) - fdc->drive = 0; - else - fdc->drive = fdc->dor & 3; - fdc->rw_drive = fdc->params[0] & 3; - if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) - fdc->stat |= (1 << real_drive(fdc, fdc->drive)); - } - } - if (fdc->pnum == fdc->ptot) { - fdc_log("Got all params %02X\n", fdc->command); - fifo_reset(fdc->fifo_p); - fdc->interrupt = fdc->processed_cmd; - fdc->reset_stat = 0; - /* Disable timer if enabled. */ - timer_disable(&fdc->timer); - /* Start timer if needed at this point. */ - switch (fdc->interrupt & 0x1f) { - case 0x02: /* Read a track */ - case 0x03: /* Specify */ - case 0x0a: /* Read sector ID */ - case 0x05: /* Write data */ - case 0x06: /* Read data */ - case 0x09: /* Write deleted data */ - case 0x0c: /* Read deleted data */ - case 0x11: /* Scan equal */ - case 0x12: /* Perpendicular mode */ - case 0x16: /* Verify */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - /* Do nothing. */ + case 0x05: /*Write data*/ + case 0x09: /*Write deleted data*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - case 0x07: /* Recalibrate */ - case 0x0f: /* Seek */ - if (fdc->flags & FDC_FLAG_PCJR) - timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); - else - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + case 0x06: /*Read data*/ + case 0x0c: /*Read deleted data*/ + case 0x11: /*Scan equal*/ + case 0x19: /*Scan low or equal*/ + case 0x16: /*Verify*/ + case 0x1d: /*Scan high or equal*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; + if ((fdc->command & 0x1F) == 0x16) + fdc->deleted = 2; + fdc->deleted |= (fdc->command & 0x20); + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - default: - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); - break; - } - /* Process the firt phase of the command. */ - switch (fdc->processed_cmd) { - case 0x02: /* Read a track */ - fdc_io_command_phase1(fdc, 0); - fdc->read_track_sector.id.c = fdc->params[1]; - fdc->read_track_sector.id.h = fdc->params[2]; - fdc->read_track_sector.id.r = 1; - fdc->read_track_sector.id.n = fdc->params[4]; - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; + case 0x17: /*Powerdown mode*/ + if (!(fdc->flags & FDC_FLAG_ALI)) { + fdc_bad_command(fdc); + break; } - fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x03: /* Specify */ - fdc->stat = 0x80; - fdc->specify[0] = fdc->params[0]; - fdc->specify[1] = fdc->params[1]; - fdc->dma = (fdc->specify[1] & 1) ^ 1; - if (!fdc->dma) - dma_set_drq(fdc->dma_ch, 0); - break; - case 0x04: /*Sense drive status*/ - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - break; - case 0x05: /* Write data */ - case 0x09: /* Write deleted data */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x11: /* Scan equal */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x16: /* Verify */ - if (fdc->params[0] & 0x80) - fdc->sc = fdc->params[7]; fallthrough; - case 0x06: /* Read data */ - case 0x0c: /* Read deleted data */ - fdc_io_command_phase1(fdc, 0); - fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { - /* DMA is in verify mode, treat this like a VERIFY command. */ - fdc_log("Verify-mode read!\n"); - fdc->tc = 1; - fdc->deleted |= 2; - } - fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + case 0x07: /*Recalibrate*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; break; - - case 0x07: /* Recalibrate */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << real_drive(fdc, fdc->drive)); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->st0 = fdc->params[0] & 3; - fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; - fdc->st0 |= 0x80; - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { - fdc_log("Failed recalibrate\n"); - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) - fdc->st0 = 0x70 | (fdc->params[0] & 3); - else - fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->pcn[fdc->params[0] & 3] = 0; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x08: /*Sense interrupt status*/ + fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); + fdc->lastdrive = fdc->drive; + fdc_sis(fdc); + break; + case 0x0a: /*Read sector ID*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + break; + case 0x0d: /*Format track*/ + fdc->pnum = 0; + fdc->ptot = 5; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + fdc->format_state = 0; + break; + case 0x0e: /*Dump registers*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) - fdc_seek(fdc, fdc->drive, -fdc->max_track); - fdc_log("Recalibrating...\n"); - fdc->seek_dir = fdc->step = 1; + fdc->lastdrive = fdc->drive; + fdc->interrupt = 0x0e; + fdc_callback(fdc); break; - case 0x0a: /* Read sector ID */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { - fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) - fdc->stat = 0x70; - else - fdc->stat = 0x50; + case 0x0f: /*Seek*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; + break; + case 0x18: /*NSC*/ + if (!(fdc->flags & FDC_FLAG_NSC)) { + fdc_bad_command(fdc); + break; + } + fallthrough; + case 0x10: /*Get version*/ + case 0x14: /*Unlock*/ + case 0x94: /*Lock*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); + break; + } + fdc->lastdrive = fdc->drive; + fdc->interrupt = fdc->command; + fdc_callback(fdc); + break; + case 0x12: /*Set perpendicular mode*/ + if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; } else - fdc_noidam(fdc); + fdc_bad_command(fdc); break; - case 0x0d: /* Format */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - fdc->gap = fdc->params[3]; - fdc->format_sectors = fdc->params[2]; - fdc->format_n = fdc->params[1]; - fdc->format_state = 1; - fdc->stat = 0x10; - break; - case 0x0f: /* Seek */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << fdc->drive); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->head = 0; /* TODO: See if this is correct. */ - fdc->st0 = fdc->params[0] & 0x03; - fdc->st0 |= (fdc->params[0] & 4); - fdc->st0 |= 0x80; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { - /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->command & 0x80) { - if (fdc->command & 0x40) - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - else - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } else - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x13: /*Configure*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if (fdc->command & 0x80) { - if (fdc->params[1]) { - if (fdc->command & 0x40) { - /* Relative seek inwards. */ - fdc->seek_dir = 0; - fdc_seek(fdc, fdc->drive, fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - } else { - /* Relative seek outwards. */ - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, -fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } - fdc->step = 1; - } else { - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - } else { - fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); - if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { - fdc_log("Failed seek\n"); - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) - fdc->seek_dir = 0; - else - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - fdc->step = 1; - } + fdc->pnum = 0; + fdc->ptot = 3; + fdc->stat |= 0x90; break; - case 0x12: /* Perpendicular mode */ - fdc->stat = 0x80; - if (fdc->params[0] & 0x80) - fdc->perp = fdc->params[0] & 0x3f; - else { - fdc->perp &= 0xfc; - fdc->perp |= (fdc->params[0] & 0x03); - } - return; - default: + fdc_bad_command(fdc); break; } - } else - fdc->stat = 0x90 | (fdc->stat & 0xf); - } - return; - case 7: - if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) - return; - fdc->rate = val & 0x03; - if (fdc->flags & FDC_FLAG_PS2) - fdc->noprec = !!(val & 0x04); - return; + } else { + fdc->stat = 0x10 | (fdc->stat & 0xf); + fdc->params[fdc->pnum++] = val; + if (fdc->pnum == 1) { + if (command_has_drivesel[fdc->command & 0x1F]) { + if (fdc->flags & FDC_FLAG_PCJR) + fdc->drive = 0; + else + fdc->drive = fdc->dor & 3; + fdc->rw_drive = fdc->params[0] & 3; + if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) + fdc->stat |= (1 << real_drive(fdc, fdc->drive)); + } + } + if (fdc->pnum == fdc->ptot) { + fdc_log("Got all params %02X\n", fdc->command); + fifo_reset(fdc->fifo_p); + fdc->interrupt = fdc->processed_cmd; + fdc->reset_stat = 0; + /* Disable timer if enabled. */ + timer_disable(&fdc->timer); + /* Start timer if needed at this point. */ + switch (fdc->interrupt & 0x1f) { + case 0x02: /* Read a track */ + case 0x03: /* Specify */ + case 0x0a: /* Read sector ID */ + case 0x05: /* Write data */ + case 0x06: /* Read data */ + case 0x09: /* Write deleted data */ + case 0x0c: /* Read deleted data */ + case 0x11: /* Scan equal */ + case 0x12: /* Perpendicular mode */ + case 0x16: /* Verify */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + /* Do nothing. */ + break; + case 0x07: /* Recalibrate */ + case 0x0f: /* Seek */ + if (fdc->flags & FDC_FLAG_PCJR) + timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); + else + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + default: + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + } + /* Process the firt phase of the command. */ + switch (fdc->processed_cmd) { + case 0x02: /* Read a track */ + fdc_io_command_phase1(fdc, 0); + fdc->read_track_sector.id.c = fdc->params[1]; + fdc->read_track_sector.id.h = fdc->params[2]; + fdc->read_track_sector.id.r = 1; + fdc->read_track_sector.id.n = fdc->params[4]; + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x03: /* Specify */ + fdc->stat = 0x80; + fdc->specify[0] = fdc->params[0]; + fdc->specify[1] = fdc->params[1]; + fdc->dma = (fdc->specify[1] & 1) ^ 1; + if (!fdc->dma) + dma_set_drq(fdc->dma_ch, 0); + break; + case 0x04: /*Sense drive status*/ + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + break; + case 0x05: /* Write data */ + case 0x09: /* Write deleted data */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x11: /* Scan equal */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x16: /* Verify */ + if (fdc->params[0] & 0x80) + fdc->sc = fdc->params[7]; + fallthrough; + case 0x06: /* Read data */ + case 0x0c: /* Read deleted data */ + fdc_io_command_phase1(fdc, 0); + fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { + /* DMA is in verify mode, treat this like a VERIFY command. */ + fdc_log("Verify-mode read!\n"); + fdc->tc = 1; + fdc->deleted |= 2; + } + fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; - default: - break; - } + case 0x07: /* Recalibrate */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << real_drive(fdc, fdc->drive)); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->st0 = fdc->params[0] & 3; + fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; + fdc->st0 |= 0x80; + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { + fdc_log("Failed recalibrate\n"); + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) + fdc->st0 = 0x70 | (fdc->params[0] & 3); + else + fdc->st0 = 0x20 | (fdc->params[0] & 3); + fdc->pcn[fdc->params[0] & 3] = 0; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) + fdc_seek(fdc, fdc->drive, -fdc->max_track); + fdc_log("Recalibrating...\n"); + fdc->seek_dir = fdc->step = 1; + break; + case 0x0a: /* Read sector ID */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { + fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) + fdc->stat = 0x70; + else + fdc->stat = 0x50; + } else + fdc_noidam(fdc); + break; + case 0x0d: /* Format */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + fdc->gap = fdc->params[3]; + fdc->format_sectors = fdc->params[2]; + fdc->format_n = fdc->params[1]; + fdc->format_state = 1; + fdc->stat = 0x10; + break; + case 0x0f: /* Seek */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << fdc->drive); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->head = 0; /* TODO: See if this is correct. */ + fdc->st0 = fdc->params[0] & 0x03; + fdc->st0 |= (fdc->params[0] & 4); + fdc->st0 |= 0x80; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { + /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->command & 0x80) { + if (fdc->command & 0x40) + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + else + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } else + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->command & 0x80) { + if (fdc->params[1]) { + if (fdc->command & 0x40) { + /* Relative seek inwards. */ + fdc->seek_dir = 0; + fdc_seek(fdc, fdc->drive, fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + } else { + /* Relative seek outwards. */ + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, -fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } + fdc->step = 1; + } else { + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + } else { + fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); + if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { + fdc_log("Failed seek\n"); + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) + fdc->seek_dir = 0; + else + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + fdc->step = 1; + } + break; + case 0x12: /* Perpendicular mode */ + fdc->stat = 0x80; + if (fdc->params[0] & 0x80) + fdc->perp = fdc->params[0] & 0x3f; + else { + fdc->perp &= 0xfc; + fdc->perp |= (fdc->params[0] & 0x03); + } + return; + + default: + break; + } + } else + fdc->stat = 0x90 | (fdc->stat & 0xf); + } + return; + case 7: + if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) + return; + fdc->rate = val & 0x03; + if (fdc->flags & FDC_FLAG_PS2) + fdc->noprec = !!(val & 0x04); + return; + + default: + break; + } } uint8_t @@ -1376,213 +1385,214 @@ fdc_read(uint16_t addr, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2)) switch (addr & 7) { - case 0: /* STA */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - /* TODO: - Bit 2: INDEX (best return always 0 as it goes by very fast) - */ - if (fdc->seek_dir) /* nDIRECTION */ - ret |= 0x01; - if (writeprot[drive]) /* WRITEPROT */ - ret |= 0x02; - if (!fdd_get_head(drive)) /* nHDSEL */ - ret |= 0x08; - if (fdd_track0(drive)) /* TRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (dma_get_drq(fdc->dma_ch)) /* DRQ */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x04; - /* TODO: - Bit 2: nINDEX (best return always 1 as it goes by very fast) - */ - if (!fdc->seek_dir) /* DIRECTION */ - ret |= 0x01; - if (!writeprot[drive]) /* nWRITEPROT */ - ret |= 0x02; - if (fdd_get_head(drive)) /* HDSEL */ - ret |= 0x08; - if (!fdd_track0(drive)) /* nTRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else - ret = 0xff; - break; - case 1: /* STB */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x80; - switch (drive) { /* -Drive Select 1,0 */ - case 0: - ret |= 0x43; - break; - case 1: - ret |= 0x23; - break; - case 2: - ret |= 0x62; - break; - case 3: - ret |= 0x61; - break; - - default: - break; - } - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0xc0; - ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ - } else { - if (is486 || !fdc->enable_3f1) + if (!fdc->power_down || ((addr & 7) == 2)) + switch (addr & 7) { + case 0: /* STA */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + /* TODO: + Bit 2: INDEX (best return always 0 as it goes by very fast) + */ + if (fdc->seek_dir) /* nDIRECTION */ + ret |= 0x01; + if (writeprot[drive]) /* WRITEPROT */ + ret |= 0x02; + if (!fdd_get_head(drive)) /* nHDSEL */ + ret |= 0x08; + if (fdd_track0(drive)) /* TRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (dma_get_drq(fdc->dma_ch)) /* DRQ */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x04; + /* TODO: + Bit 2: nINDEX (best return always 1 as it goes by very fast) + */ + if (!fdc->seek_dir) /* DIRECTION */ + ret |= 0x01; + if (!writeprot[drive]) /* nWRITEPROT */ + ret |= 0x02; + if (fdd_get_head(drive)) /* HDSEL */ + ret |= 0x08; + if (!fdd_track0(drive)) /* nTRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else ret = 0xff; - else { - if (fdc->flags & FDC_FLAG_UMC) { - drive = real_drive(fdc, fdc->dor & 1); - ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; - } else { - /* TODO: What is this and what is it used for? - It's almost identical to the PS/2 MCA mode. */ - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x70; - ret &= ~(drive ? 0x40 : 0x20); - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + break; + case 1: /* STB */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x80; + switch (drive) { /* -Drive Select 1,0 */ + case 0: + ret |= 0x43; + break; + case 1: + ret |= 0x23; + break; + case 2: + ret |= 0x62; + break; + case 3: + ret |= 0x61; + break; + + default: + break; + } + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0xc0; + ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } else { + if (is486 || !fdc->enable_3f1) + ret = 0xff; + else { + if (fdc->flags & FDC_FLAG_UMC) { + drive = real_drive(fdc, fdc->dor & 1); + ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; + } else { + /* TODO: What is this and what is it used for? + It's almost identical to the PS/2 MCA mode. */ + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x70; + ret &= ~(drive ? 0x40 : 0x20); + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } } } - } - break; - case 2: - ret = fdc->dor; - break; - case 3: - drive = real_drive(fdc, fdc->dor & 3); - /* TODO: FDC_FLAG_PS2_TDR? */ - if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { - /* PS/1 Model 2121 seems return drive type in port - * 0x3f3, despite the 82077AA fdc_t not implementing - * this. This is presumably implemented outside the - * fdc_t on one of the motherboard's support chips. - * - * Confirmed: 00=1.44M 3.5 - * 10=2.88M 3.5 - * 20=1.2M 5.25 - * 30=1.2M 5.25 - * - * as reported by Configur.exe. - */ - if (fdd_is_525(drive)) - ret = 0x20; - else if (fdd_is_ed(drive)) - ret = 0x10; - else - ret = 0x00; - /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ - } else if (!fdc->enh_mode) - ret = 0x20; - else if (fdc->flags & FDC_FLAG_SMC661) - ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); - else - ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); - break; - case 4: /*Status*/ - ret = fdc->stat; - break; - case 5: /*Data*/ - if (fdc->fifointest) { - /* Read FIFO buffer in the test mode (PS/55) */ - ret = fifo_read(fdc->fifo_p); break; - } - if ((fdc->stat & 0xf0) == 0xf0) { - fdc->stat &= ~0x80; - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->data_ready = 0; - ret = fdc->dat; - } else - ret = fifo_read(fdc->fifo_p); + case 2: + ret = fdc->dor; break; - } - if (fdc->paramstogo) { - fdc->stat &= ~0x80; - fdc_log("%i parameters to go\n", fdc->paramstogo); - fdc->paramstogo--; - ret = fdc->res[10 - fdc->paramstogo]; - if (!fdc->paramstogo) - fdc->stat = 0x80; - else - fdc->stat |= 0xC0; - } else if (fdc->dma) { - ret = fdc->dat; - break; - } else { - fdc->stat &= ~0x80; - if (lastbyte) - fdc->stat = 0x80; - lastbyte = 0; - ret = fdc->dat; - fdc->data_ready = 0; - } - fdc->stat &= 0xf0; - break; - case 7: /*Disk change*/ - drive = real_drive(fdc, fdc->dor & 3); - - if (fdc->flags & FDC_FLAG_PS2) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; - ret |= (fdc->dor & 0x08); - ret |= (fdc->noprec << 2); - ret |= (fdc->rate & 0x03); - } else - ret = 0x00; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - ret |= ((fdc->rate & 0x03) << 1); - ret |= fdc_get_densel(fdc, drive); - ret |= 0x78; - } else - ret = 0xf9; - } else { - if (fdc->dor & (0x10 << drive)) { - if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) - ret = 0x00; + case 3: + drive = real_drive(fdc, fdc->dor & 3); + /* TODO: FDC_FLAG_PS2_TDR? */ + if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { + /* PS/1 Model 2121 seems return drive type in port + * 0x3f3, despite the 82077AA fdc_t not implementing + * this. This is presumably implemented outside the + * fdc_t on one of the motherboard's support chips. + * + * Confirmed: 00=1.44M 3.5 + * 10=2.88M 3.5 + * 20=1.2M 5.25 + * 30=1.2M 5.25 + * + * as reported by Configur.exe. + */ + if (fdd_is_525(drive)) + ret = 0x20; + else if (fdd_is_ed(drive)) + ret = 0x10; else + ret = 0x00; + /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ + } else if (!fdc->enh_mode) + ret = 0x20; + else if (fdc->flags & FDC_FLAG_SMC661) + ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); + else + ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); + break; + case 4: /*Status*/ + ret = fdc->stat; + break; + case 5: /*Data*/ + if (fdc->fifointest) { + /* Read FIFO buffer in the test mode (PS/55) */ + ret = fifo_read(fdc->fifo_p); + break; + } + if ((fdc->stat & 0xf0) == 0xf0) { + fdc->stat &= ~0x80; + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->data_ready = 0; + ret = fdc->dat; + } else + ret = fifo_read(fdc->fifo_p); + break; + } + if (fdc->paramstogo) { + fdc->stat &= ~0x80; + fdc_log("%i parameters to go\n", fdc->paramstogo); + fdc->paramstogo--; + ret = fdc->res[10 - fdc->paramstogo]; + if (!fdc->paramstogo) + fdc->stat = 0x80; + else + fdc->stat |= 0xC0; + } else if (fdc->dma) { + ret = fdc->dat; + break; + } else { + fdc->stat &= ~0x80; + if (lastbyte) + fdc->stat = 0x80; + lastbyte = 0; + ret = fdc->dat; + fdc->data_ready = 0; + } + fdc->stat &= 0xf0; + break; + case 7: /*Disk change*/ + drive = real_drive(fdc, fdc->dor & 3); + + if (fdc->flags & FDC_FLAG_PS2) { + if (fdc->dor & (0x10 << drive)) { + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; + ret |= (fdc->dor & 0x08); + ret |= (fdc->noprec << 2); + ret |= (fdc->rate & 0x03); + } else + ret = 0x00; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (fdc->dor & (0x10 << drive)) { ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - } else - ret = 0x00; - if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ - ret ^= 0x80; + ret |= ((fdc->rate & 0x03) << 1); + ret |= fdc_get_densel(fdc, drive); + ret |= 0x78; + } else + ret = 0xf9; + } else { + if (fdc->dor & (0x10 << drive)) { + if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) + ret = 0x00; + else + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; + } else + ret = 0x00; + if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ + ret ^= 0x80; - /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ - if (fdc->flags & FDC_FLAG_TOSHIBA) { - ret |= (3 << 5); - ret |= 0x01; - } else - ret |= 0x7F; - } + /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ + if (fdc->flags & FDC_FLAG_TOSHIBA) { + ret |= (3 << 5); + ret |= 0x01; + } else + ret |= 0x7F; + } - fdc->step = 0; - break; - default: - ret = 0xff; - } + fdc->step = 0; + break; + default: + ret = 0xff; + } fdc_log("[%04X:%08X] Read FDC %04X %02X [%i:%02X]\n", CS, cpu_state.pc, addr, ret, drive, fdc->dor & (0x10 << drive)); return ret; } @@ -1886,7 +1896,7 @@ fdc_callback(void *priv) } else fdc->interrupt = -3; timer_set_delay_u64(&fdc->timer, 2048 * TIMER_USEC); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); return; case 0x0d: /*Format track*/ if (fdc->format_state == 1) { @@ -1929,7 +1939,7 @@ fdc_callback(void *priv) return; case 0x0f: /*Seek*/ fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); // Interrupts and callbacks in the fdd callback function return; case 0x10: /*Version*/ @@ -2863,4 +2873,4 @@ const device_t fdc_ps2_mca_device = { .speed_changed = NULL, .force_redraw = NULL, .config = NULL -}; +}; \ No newline at end of file diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index a0bef7ac6..212317f49 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -79,11 +79,34 @@ typedef struct fdd_t { fdd_t fdd[FDD_NUM]; +enum { + FDD_OP_NONE = 0, + FDD_OP_READ, + FDD_OP_WRITE, + FDD_OP_COMPARE, + FDD_OP_READADDR, + FDD_OP_FORMAT +}; + +typedef struct fdd_pending_op_t { + int pending; + int op; + int sector; + int track; + int side; + int density; + int sector_size; + uint8_t fill; +} fdd_pending_op_t; + +static fdd_pending_op_t fdd_pending[FDD_NUM]; + char floppyfns[FDD_NUM][512]; char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; pc_timer_t fdd_poll_time[FDD_NUM]; pc_timer_t fdd_seek_timer[FDD_NUM]; +int fdd_seek_in_progress[FDD_NUM] = { 0, 0, 0, 0 }; static int fdd_notfound = 0; static int driveloaders[FDD_NUM]; @@ -293,10 +316,49 @@ fdd_seek_complete_callback(void *priv) { DRIVE *drive = (DRIVE *) priv; + fdd_seek_in_progress[drive->id] = 0; + fdd_log("fdd_seek_complete_callback(drive=%d) - TIMER FIRED! seek_in_progress=1\n", drive->id); fdd_log("Notifying FDC of seek completion\n"); fdd_do_seek(drive->id, fdd[drive->id].track); - fdc_seek_complete_interrupt(fdd_fdc, drive->id); + + int had_pending = fdd_pending[drive->id].pending; + if (had_pending) { + fdd_pending_op_t *po = &fdd_pending[drive->id]; + fdd_log("Starting deferred op %d after seek on drive %d (trk=%d, side=%d, sec=%d)\n", + po->op, drive->id, po->track, po->side, po->sector); + + switch (po->op) { + case FDD_OP_READ: + if (drives[drive->id].readsector) + drives[drive->id].readsector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_WRITE: + if (drives[drive->id].writesector) + drives[drive->id].writesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_COMPARE: + if (drives[drive->id].comparesector) + drives[drive->id].comparesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_READADDR: + if (drives[drive->id].readaddress) + drives[drive->id].readaddress(drive->id, po->side, po->density); + break; + case FDD_OP_FORMAT: + if (drives[drive->id].format) + drives[drive->id].format(drive->id, po->side, po->density, po->fill); + break; + default: + break; + } + + po->pending = 0; + po->op = FDD_OP_NONE; + } + + if (!had_pending) + fdc_seek_complete_interrupt(fdd_fdc, drive->id); } void @@ -306,6 +368,11 @@ fdd_seek(int drive, int track_diff) if (!track_diff) return; + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek already in progress for drive %d, ignoring new seek request\n", drive); + return; + } + int old_track = fdd[drive].track; fdd[drive].track += track_diff; @@ -327,12 +394,14 @@ fdd_seek(int drive, int track_diff) /* Multi-track seek */ fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track); } - + if (old_track + track_diff < 0) { fdd_do_seek(drive, fdd[drive].track); return; } + fdd_seek_in_progress[drive] = 1; + if (!fdd_seek_timer[drive].callback) { timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0); } @@ -629,7 +698,7 @@ fdd_set_motor_enable(int drive, int motor_enable) { fdd_log("fdd_set_motor_enable(%d, %d)\n", drive, motor_enable); fdd_audio_set_motor_enable(drive, motor_enable); - + if (motor_enable && !motoron[drive]) { timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(drive)); } else if (!motor_enable && motoron[drive]) { @@ -700,6 +769,22 @@ void fdd_readsector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_readsector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring READ (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READ, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].readsector) drives[drive].readsector(drive, sector, track, side, density, sector_size); else @@ -710,6 +795,22 @@ void fdd_writesector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_writesector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring WRITE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_WRITE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].writesector) drives[drive].writesector(drive, sector, track, side, density, sector_size); else @@ -719,6 +820,21 @@ fdd_writesector(int drive, int sector, int track, int side, int density, int sec void fdd_comparesector(int drive, int sector, int track, int side, int density, int sector_size) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring COMPARE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_COMPARE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].comparesector) drives[drive].comparesector(drive, sector, track, side, density, sector_size); else @@ -728,6 +844,19 @@ fdd_comparesector(int drive, int sector, int track, int side, int density, int s void fdd_readaddress(int drive, int side, int density) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring READADDRESS (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READADDR, + .track = fdd[drive].track, + .side = side, + .density = density + }; + return; + } + if (drives[drive].readaddress) drives[drive].readaddress(drive, side, density); } @@ -735,6 +864,20 @@ fdd_readaddress(int drive, int side, int density) void fdd_format(int drive, int side, int density, uint8_t fill) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring FORMAT (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_FORMAT, + .track = fdd[drive].track, + .side = side, + .density = density, + .fill = fill + }; + return; + } + if (drives[drive].format) drives[drive].format(drive, side, density, fill); else @@ -777,7 +920,7 @@ fdd_init(void) if (fdd_sounds_enabled) { fdd_audio_init(); - } + } } void From 836f855683bc822e113ab1b715d5579dc74c5a6a Mon Sep 17 00:00:00 2001 From: Toni Riikonen Date: Sat, 27 Sep 2025 11:51:38 +0300 Subject: [PATCH 264/274] Fixes #6220 floppy disk issues with OS/2 and NT 3.1 systems (#6232) * Initial spindle emulation working for windows atleast * Spingle motor spin-up, spin-down implemented with smooth transitions to motor-on loop. * Moved fdd audio emulation to a separate file * Multiple drives sound emulation * Single sector movement sound emulations implemented * Rename project to Immersive86Box and update details Updated README to reflect the new project name and added details about the Immersive86Box features and future plans. * Revise contribution guidelines in CONTRIBUTING.md * Update vulnerability reporting instructions * System fan-sound next feature after basic fdd sound emulation is ready * v0.5 multitrack audio seek sfx * Removed unnecessary stuff * no .vs folder for git * Added currently used fdd sound effects and readme.txt for source of the files and intallation instructions * Add audio emulation installation instructions Added instructions for audio emulation installation. * Code and audio samples merged * Simplify audio emulation installation instructions * FDC seeking fixed, not instant anymore drive is set to busy during the operation and when it's finished at call fdc to set the appropriate fdc flags. Also added time logic to fdd to calculate seek duration and a callback function for it. * FDD sound samples volume control * Menu options to enable / disable fdd sound for all drives. DISABLE_FDD_AUDIO definition added, to disable the feature via cmake/build. * Revert readme etc. changes * Revert "Revise contribution guidelines in CONTRIBUTING.md" This reverts commit 98a0478225bbf8bf0fb0e7edfd5c00636ecc0eed. * Revert "Update vulnerability reporting instructions" This reverts commit 7d32cb659b018b26bdaa4a1e06ee9c3d09278aac. * Fixed merge issue * Removed excess files * Fixed PCJr seeking not to break the FDC implementation. Now seeking will take the "correct" amount of time for each system and the seek time is based on the track count. E.g. 40 track FDD system causes 40 track seek time to be 80/40 * 6ms * 40 tracks + 50ms = 480ms + 50ms -> 530ms. 80 track system full seek is 80/80 * 6ms * 80 + 50ms = 530ms, 40 track seek would take 240 + 50 = 290ms. * Fixed PS/1, PS/2 and PS/55 FDD issues. * FDD_AUDIO: Updating samples looked in executablePath/samples and if now found there, looks in the executable directory * Updated installation instructions * Removed samples path strcat use * fdd_audio 5.25 samples and support added * FDD audio timing/volume tunings * Timing fixes for authentity and special longer timings for PCJr * Fixed second drive motor keeps running when first drive is only accessed. * Fixed PCJr random failure issue, timings * CodeQL fix for load_wav-function. Check the filename to be proper filename * Revert "Fixed second drive motor keeps running when first drive is only accessed." This reverts commit 307b173ae7d40c9efafed8d432e01cce9808b111. * Teac 5.25" drive samples added. Added per drive audio selection to FDD settings. * Fixes merge problem * Fixes #6220: OS/2 Warp 3.0 install disk issue and NT 3.1 floppy disk issues. Changed the fdc->stat bit to 0x10 which states fdc busy. Previous implementation with seek time 0, set the ready flag 0x80 immediadely - which was correct for that time, but now as the drive is busy during seek, the value should 0x10. Implemented a backup for fdd commands during fdd seek to be processes after the seek is completed. --------- Co-authored-by: Toni Riikonen --- src/floppy/fdc.c | 1522 ++++++++++++++++++++-------------------- src/floppy/fdd.c | 151 +++- src/floppy/fdd_audio.c | 1 + 3 files changed, 914 insertions(+), 760 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index bcf4a1299..212bf9ce0 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -184,7 +184,7 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x80; + fdc->stat = 0x10; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->head = 0; @@ -470,9 +470,9 @@ uint8_t fdc_get_shadow(fdc_t *fdc) { uint8_t ret = (fdc->rate & 0x03) | - ((fdc->pretrk & 0x07) << 2) | - (fdc->power_down ? 0x40 : 0x00) | - ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); + ((fdc->pretrk & 0x07) << 2) | + (fdc->power_down ? 0x40 : 0x00) | + ((fdc_read(0x03f2, fdc) & 0x04) ? 0x80 : 0x00); return ret; } @@ -532,36 +532,36 @@ fdc_update_rate(fdc_t *fdc, int drive) fdc->enh_mode && !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 500; else if ((fdc->rwc[drive] == 3) && fdc->enh_mode && - !(fdc->flags & FDC_FLAG_SMC661)) + !(fdc->flags & FDC_FLAG_SMC661)) fdc->bit_rate = 250; else switch (fdc->rate) { - default: - break; - case 0: /*High density*/ - fdc->bit_rate = 500; - break; - case 1: /*Double density (360 rpm)*/ - switch (fdc->drvrate[drive]) { - default: - break; - case 0: - fdc->bit_rate = 300; - break; - case 1: - fdc->bit_rate = 500; - break; - case 2: - fdc->bit_rate = 2000; - break; - } - break; - case 2: /*Double density*/ - fdc->bit_rate = 250; - break; - case 3: /*Extended density*/ - fdc->bit_rate = 1000; - break; - } + default: + break; + case 0: /*High density*/ + fdc->bit_rate = 500; + break; + case 1: /*Double density (360 rpm)*/ + switch (fdc->drvrate[drive]) { + default: + break; + case 0: + fdc->bit_rate = 300; + break; + case 1: + fdc->bit_rate = 500; + break; + case 2: + fdc->bit_rate = 2000; + break; + } + break; + case 2: /*Double density*/ + fdc->bit_rate = 250; + break; + case 3: /*Extended density*/ + fdc->bit_rate = 1000; + break; + } fdc->bitcell_period = (1000000 / fdc->bit_rate) * 2; /*Bitcell period in ns*/ } @@ -695,7 +695,7 @@ void fdc_seek(fdc_t *fdc, int drive, int params) { fdd_seek(real_drive(fdc, drive), params); - fdc->stat |= (1 << fdc->drive); + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << fdc->drive); } static void @@ -719,8 +719,10 @@ fdc_io_command_phase1(fdc_t *fdc, int out) fdc->dtl = fdc->params[7]; fdc->rw_track = fdc->params[1]; + int implied_seek = 0; if (fdc->config & 0x40) { if (fdc->rw_track != fdc->pcn[fdc->params[0] & 3]) { + implied_seek = 1; fdc_seek(fdc, fdc->drive, ((int) fdc->rw_track) - ((int) fdc->pcn[fdc->params[0] & 3])); fdc->pcn[fdc->params[0] & 3] = fdc->rw_track; } @@ -730,6 +732,12 @@ fdc_io_command_phase1(fdc_t *fdc, int out) ui_sb_update_icon_write(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); else ui_sb_update_icon(SB_FLOPPY | real_drive(fdc, fdc->drive), 1); + + if (implied_seek) { + fdc->stat = (fdc->stat & 0x0F) | 0x10 | (1 << real_drive(fdc, fdc->drive)); /* CB=1, per-drive busy */ + return; + } + fdc->stat = out ? 0x10 : 0x50; if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) { fdc->stat |= 0x20; @@ -791,7 +799,7 @@ fdc_soft_reset(fdc_t *fdc) } fdc_ctrl_reset(fdc); - } + } } static void @@ -806,565 +814,566 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) switch (addr & 7) { - case 0: - return; - case 1: - return; - case 2: /*DOR*/ - if (fdc->flags & FDC_FLAG_PCJR) { - if ((fdc->dor & 0x40) && !(val & 0x40)) { - timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); - fdc->watchdog_count = 1000; - picintc(1 << fdc->irq); - } - if ((val & 0x80) && !(fdc->dor & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -1; - ui_sb_update_icon(SB_FLOPPY | 0, 0); - ui_sb_update_icon_write(SB_FLOPPY | 0, 0); - fdc_ctrl_reset(fdc); - } - if (!fdd_get_flags(0)) - val &= 0xfe; - fdd_set_motor_enable(0, val & 0x01); - fdc->st0 &= ~0x07; - fdc->st0 |= (fdd_get_head(0) ? 4 : 0); - } else { - /* - Writing this bit to logic "1" will enable the DRQ, - nDACK, TC and FINTR outputs. This bit being a - logic "0" will disable the nDACK and TC inputs, and - hold the DRQ and FINTR outputs in a high - impedance state. - */ - if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { - fdc->tc = 1; - fdc->fintr = 0; - picintc(1 << fdc->irq); - } - if (!(val & 4)) { - fdd_stop(real_drive(fdc, val & 3)); - fdc->stat = 0x00; - fdc->pnum = fdc->ptot = 0; - } - if ((val & 4) && !(fdc->dor & 4)) - fdc_soft_reset(fdc); - /* We can now simplify this since each motor now spins separately. */ - for (int i = 0; i < FDD_NUM; i++) { - drive_num = real_drive(fdc, i); - if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) - val &= ~(0x10 << drive_num); - else - fdd_set_motor_enable(i, (val & (0x10 << drive_num))); - } - drive_num = real_drive(fdc, val & 0x03); - current_drive = drive_num; - fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); - } - fdc->dor = val; - return; - case 3: /* TDR */ - if (fdc->enh_mode) { - if (fdc->flags & FDC_FLAG_SMC661) { - fdc_set_swap(fdc, !!(val & 0x20)); - fdc_update_densel_force(fdc, (val & 0x18) >> 3); + if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) + switch (addr & 7) { + case 0: + return; + case 1: + return; + case 2: /*DOR*/ + if (fdc->flags & FDC_FLAG_PCJR) { + if ((fdc->dor & 0x40) && !(val & 0x40)) { + timer_set_delay_u64(&fdc->watchdog_timer, 1000 * TIMER_USEC); + fdc->watchdog_count = 1000; + picintc(1 << fdc->irq); + } + if ((val & 0x80) && !(fdc->dor & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -1; + ui_sb_update_icon(SB_FLOPPY | 0, 0); + ui_sb_update_icon_write(SB_FLOPPY | 0, 0); + fdc_ctrl_reset(fdc); + } + if (!fdd_get_flags(0)) + val &= 0xfe; + fdd_set_motor_enable(0, val & 0x01); + fdc->st0 &= ~0x07; + fdc->st0 |= (fdd_get_head(0) ? 4 : 0); } else { - drive = real_drive(fdc, fdc->dor & 3); - fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + /* + Writing this bit to logic "1" will enable the DRQ, + nDACK, TC and FINTR outputs. This bit being a + logic "0" will disable the nDACK and TC inputs, and + hold the DRQ and FINTR outputs in a high + impedance state. + */ + if (!(val & 8) && (fdc->dor & 8) && !(fdc->flags & FDC_FLAG_PS2_MCA)) { + fdc->tc = 1; + fdc->fintr = 0; + picintc(1 << fdc->irq); + } + if (!(val & 4)) { + fdd_stop(real_drive(fdc, val & 3)); + fdc->stat = 0x00; + fdc->pnum = fdc->ptot = 0; + } + if ((val & 4) && !(fdc->dor & 4)) + fdc_soft_reset(fdc); + /* We can now simplify this since each motor now spins separately. */ + for (int i = 0; i < FDD_NUM; i++) { + drive_num = real_drive(fdc, i); + if ((!fdd_get_flags(drive_num)) || (drive_num >= FDD_NUM)) + val &= ~(0x10 << drive_num); + else + fdd_set_motor_enable(i, (val & (0x10 << drive_num))); + } + drive_num = real_drive(fdc, val & 0x03); + current_drive = drive_num; + fdc->st0 = (fdc->st0 & 0xf8) | (val & 0x03) | (fdd_get_head(drive_num) ? 4 : 0); } - } - /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) - The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. - If it fails, then floppy drives will be treated as DD drives. */ - if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (val & 0x04) { - fdc->tfifo = 8; - fdc->fifointest = 1; - } else { - fdc->tfifo = 1; - fdc->fifointest = 0; + fdc->dor = val; + return; + case 3: /* TDR */ + if (fdc->enh_mode) { + if (fdc->flags & FDC_FLAG_SMC661) { + fdc_set_swap(fdc, !!(val & 0x20)); + fdc_update_densel_force(fdc, (val & 0x18) >> 3); + } else { + drive = real_drive(fdc, fdc->dor & 3); + fdc_update_rwc(fdc, drive, (val & 0x30) >> 4); + } } - fifo_reset(fdc->fifo_p); - fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); - fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); - } - return; - case 4: /* DSR */ - if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { - if (!(val & 0x80)) { - timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); - fdc->interrupt = -6; + /* Bit 2: FIFO test mode (PS/55 5550-S,T only. Undocumented) + The Power-on Self Test of PS/55 writes and verifies 8 bytes of FIFO buffer through I/O 3F5h. + If it fails, then floppy drives will be treated as DD drives. */ + if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (val & 0x04) { + fdc->tfifo = 8; + fdc->fifointest = 1; + } else { + fdc->tfifo = 1; + fdc->fifointest = 0; + } + fifo_reset(fdc->fifo_p); + fifo_set_len(fdc->fifo_p, fdc->tfifo + 1); + fifo_set_trigger_len(fdc->fifo_p, fdc->tfifo + 1); } - if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) - fdc_soft_reset(fdc); - } - fdc->dsr = val; - return; - case 5: /*Command register*/ - if (fdc->fifointest) { - /* Write FIFO buffer in the test mode (PS/55) */ - fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); - fifo_write(val, fdc->fifo_p); - if (fifo_get_full(fdc->fifo_p)) - fdc->stat &= ~0x80; - break; - } - if ((fdc->stat & 0xf0) == 0xb0) { - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->dat = val; - fdc->stat &= ~0x80; - } else { + return; + case 4: /* DSR */ + if (!(fdc->flags & FDC_FLAG_NO_DSR_RESET)) { + if (!(val & 0x80)) { + timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC); + fdc->interrupt = -6; + } + if (fdc->power_down || ((val & 0x80) && !(fdc->dsr & 0x80))) + fdc_soft_reset(fdc); + } + fdc->dsr = val; + return; + case 5: /*Command register*/ + if (fdc->fifointest) { + /* Write FIFO buffer in the test mode (PS/55) */ + fdc_log("FIFO buffer position = %X\n", ((fifo_t *) fdc->fifo_p)->end); fifo_write(val, fdc->fifo_p); if (fifo_get_full(fdc->fifo_p)) fdc->stat &= ~0x80; + break; } - break; - } - if (fdc->pnum == fdc->ptot) { - if ((fdc->stat & 0xf0) != 0x80) { - /* If bit 4 of the MSR is set, or the MSR is 0x00, - the fdc_t is NOT in the command phase, therefore - do NOT accept commands. */ - return; + if ((fdc->stat & 0xf0) == 0xb0) { + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->dat = val; + fdc->stat &= ~0x80; + } else { + fifo_write(val, fdc->fifo_p); + if (fifo_get_full(fdc->fifo_p)) + fdc->stat &= ~0x80; + } + break; } + if (fdc->pnum == fdc->ptot) { + if ((fdc->stat & 0xf0) != 0x80) { + /* If bit 4 of the MSR is set, or the MSR is 0x00, + the fdc_t is NOT in the command phase, therefore + do NOT accept commands. */ + return; + } - fdc->stat &= 0xf; + fdc->stat &= 0xf; - fdc->tc = 0; - fdc->data_ready = 0; + fdc->tc = 0; + fdc->data_ready = 0; - fdc->command = val; - fdc->stat |= 0x10; - fdc_log("Starting FDC command %02X\n", fdc->command); - fdc->error = 0; + fdc->command = val; + fdc->stat |= 0x10; + fdc_log("Starting FDC command %02X\n", fdc->command); + fdc->error = 0; - if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || - ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || - ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || - ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || - ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) - fdc->processed_cmd = fdc->command & 0x1f; - else - fdc->processed_cmd = fdc->command; + if (((fdc->command & 0x1f) == 0x02) || ((fdc->command & 0x1f) == 0x05) || + ((fdc->command & 0x1f) == 0x06) || ((fdc->command & 0x1f) == 0x0a) || + ((fdc->command & 0x1f) == 0x0c) || ((fdc->command & 0x1f) == 0x0d) || + ((fdc->command & 0x1f) == 0x11) || ((fdc->command & 0x1f) == 0x16) || + ((fdc->command & 0x1f) == 0x19) || ((fdc->command & 0x1f) == 0x1d)) + fdc->processed_cmd = fdc->command & 0x1f; + else + fdc->processed_cmd = fdc->command; - switch (fdc->processed_cmd) { - case 0x01: /*Mode*/ - if (fdc->flags & FDC_FLAG_NSC) { - fdc->pnum = 0; - fdc->ptot = 4; + switch (fdc->processed_cmd) { + case 0x01: /*Mode*/ + if (fdc->flags & FDC_FLAG_NSC) { + fdc->pnum = 0; + fdc->ptot = 4; + fdc->stat |= 0x90; + fdc->format_state = 0; + } else + fdc_bad_command(fdc); + break; + case 0x02: /*Read track*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->pnum = 0; + fdc->ptot = 8; fdc->stat |= 0x90; - fdc->format_state = 0; - } else - fdc_bad_command(fdc); - break; - case 0x02: /*Read track*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x03: /*Specify*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x04: /*Sense drive status*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x05: /*Write data*/ - case 0x09: /*Write deleted data*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x06: /*Read data*/ - case 0x0c: /*Read deleted data*/ - case 0x11: /*Scan equal*/ - case 0x19: /*Scan low or equal*/ - case 0x16: /*Verify*/ - case 0x1d: /*Scan high or equal*/ - fdc->satisfying_sectors = 0; - fdc->sc = 0; - fdc->wrong_am = 0; - fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; - if ((fdc->command & 0x1F) == 0x16) - fdc->deleted = 2; - fdc->deleted |= (fdc->command & 0x20); - fdc->pnum = 0; - fdc->ptot = 8; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x17: /*Powerdown mode*/ - if (!(fdc->flags & FDC_FLAG_ALI)) { - fdc_bad_command(fdc); + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - } - fallthrough; - case 0x07: /*Recalibrate*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - break; - case 0x08: /*Sense interrupt status*/ - fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); - fdc->lastdrive = fdc->drive; - fdc_sis(fdc); - break; - case 0x0a: /*Read sector ID*/ - fdc->pnum = 0; - fdc->ptot = 1; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - break; - case 0x0d: /*Format track*/ - fdc->pnum = 0; - fdc->ptot = 5; - fdc->stat |= 0x90; - fdc->mfm = (fdc->command & 0x40) ? 1 : 0; - fdc->format_state = 0; - break; - case 0x0e: /*Dump registers*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); + case 0x03: /*Specify*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = 0x0e; - fdc_callback(fdc); - break; - case 0x0f: /*Seek*/ - fdc->pnum = 0; - fdc->ptot = 2; - fdc->stat |= 0x90; - break; - case 0x18: /*NSC*/ - if (!(fdc->flags & FDC_FLAG_NSC)) { - fdc_bad_command(fdc); - break; - } - fallthrough; - case 0x10: /*Get version*/ - case 0x14: /*Unlock*/ - case 0x94: /*Lock*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); - break; - } - fdc->lastdrive = fdc->drive; - fdc->interrupt = fdc->command; - fdc_callback(fdc); - break; - case 0x12: /*Set perpendicular mode*/ - if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + case 0x04: /*Sense drive status*/ fdc->pnum = 0; fdc->ptot = 1; fdc->stat |= 0x90; - } else - fdc_bad_command(fdc); - break; - case 0x13: /*Configure*/ - if (fdc->flags & FDC_FLAG_NEC) { - fdc_bad_command(fdc); break; - } - fdc->pnum = 0; - fdc->ptot = 3; - fdc->stat |= 0x90; - break; - default: - fdc_bad_command(fdc); - break; - } - } else { - fdc->stat = 0x10 | (fdc->stat & 0xf); - fdc->params[fdc->pnum++] = val; - if (fdc->pnum == 1) { - if (command_has_drivesel[fdc->command & 0x1F]) { - if (fdc->flags & FDC_FLAG_PCJR) - fdc->drive = 0; - else - fdc->drive = fdc->dor & 3; - fdc->rw_drive = fdc->params[0] & 3; - if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) - fdc->stat |= (1 << real_drive(fdc, fdc->drive)); - } - } - if (fdc->pnum == fdc->ptot) { - fdc_log("Got all params %02X\n", fdc->command); - fifo_reset(fdc->fifo_p); - fdc->interrupt = fdc->processed_cmd; - fdc->reset_stat = 0; - /* Disable timer if enabled. */ - timer_disable(&fdc->timer); - /* Start timer if needed at this point. */ - switch (fdc->interrupt & 0x1f) { - case 0x02: /* Read a track */ - case 0x03: /* Specify */ - case 0x0a: /* Read sector ID */ - case 0x05: /* Write data */ - case 0x06: /* Read data */ - case 0x09: /* Write deleted data */ - case 0x0c: /* Read deleted data */ - case 0x11: /* Scan equal */ - case 0x12: /* Perpendicular mode */ - case 0x16: /* Verify */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - /* Do nothing. */ + case 0x05: /*Write data*/ + case 0x09: /*Write deleted data*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 9) ? 1 : 0; + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - case 0x07: /* Recalibrate */ - case 0x0f: /* Seek */ - if (fdc->flags & FDC_FLAG_PCJR) - timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); - else - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + case 0x06: /*Read data*/ + case 0x0c: /*Read deleted data*/ + case 0x11: /*Scan equal*/ + case 0x19: /*Scan low or equal*/ + case 0x16: /*Verify*/ + case 0x1d: /*Scan high or equal*/ + fdc->satisfying_sectors = 0; + fdc->sc = 0; + fdc->wrong_am = 0; + fdc->deleted = ((fdc->command & 0x1F) == 0xC) ? 1 : 0; + if ((fdc->command & 0x1F) == 0x16) + fdc->deleted = 2; + fdc->deleted |= (fdc->command & 0x20); + fdc->pnum = 0; + fdc->ptot = 8; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; break; - default: - timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); - break; - } - /* Process the firt phase of the command. */ - switch (fdc->processed_cmd) { - case 0x02: /* Read a track */ - fdc_io_command_phase1(fdc, 0); - fdc->read_track_sector.id.c = fdc->params[1]; - fdc->read_track_sector.id.h = fdc->params[2]; - fdc->read_track_sector.id.r = 1; - fdc->read_track_sector.id.n = fdc->params[4]; - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; + case 0x17: /*Powerdown mode*/ + if (!(fdc->flags & FDC_FLAG_ALI)) { + fdc_bad_command(fdc); + break; } - fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x03: /* Specify */ - fdc->stat = 0x80; - fdc->specify[0] = fdc->params[0]; - fdc->specify[1] = fdc->params[1]; - fdc->dma = (fdc->specify[1] & 1) ^ 1; - if (!fdc->dma) - dma_set_drq(fdc->dma_ch, 0); - break; - case 0x04: /*Sense drive status*/ - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - break; - case 0x05: /* Write data */ - case 0x09: /* Write deleted data */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x11: /* Scan equal */ - case 0x19: /* Scan low or equal */ - case 0x1d: /* Scan high or equal */ - fdc_io_command_phase1(fdc, 1); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); - break; - case 0x16: /* Verify */ - if (fdc->params[0] & 0x80) - fdc->sc = fdc->params[7]; fallthrough; - case 0x06: /* Read data */ - case 0x0c: /* Read deleted data */ - fdc_io_command_phase1(fdc, 0); - fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); - if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { - fdc_noidam(fdc); - return; - } - if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { - /* DMA is in verify mode, treat this like a VERIFY command. */ - fdc_log("Verify-mode read!\n"); - fdc->tc = 1; - fdc->deleted |= 2; - } - fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + case 0x07: /*Recalibrate*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; break; - - case 0x07: /* Recalibrate */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << real_drive(fdc, fdc->drive)); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->st0 = fdc->params[0] & 3; - fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; - fdc->st0 |= 0x80; - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { - fdc_log("Failed recalibrate\n"); - if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) - fdc->st0 = 0x70 | (fdc->params[0] & 3); - else - fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->pcn[fdc->params[0] & 3] = 0; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x08: /*Sense interrupt status*/ + fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat); + fdc->lastdrive = fdc->drive; + fdc_sis(fdc); + break; + case 0x0a: /*Read sector ID*/ + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + break; + case 0x0d: /*Format track*/ + fdc->pnum = 0; + fdc->ptot = 5; + fdc->stat |= 0x90; + fdc->mfm = (fdc->command & 0x40) ? 1 : 0; + fdc->format_state = 0; + break; + case 0x0e: /*Dump registers*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) - fdc_seek(fdc, fdc->drive, -fdc->max_track); - fdc_log("Recalibrating...\n"); - fdc->seek_dir = fdc->step = 1; + fdc->lastdrive = fdc->drive; + fdc->interrupt = 0x0e; + fdc_callback(fdc); break; - case 0x0a: /* Read sector ID */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { - fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) - fdc->stat = 0x70; - else - fdc->stat = 0x50; + case 0x0f: /*Seek*/ + fdc->pnum = 0; + fdc->ptot = 2; + fdc->stat |= 0x90; + break; + case 0x18: /*NSC*/ + if (!(fdc->flags & FDC_FLAG_NSC)) { + fdc_bad_command(fdc); + break; + } + fallthrough; + case 0x10: /*Get version*/ + case 0x14: /*Unlock*/ + case 0x94: /*Lock*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); + break; + } + fdc->lastdrive = fdc->drive; + fdc->interrupt = fdc->command; + fdc_callback(fdc); + break; + case 0x12: /*Set perpendicular mode*/ + if ((fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_PCJR)) { + fdc->pnum = 0; + fdc->ptot = 1; + fdc->stat |= 0x90; } else - fdc_noidam(fdc); + fdc_bad_command(fdc); break; - case 0x0d: /* Format */ - fdc_rate(fdc, fdc->drive); - fdc->head = (fdc->params[0] & 4) ? 1 : 0; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - fdc->gap = fdc->params[3]; - fdc->format_sectors = fdc->params[2]; - fdc->format_n = fdc->params[1]; - fdc->format_state = 1; - fdc->stat = 0x10; - break; - case 0x0f: /* Seek */ - fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = (1 << fdc->drive); - if (!(fdc->flags & FDC_FLAG_PCJR)) - fdc->stat |= 0x80; - fdc->head = 0; /* TODO: See if this is correct. */ - fdc->st0 = fdc->params[0] & 0x03; - fdc->st0 |= (fdc->params[0] & 4); - fdc->st0 |= 0x80; - fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); - drive_num = real_drive(fdc, fdc->drive); - /* Three conditions under which the command should fail. */ - if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { - /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->command & 0x80) { - if (fdc->command & 0x40) - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - else - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } else - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } + case 0x13: /*Configure*/ + if (fdc->flags & FDC_FLAG_NEC) { + fdc_bad_command(fdc); break; } - if (fdc->command & 0x80) { - if (fdc->params[1]) { - if (fdc->command & 0x40) { - /* Relative seek inwards. */ - fdc->seek_dir = 0; - fdc_seek(fdc, fdc->drive, fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; - } else { - /* Relative seek outwards. */ - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, -fdc->params[1]); - fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; - } - fdc->step = 1; - } else { - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - } else { - fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); - if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { - fdc_log("Failed seek\n"); - fdc->st0 = 0x20 | (fdc->params[0] & 3); - if (fdc->flags & FDC_FLAG_PCJR) { - fdc->fintr = 1; - fdc->interrupt = -4; - } else { - timer_disable(&fdc->timer); - fdc->interrupt = -3; - fdc_callback(fdc); - } - break; - } - if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) - fdc->seek_dir = 0; - else - fdc->seek_dir = 1; - fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); - fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; - fdc->step = 1; - } + fdc->pnum = 0; + fdc->ptot = 3; + fdc->stat |= 0x90; break; - case 0x12: /* Perpendicular mode */ - fdc->stat = 0x80; - if (fdc->params[0] & 0x80) - fdc->perp = fdc->params[0] & 0x3f; - else { - fdc->perp &= 0xfc; - fdc->perp |= (fdc->params[0] & 0x03); - } - return; - default: + fdc_bad_command(fdc); break; } - } else - fdc->stat = 0x90 | (fdc->stat & 0xf); - } - return; - case 7: - if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) - return; - fdc->rate = val & 0x03; - if (fdc->flags & FDC_FLAG_PS2) - fdc->noprec = !!(val & 0x04); - return; + } else { + fdc->stat = 0x10 | (fdc->stat & 0xf); + fdc->params[fdc->pnum++] = val; + if (fdc->pnum == 1) { + if (command_has_drivesel[fdc->command & 0x1F]) { + if (fdc->flags & FDC_FLAG_PCJR) + fdc->drive = 0; + else + fdc->drive = fdc->dor & 3; + fdc->rw_drive = fdc->params[0] & 3; + if (((fdc->command & 0x1F) == 7) || ((fdc->command & 0x1F) == 15)) + fdc->stat |= (1 << real_drive(fdc, fdc->drive)); + } + } + if (fdc->pnum == fdc->ptot) { + fdc_log("Got all params %02X\n", fdc->command); + fifo_reset(fdc->fifo_p); + fdc->interrupt = fdc->processed_cmd; + fdc->reset_stat = 0; + /* Disable timer if enabled. */ + timer_disable(&fdc->timer); + /* Start timer if needed at this point. */ + switch (fdc->interrupt & 0x1f) { + case 0x02: /* Read a track */ + case 0x03: /* Specify */ + case 0x0a: /* Read sector ID */ + case 0x05: /* Write data */ + case 0x06: /* Read data */ + case 0x09: /* Write deleted data */ + case 0x0c: /* Read deleted data */ + case 0x11: /* Scan equal */ + case 0x12: /* Perpendicular mode */ + case 0x16: /* Verify */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + /* Do nothing. */ + break; + case 0x07: /* Recalibrate */ + case 0x0f: /* Seek */ + if (fdc->flags & FDC_FLAG_PCJR) + timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC); + else + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + default: + timer_set_delay_u64(&fdc->timer, 256 * TIMER_USEC); + break; + } + /* Process the firt phase of the command. */ + switch (fdc->processed_cmd) { + case 0x02: /* Read a track */ + fdc_io_command_phase1(fdc, 0); + fdc->read_track_sector.id.c = fdc->params[1]; + fdc->read_track_sector.id.h = fdc->params[2]; + fdc->read_track_sector.id.r = 1; + fdc->read_track_sector.id.n = fdc->params[4]; + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_readsector(real_drive(fdc, fdc->drive), SECTOR_FIRST, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x03: /* Specify */ + fdc->stat = 0x80; + fdc->specify[0] = fdc->params[0]; + fdc->specify[1] = fdc->params[1]; + fdc->dma = (fdc->specify[1] & 1) ^ 1; + if (!fdc->dma) + dma_set_drq(fdc->dma_ch, 0); + break; + case 0x04: /*Sense drive status*/ + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + break; + case 0x05: /* Write data */ + case 0x09: /* Write deleted data */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_writesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x11: /* Scan equal */ + case 0x19: /* Scan low or equal */ + case 0x1d: /* Scan high or equal */ + fdc_io_command_phase1(fdc, 1); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + fdd_comparesector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; + case 0x16: /* Verify */ + if (fdc->params[0] & 0x80) + fdc->sc = fdc->params[7]; + fallthrough; + case 0x06: /* Read data */ + case 0x0c: /* Read deleted data */ + fdc_io_command_phase1(fdc, 0); + fdc_log("Reading sector (drive %i) (%i) (%i %i %i %i) (%i %i %i)\n", fdc->drive, fdc->params[0], fdc->params[1], fdc->params[2], fdc->params[3], fdc->params[4], fdc->params[5], fdc->params[6], fdc->params[7]); + if ((fdc->head & 0x01) && !fdd_is_double_sided(real_drive(fdc, fdc->drive))) { + fdc_noidam(fdc); + return; + } + if (((dma_mode(2) & 0x0C) == 0x00) && !(fdc->flags & FDC_FLAG_PCJR) && fdc->dma) { + /* DMA is in verify mode, treat this like a VERIFY command. */ + fdc_log("Verify-mode read!\n"); + fdc->tc = 1; + fdc->deleted |= 2; + } + fdd_readsector(real_drive(fdc, fdc->drive), fdc->sector, fdc->params[1], fdc->head, fdc->rate, fdc->params[4]); + break; - default: - break; - } + case 0x07: /* Recalibrate */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << real_drive(fdc, fdc->drive)); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->st0 = fdc->params[0] & 3; + fdc->st0 |= fdd_get_head(real_drive(fdc, fdc->drive)) ? 0x04 : 0x00; + fdc->st0 |= 0x80; + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num] || fdd_track0(drive_num)) { + fdc_log("Failed recalibrate\n"); + if ((drive_num >= FDD_NUM) || !fdd_get_flags(drive_num) || !motoron[drive_num]) + fdc->st0 = 0x70 | (fdc->params[0] & 3); + else + fdc->st0 = 0x20 | (fdc->params[0] & 3); + fdc->pcn[fdc->params[0] & 3] = 0; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) + fdc_seek(fdc, fdc->drive, -fdc->max_track); + fdc_log("Recalibrating...\n"); + fdc->seek_dir = fdc->step = 1; + break; + case 0x0a: /* Read sector ID */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + if ((real_drive(fdc, fdc->drive) != 1) || fdc->drv2en) { + fdd_readaddress(real_drive(fdc, fdc->drive), fdc->head, fdc->rate); + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->dma) + fdc->stat = 0x70; + else + fdc->stat = 0x50; + } else + fdc_noidam(fdc); + break; + case 0x0d: /* Format */ + fdc_rate(fdc, fdc->drive); + fdc->head = (fdc->params[0] & 4) ? 1 : 0; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + fdc->gap = fdc->params[3]; + fdc->format_sectors = fdc->params[2]; + fdc->format_n = fdc->params[1]; + fdc->format_state = 1; + fdc->stat = 0x10; + break; + case 0x0f: /* Seek */ + fdc->rw_drive = fdc->params[0] & 3; + fdc->stat = 0x10 | (1 << fdc->drive); + if (!(fdc->flags & FDC_FLAG_PCJR)) + fdc->stat |= 0x80; + fdc->head = 0; /* TODO: See if this is correct. */ + fdc->st0 = fdc->params[0] & 0x03; + fdc->st0 |= (fdc->params[0] & 4); + fdc->st0 |= 0x80; + fdd_set_head(real_drive(fdc, fdc->drive), (fdc->params[0] & 4) ? 1 : 0); + drive_num = real_drive(fdc, fdc->drive); + /* Three conditions under which the command should fail. */ + if (!fdd_get_flags(drive_num) || (drive_num >= FDD_NUM) || !motoron[drive_num]) { + /* Yes, failed SEEK's still report success, unlike failed RECALIBRATE's. */ + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->command & 0x80) { + if (fdc->command & 0x40) + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + else + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } else + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->command & 0x80) { + if (fdc->params[1]) { + if (fdc->command & 0x40) { + /* Relative seek inwards. */ + fdc->seek_dir = 0; + fdc_seek(fdc, fdc->drive, fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] += fdc->params[1]; + } else { + /* Relative seek outwards. */ + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, -fdc->params[1]); + fdc->pcn[fdc->params[0] & 3] -= fdc->params[1]; + } + fdc->step = 1; + } else { + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + } else { + fdc_log("Seeking to track %i (PCN = %i)...\n", fdc->params[1], fdc->pcn[fdc->params[0] & 3]); + if ((fdc->params[1] - fdc->pcn[fdc->params[0] & 3]) == 0) { + fdc_log("Failed seek\n"); + fdc->st0 = 0x20 | (fdc->params[0] & 3); + if (fdc->flags & FDC_FLAG_PCJR) { + fdc->fintr = 1; + fdc->interrupt = -4; + } else { + timer_disable(&fdc->timer); + fdc->interrupt = -3; + fdc_callback(fdc); + } + break; + } + if (fdc->params[1] > fdc->pcn[fdc->params[0] & 3]) + fdc->seek_dir = 0; + else + fdc->seek_dir = 1; + fdc_seek(fdc, fdc->drive, fdc->params[1] - fdc->pcn[fdc->params[0] & 3]); + fdc->pcn[fdc->params[0] & 3] = fdc->params[1]; + fdc->step = 1; + } + break; + case 0x12: /* Perpendicular mode */ + fdc->stat = 0x80; + if (fdc->params[0] & 0x80) + fdc->perp = fdc->params[0] & 0x3f; + else { + fdc->perp &= 0xfc; + fdc->perp |= (fdc->params[0] & 0x03); + } + return; + + default: + break; + } + } else + fdc->stat = 0x90 | (fdc->stat & 0xf); + } + return; + case 7: + if (!(fdc->flags & FDC_FLAG_TOSHIBA) && !(fdc->flags & FDC_FLAG_AT) && !(fdc->flags & FDC_FLAG_UMC)) + return; + fdc->rate = val & 0x03; + if (fdc->flags & FDC_FLAG_PS2) + fdc->noprec = !!(val & 0x04); + return; + + default: + break; + } } uint8_t @@ -1376,213 +1385,214 @@ fdc_read(uint16_t addr, void *priv) cycles -= ISA_CYCLES(8); - if (!fdc->power_down || ((addr & 7) == 2)) switch (addr & 7) { - case 0: /* STA */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - /* TODO: - Bit 2: INDEX (best return always 0 as it goes by very fast) - */ - if (fdc->seek_dir) /* nDIRECTION */ - ret |= 0x01; - if (writeprot[drive]) /* WRITEPROT */ - ret |= 0x02; - if (!fdd_get_head(drive)) /* nHDSEL */ - ret |= 0x08; - if (fdd_track0(drive)) /* TRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (dma_get_drq(fdc->dma_ch)) /* DRQ */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x04; - /* TODO: - Bit 2: nINDEX (best return always 1 as it goes by very fast) - */ - if (!fdc->seek_dir) /* DIRECTION */ - ret |= 0x01; - if (!writeprot[drive]) /* nWRITEPROT */ - ret |= 0x02; - if (fdd_get_head(drive)) /* HDSEL */ - ret |= 0x08; - if (!fdd_track0(drive)) /* nTRK0 */ - ret |= 0x10; - if (fdc->step) /* STEP */ - ret |= 0x20; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x40; - if (fdc->fintr || fdc->reset_stat) /* INTR */ - ret |= 0x80; - } else - ret = 0xff; - break; - case 1: /* STB */ - if (fdc->flags & FDC_FLAG_PS2) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x00; - if (!fdd_get_type(1)) /* -Drive 2 Installed */ - ret |= 0x80; - switch (drive) { /* -Drive Select 1,0 */ - case 0: - ret |= 0x43; - break; - case 1: - ret |= 0x23; - break; - case 2: - ret |= 0x62; - break; - case 3: - ret |= 0x61; - break; - - default: - break; - } - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - drive = real_drive(fdc, fdc->dor & 3); - ret = 0xc0; - ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ - } else { - if (is486 || !fdc->enable_3f1) + if (!fdc->power_down || ((addr & 7) == 2)) + switch (addr & 7) { + case 0: /* STA */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + /* TODO: + Bit 2: INDEX (best return always 0 as it goes by very fast) + */ + if (fdc->seek_dir) /* nDIRECTION */ + ret |= 0x01; + if (writeprot[drive]) /* WRITEPROT */ + ret |= 0x02; + if (!fdd_get_head(drive)) /* nHDSEL */ + ret |= 0x08; + if (fdd_track0(drive)) /* TRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (dma_get_drq(fdc->dma_ch)) /* DRQ */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x04; + /* TODO: + Bit 2: nINDEX (best return always 1 as it goes by very fast) + */ + if (!fdc->seek_dir) /* DIRECTION */ + ret |= 0x01; + if (!writeprot[drive]) /* nWRITEPROT */ + ret |= 0x02; + if (fdd_get_head(drive)) /* HDSEL */ + ret |= 0x08; + if (!fdd_track0(drive)) /* nTRK0 */ + ret |= 0x10; + if (fdc->step) /* STEP */ + ret |= 0x20; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x40; + if (fdc->fintr || fdc->reset_stat) /* INTR */ + ret |= 0x80; + } else ret = 0xff; - else { - if (fdc->flags & FDC_FLAG_UMC) { - drive = real_drive(fdc, fdc->dor & 1); - ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; - } else { - /* TODO: What is this and what is it used for? - It's almost identical to the PS/2 MCA mode. */ - drive = real_drive(fdc, fdc->dor & 3); - ret = 0x70; - ret &= ~(drive ? 0x40 : 0x20); - ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + break; + case 1: /* STB */ + if (fdc->flags & FDC_FLAG_PS2) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x00; + if (!fdd_get_type(1)) /* -Drive 2 Installed */ + ret |= 0x80; + switch (drive) { /* -Drive Select 1,0 */ + case 0: + ret |= 0x43; + break; + case 1: + ret |= 0x23; + break; + case 2: + ret |= 0x62; + break; + case 3: + ret |= 0x61; + break; + + default: + break; + } + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + drive = real_drive(fdc, fdc->dor & 3); + ret = 0xc0; + ret |= (fdc->dor & 0x01) << 5; /* Drive Select 0 */ + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } else { + if (is486 || !fdc->enable_3f1) + ret = 0xff; + else { + if (fdc->flags & FDC_FLAG_UMC) { + drive = real_drive(fdc, fdc->dor & 1); + ret = !fdd_is_dd(drive) ? ((fdc->dor & 1) ? 2 : 1) : 0; + } else { + /* TODO: What is this and what is it used for? + It's almost identical to the PS/2 MCA mode. */ + drive = real_drive(fdc, fdc->dor & 3); + ret = 0x70; + ret &= ~(drive ? 0x40 : 0x20); + ret |= (fdc->dor & 0x30) >> 4; /* Motor Select 1, 0 */ + } } } - } - break; - case 2: - ret = fdc->dor; - break; - case 3: - drive = real_drive(fdc, fdc->dor & 3); - /* TODO: FDC_FLAG_PS2_TDR? */ - if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { - /* PS/1 Model 2121 seems return drive type in port - * 0x3f3, despite the 82077AA fdc_t not implementing - * this. This is presumably implemented outside the - * fdc_t on one of the motherboard's support chips. - * - * Confirmed: 00=1.44M 3.5 - * 10=2.88M 3.5 - * 20=1.2M 5.25 - * 30=1.2M 5.25 - * - * as reported by Configur.exe. - */ - if (fdd_is_525(drive)) - ret = 0x20; - else if (fdd_is_ed(drive)) - ret = 0x10; - else - ret = 0x00; - /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ - } else if (!fdc->enh_mode) - ret = 0x20; - else if (fdc->flags & FDC_FLAG_SMC661) - ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); - else - ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); - break; - case 4: /*Status*/ - ret = fdc->stat; - break; - case 5: /*Data*/ - if (fdc->fifointest) { - /* Read FIFO buffer in the test mode (PS/55) */ - ret = fifo_read(fdc->fifo_p); break; - } - if ((fdc->stat & 0xf0) == 0xf0) { - fdc->stat &= ~0x80; - if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { - fdc->data_ready = 0; - ret = fdc->dat; - } else - ret = fifo_read(fdc->fifo_p); + case 2: + ret = fdc->dor; break; - } - if (fdc->paramstogo) { - fdc->stat &= ~0x80; - fdc_log("%i parameters to go\n", fdc->paramstogo); - fdc->paramstogo--; - ret = fdc->res[10 - fdc->paramstogo]; - if (!fdc->paramstogo) - fdc->stat = 0x80; - else - fdc->stat |= 0xC0; - } else if (fdc->dma) { - ret = fdc->dat; - break; - } else { - fdc->stat &= ~0x80; - if (lastbyte) - fdc->stat = 0x80; - lastbyte = 0; - ret = fdc->dat; - fdc->data_ready = 0; - } - fdc->stat &= 0xf0; - break; - case 7: /*Disk change*/ - drive = real_drive(fdc, fdc->dor & 3); - - if (fdc->flags & FDC_FLAG_PS2) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; - ret |= (fdc->dor & 0x08); - ret |= (fdc->noprec << 2); - ret |= (fdc->rate & 0x03); - } else - ret = 0x00; - } else if (fdc->flags & FDC_FLAG_PS2_MCA) { - if (fdc->dor & (0x10 << drive)) { - ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - ret |= ((fdc->rate & 0x03) << 1); - ret |= fdc_get_densel(fdc, drive); - ret |= 0x78; - } else - ret = 0xf9; - } else { - if (fdc->dor & (0x10 << drive)) { - if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) - ret = 0x00; + case 3: + drive = real_drive(fdc, fdc->dor & 3); + /* TODO: FDC_FLAG_PS2_TDR? */ + if ((fdc->flags & FDC_FLAG_PS2) || (fdc->flags & FDC_FLAG_PS2_MCA)) { + /* PS/1 Model 2121 seems return drive type in port + * 0x3f3, despite the 82077AA fdc_t not implementing + * this. This is presumably implemented outside the + * fdc_t on one of the motherboard's support chips. + * + * Confirmed: 00=1.44M 3.5 + * 10=2.88M 3.5 + * 20=1.2M 5.25 + * 30=1.2M 5.25 + * + * as reported by Configur.exe. + */ + if (fdd_is_525(drive)) + ret = 0x20; + else if (fdd_is_ed(drive)) + ret = 0x10; else + ret = 0x00; + /* PS/55 POST throws an error and halt if ret = 1 or 2, somehow. */ + } else if (!fdc->enh_mode) + ret = 0x20; + else if (fdc->flags & FDC_FLAG_SMC661) + ret = (fdc->densel_force << 3) | ((!!fdc->swap) << 5) | (fdc->media_id << 6); + else + ret = (fdc->rwc[drive] << 4) | (fdc->media_id << 6); + break; + case 4: /*Status*/ + ret = fdc->stat; + break; + case 5: /*Data*/ + if (fdc->fifointest) { + /* Read FIFO buffer in the test mode (PS/55) */ + ret = fifo_read(fdc->fifo_p); + break; + } + if ((fdc->stat & 0xf0) == 0xf0) { + fdc->stat &= ~0x80; + if ((fdc->flags & FDC_FLAG_PCJR) || !fdc->fifo) { + fdc->data_ready = 0; + ret = fdc->dat; + } else + ret = fifo_read(fdc->fifo_p); + break; + } + if (fdc->paramstogo) { + fdc->stat &= ~0x80; + fdc_log("%i parameters to go\n", fdc->paramstogo); + fdc->paramstogo--; + ret = fdc->res[10 - fdc->paramstogo]; + if (!fdc->paramstogo) + fdc->stat = 0x80; + else + fdc->stat |= 0xC0; + } else if (fdc->dma) { + ret = fdc->dat; + break; + } else { + fdc->stat &= ~0x80; + if (lastbyte) + fdc->stat = 0x80; + lastbyte = 0; + ret = fdc->dat; + fdc->data_ready = 0; + } + fdc->stat &= 0xf0; + break; + case 7: /*Disk change*/ + drive = real_drive(fdc, fdc->dor & 3); + + if (fdc->flags & FDC_FLAG_PS2) { + if (fdc->dor & (0x10 << drive)) { + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x00 : 0x80; + ret |= (fdc->dor & 0x08); + ret |= (fdc->noprec << 2); + ret |= (fdc->rate & 0x03); + } else + ret = 0x00; + } else if (fdc->flags & FDC_FLAG_PS2_MCA) { + if (fdc->dor & (0x10 << drive)) { ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; - } else - ret = 0x00; - if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ - ret ^= 0x80; + ret |= ((fdc->rate & 0x03) << 1); + ret |= fdc_get_densel(fdc, drive); + ret |= 0x78; + } else + ret = 0xf9; + } else { + if (fdc->dor & (0x10 << drive)) { + if ((drive == 1) && (fdc->flags & FDC_FLAG_TOSHIBA)) + ret = 0x00; + else + ret = (fdd_changed[drive] || drive_empty[drive]) ? 0x80 : 0x00; + } else + ret = 0x00; + if (fdc->flags & FDC_FLAG_DISKCHG_ACTLOW) /*PC2086/3086 seem to reverse this bit*/ + ret ^= 0x80; - /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ - if (fdc->flags & FDC_FLAG_TOSHIBA) { - ret |= (3 << 5); - ret |= 0x01; - } else - ret |= 0x7F; - } + /* 0 = ????, 1 = Ext. FDD off, 2 = Ext. FDD = FDD A, 3 = Ext. FDD = FDD B */ + if (fdc->flags & FDC_FLAG_TOSHIBA) { + ret |= (3 << 5); + ret |= 0x01; + } else + ret |= 0x7F; + } - fdc->step = 0; - break; - default: - ret = 0xff; - } + fdc->step = 0; + break; + default: + ret = 0xff; + } fdc_log("[%04X:%08X] Read FDC %04X %02X [%i:%02X]\n", CS, cpu_state.pc, addr, ret, drive, fdc->dor & (0x10 << drive)); return ret; } @@ -1886,7 +1896,7 @@ fdc_callback(void *priv) } else fdc->interrupt = -3; timer_set_delay_u64(&fdc->timer, 2048 * TIMER_USEC); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); return; case 0x0d: /*Format track*/ if (fdc->format_state == 1) { @@ -1929,7 +1939,7 @@ fdc_callback(void *priv) return; case 0x0f: /*Seek*/ fdc->st0 = 0x20 | (fdc->params[0] & 3); - fdc->stat = 0x80 | (1 << fdc->rw_drive); + fdc->stat = 0x10 | (1 << fdc->rw_drive); // Interrupts and callbacks in the fdd callback function return; case 0x10: /*Version*/ @@ -2863,4 +2873,4 @@ const device_t fdc_ps2_mca_device = { .speed_changed = NULL, .force_redraw = NULL, .config = NULL -}; +}; \ No newline at end of file diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index a0bef7ac6..212317f49 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -79,11 +79,34 @@ typedef struct fdd_t { fdd_t fdd[FDD_NUM]; +enum { + FDD_OP_NONE = 0, + FDD_OP_READ, + FDD_OP_WRITE, + FDD_OP_COMPARE, + FDD_OP_READADDR, + FDD_OP_FORMAT +}; + +typedef struct fdd_pending_op_t { + int pending; + int op; + int sector; + int track; + int side; + int density; + int sector_size; + uint8_t fill; +} fdd_pending_op_t; + +static fdd_pending_op_t fdd_pending[FDD_NUM]; + char floppyfns[FDD_NUM][512]; char *fdd_image_history[FDD_NUM][FLOPPY_IMAGE_HISTORY]; pc_timer_t fdd_poll_time[FDD_NUM]; pc_timer_t fdd_seek_timer[FDD_NUM]; +int fdd_seek_in_progress[FDD_NUM] = { 0, 0, 0, 0 }; static int fdd_notfound = 0; static int driveloaders[FDD_NUM]; @@ -293,10 +316,49 @@ fdd_seek_complete_callback(void *priv) { DRIVE *drive = (DRIVE *) priv; + fdd_seek_in_progress[drive->id] = 0; + fdd_log("fdd_seek_complete_callback(drive=%d) - TIMER FIRED! seek_in_progress=1\n", drive->id); fdd_log("Notifying FDC of seek completion\n"); fdd_do_seek(drive->id, fdd[drive->id].track); - fdc_seek_complete_interrupt(fdd_fdc, drive->id); + + int had_pending = fdd_pending[drive->id].pending; + if (had_pending) { + fdd_pending_op_t *po = &fdd_pending[drive->id]; + fdd_log("Starting deferred op %d after seek on drive %d (trk=%d, side=%d, sec=%d)\n", + po->op, drive->id, po->track, po->side, po->sector); + + switch (po->op) { + case FDD_OP_READ: + if (drives[drive->id].readsector) + drives[drive->id].readsector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_WRITE: + if (drives[drive->id].writesector) + drives[drive->id].writesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_COMPARE: + if (drives[drive->id].comparesector) + drives[drive->id].comparesector(drive->id, po->sector, po->track, po->side, po->density, po->sector_size); + break; + case FDD_OP_READADDR: + if (drives[drive->id].readaddress) + drives[drive->id].readaddress(drive->id, po->side, po->density); + break; + case FDD_OP_FORMAT: + if (drives[drive->id].format) + drives[drive->id].format(drive->id, po->side, po->density, po->fill); + break; + default: + break; + } + + po->pending = 0; + po->op = FDD_OP_NONE; + } + + if (!had_pending) + fdc_seek_complete_interrupt(fdd_fdc, drive->id); } void @@ -306,6 +368,11 @@ fdd_seek(int drive, int track_diff) if (!track_diff) return; + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek already in progress for drive %d, ignoring new seek request\n", drive); + return; + } + int old_track = fdd[drive].track; fdd[drive].track += track_diff; @@ -327,12 +394,14 @@ fdd_seek(int drive, int track_diff) /* Multi-track seek */ fdd_audio_play_multi_track_seek(drive, old_track, fdd[drive].track); } - + if (old_track + track_diff < 0) { fdd_do_seek(drive, fdd[drive].track); return; } + fdd_seek_in_progress[drive] = 1; + if (!fdd_seek_timer[drive].callback) { timer_add(&(fdd_seek_timer[drive]), fdd_seek_complete_callback, &drives[drive], 0); } @@ -629,7 +698,7 @@ fdd_set_motor_enable(int drive, int motor_enable) { fdd_log("fdd_set_motor_enable(%d, %d)\n", drive, motor_enable); fdd_audio_set_motor_enable(drive, motor_enable); - + if (motor_enable && !motoron[drive]) { timer_set_delay_u64(&fdd_poll_time[drive], fdd_byteperiod(drive)); } else if (!motor_enable && motoron[drive]) { @@ -700,6 +769,22 @@ void fdd_readsector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_readsector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring READ (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READ, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].readsector) drives[drive].readsector(drive, sector, track, side, density, sector_size); else @@ -710,6 +795,22 @@ void fdd_writesector(int drive, int sector, int track, int side, int density, int sector_size) { fdd_log("fdd_writesector(%d, %d, %d, %d, %d, %d)\n", drive, sector, track, side, density, sector_size); + + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring WRITE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_WRITE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].writesector) drives[drive].writesector(drive, sector, track, side, density, sector_size); else @@ -719,6 +820,21 @@ fdd_writesector(int drive, int sector, int track, int side, int density, int sec void fdd_comparesector(int drive, int sector, int track, int side, int density, int sector_size) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring COMPARE (trk=%d->%d, side=%d, sec=%d)\n", + drive, fdd[drive].track, track, side, sector); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_COMPARE, + .sector = sector, + .track = track, + .side = side, + .density = density, + .sector_size = sector_size + }; + return; + } + if (drives[drive].comparesector) drives[drive].comparesector(drive, sector, track, side, density, sector_size); else @@ -728,6 +844,19 @@ fdd_comparesector(int drive, int sector, int track, int side, int density, int s void fdd_readaddress(int drive, int side, int density) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring READADDRESS (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_READADDR, + .track = fdd[drive].track, + .side = side, + .density = density + }; + return; + } + if (drives[drive].readaddress) drives[drive].readaddress(drive, side, density); } @@ -735,6 +864,20 @@ fdd_readaddress(int drive, int side, int density) void fdd_format(int drive, int side, int density, uint8_t fill) { + if (fdd_seek_in_progress[drive]) { + fdd_log("Seek in progress on drive %d, deferring FORMAT (trk=%d, side=%d)\n", + drive, fdd[drive].track, side); + fdd_pending[drive] = (fdd_pending_op_t) { + .pending = 1, + .op = FDD_OP_FORMAT, + .track = fdd[drive].track, + .side = side, + .density = density, + .fill = fill + }; + return; + } + if (drives[drive].format) drives[drive].format(drive, side, density, fill); else @@ -777,7 +920,7 @@ fdd_init(void) if (fdd_sounds_enabled) { fdd_audio_init(); - } + } } void diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 409ddfe8c..04ae9ba68 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -176,6 +176,7 @@ static int16_t * load_wav(const char *filename, int *sample_count) { FILE *f = NULL; + char full_path[2048]; if ((filename == NULL) || (strlen(filename) == 0)) return NULL; From fe0277c03135168a0f97582ea6537e4df0c75987 Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 13:28:23 +0200 Subject: [PATCH 265/274] FDC: Fix a warning and the incorrect status flags. --- src/floppy/fdc.c | 6 +++--- src/floppy/fdd_audio.c | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 212bf9ce0..44adc569b 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -184,7 +184,7 @@ fdc_ctrl_reset(void *priv) { fdc_t *fdc = (fdc_t *) priv; - fdc->stat = 0x10; + fdc->stat = 0x80; fdc->pnum = fdc->ptot = 0; fdc->st0 = 0; fdc->head = 0; @@ -1211,7 +1211,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) case 0x07: /* Recalibrate */ fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = 0x10 | (1 << real_drive(fdc, fdc->drive)); + fdc->stat = (1 << real_drive(fdc, fdc->drive)); if (!(fdc->flags & FDC_FLAG_PCJR)) fdc->stat |= 0x80; fdc->st0 = fdc->params[0] & 3; @@ -1266,7 +1266,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) break; case 0x0f: /* Seek */ fdc->rw_drive = fdc->params[0] & 3; - fdc->stat = 0x10 | (1 << fdc->drive); + fdc->stat = (1 << fdc->drive); if (!(fdc->flags & FDC_FLAG_PCJR)) fdc->stat |= 0x80; fdc->head = 0; /* TODO: See if this is correct. */ diff --git a/src/floppy/fdd_audio.c b/src/floppy/fdd_audio.c index 04ae9ba68..409ddfe8c 100644 --- a/src/floppy/fdd_audio.c +++ b/src/floppy/fdd_audio.c @@ -176,7 +176,6 @@ static int16_t * load_wav(const char *filename, int *sample_count) { FILE *f = NULL; - char full_path[2048]; if ((filename == NULL) || (strlen(filename) == 0)) return NULL; From bc085ac02f5481cd0d4df344340fadf2384d9636 Mon Sep 17 00:00:00 2001 From: andresdelcampo <33843515+andresdelcampo@users.noreply.github.com> Date: Sat, 27 Sep 2025 13:31:46 +0200 Subject: [PATCH 266/274] Change window resizing logic when using 4:3 aspect ratio (#6233) Change window resizing logic when using 4:3 aspect ratio to resize content only. Fixed issues in Remember size and position that are derived from the change. There is a slight flicker while resizing with force 4:3 aspect ratio. --- src/qt/qt_mainwindow.cpp | 154 +++++++++++++++++---------------------- src/qt/qt_mainwindow.hpp | 4 +- 2 files changed, 70 insertions(+), 88 deletions(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 954547078..fbc2d8a47 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -84,6 +84,7 @@ extern bool cpu_thread_running; #include #include #include +#include #include #include #include @@ -171,8 +172,6 @@ extern "C" void qt_blit(int x, int y, int w, int h, int monitor_index); extern MainWindow *main_window; -bool MainWindow::s_adjustingForce43 = false; - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) @@ -933,11 +932,15 @@ MainWindow::closeEvent(QCloseEvent *event) } } if (window_remember) { - window_w = ui->stackedWidget->width(); - window_h = ui->stackedWidget->height(); + // If maximized, persist the normal (restorable) geometry + const bool wasMax = isMaximized(); + QRect normal = wasMax ? this->normalGeometry() : this->geometry(); + // Save WINDOW size (not the content widget’s 4:3 box) + window_w = normal.width(); + window_h = normal.height(); if (!QApplication::platformName().contains("wayland")) { - window_x = this->geometry().x(); - window_y = this->geometry().y(); + window_x = normal.x(); + window_y = normal.y(); } for (int i = 1; i < MONITORS_NUM; i++) { if (renderers[i]) { @@ -1021,65 +1024,56 @@ void MainWindow::updateShortcuts() seq = QKeySequence::fromString(acc_keys[accID].seq); ui->actionMute_Unmute->setShortcut(seq); } - -void -MainWindow::adjustForForce43(const QSize &newWinSize) + +void +MainWindow::applyContentLayoutForCurrentState() { - // Only act in resizable mode with Force 4:3 enabled and not fullscreen - if (!(vid_resize == 1 && force_43 > 0) || video_fullscreen || s_adjustingForce43) - return; + auto applyFill = [this](const QRect& r){ + ui->stackedWidget->setGeometry(r); + ui->stackedWidget->onResize(r.width(), r.height()); + if (monitors[0].mon_scrnsz_x != r.width() || monitors[0].mon_scrnsz_y != r.height()) { + monitors[0].mon_scrnsz_x = r.width(); + monitors[0].mon_scrnsz_y = r.height(); + plat_resize_request(r.width(), r.height(), 0); + } + }; - s_adjustingForce43 = true; + auto apply43 = [this](const QRect& area){ + int areaW = area.width(); + int areaH = area.height(); + if (areaW <= 0 || areaH <= 0) return; - // Height consumed by menu/status/toolbars - int chromeH = menuBar()->height() - + (hide_status_bar ? 0 : statusBar()->height()) - + (hide_tool_bar ? 0 : ui->toolBar->height()); + int targetW = areaW; + int targetH = (areaW * 3) / 4; + if (targetH > areaH) { + targetH = areaH; + targetW = (areaH * 4) / 3; + } - // Compute client area size in device‑independent pixels - double dpr = (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.0); - int winW = newWinSize.width(); - int winH = newWinSize.height(); - int clientW = static_cast(winW / dpr); - int clientH = static_cast((winH - chromeH) / dpr); + const int x = area.x() + (areaW - targetW) / 2; + const int y = area.y() + (areaH - targetH) / 2; - if (clientW <= 0 || clientH <= 0) { - s_adjustingForce43 = false; - return; - } + ui->stackedWidget->setGeometry(x, y, targetW, targetH); + ui->stackedWidget->onResize(targetW, targetH); - // Decide which dimension the user changed most – adjust the other - int curW = static_cast(width() / dpr); - int curH = static_cast((height() - chromeH) / dpr); - bool widthChanged = std::abs(clientW - curW) >= std::abs(clientH - curH); + if (monitors[0].mon_scrnsz_x != targetW || monitors[0].mon_scrnsz_y != targetH) { + monitors[0].mon_scrnsz_x = targetW; + monitors[0].mon_scrnsz_y = targetH; + plat_resize_request(targetW, targetH, 0); + } + }; - int targetW, targetH; - if (widthChanged) { - // user dragged width – compute matching height for 4:3 - targetW = clientW; - targetH = (clientW * 3) / 4; - } else { - // user dragged height – compute matching width for 4:3 - targetH = clientH; - targetW = (clientH * 4) / 3; - } + QWidget *cw = this->centralWidget(); + if (!cw) return; - // Convert back to window size including chrome and apply - int newW = static_cast(targetW * dpr); - int newH = static_cast(targetH * dpr) + chromeH; - if (newW != winW || newH != winH) - resize(newW, newH); + const QRect area = cw->contentsRect(); - // Update emulator framebuffer size and notify platform - monitors[0].mon_scrnsz_x = targetW; - monitors[0].mon_scrnsz_y = targetH; - plat_resize_request(targetW, targetH, 0); + // Fullscreen always fills (legacy behavior) + if (video_fullscreen) { applyFill(area); return; } - // Allow renderer widget to grow and recompute scaling - ui->stackedWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - ui->stackedWidget->onResize(width(), height()); - - s_adjustingForce43 = false; + // Windowed: enforce 4:3 only when requested, otherwise fill + if (force_43 > 0) apply43(area); + else applyFill(area); } void @@ -1088,26 +1082,10 @@ MainWindow::resizeEvent(QResizeEvent *event) //qDebug() << pos().x() + event->size().width(); //qDebug() << pos().y() + event->size().height(); - // Enforce 4:3 aspect ratio in resizable mode when the option is set - adjustForForce43(event->size()); - - if (vid_resize == 1 || video_fullscreen) - return; + // Always let QMainWindow do its layout first + QMainWindow::resizeEvent(event); - int newX = pos().x(); - int newY = pos().y(); - - if (((frameGeometry().x() + event->size().width() + 1) > util::screenOfWidget(this)->availableGeometry().right())) { - //move(util::screenOfWidget(this)->availableGeometry().right() - size().width() - 1, pos().y()); - newX = util::screenOfWidget(this)->availableGeometry().right() - frameGeometry().width() - 1; - if (newX < 1) newX = 1; - } - - if (((frameGeometry().y() + event->size().height() + 1) > util::screenOfWidget(this)->availableGeometry().bottom())) { - newY = util::screenOfWidget(this)->availableGeometry().bottom() - frameGeometry().height() - 1; - if (newY < 1) newY = 1; - } - move(newX, newY); + applyContentLayoutForCurrentState(); } void @@ -1201,12 +1179,25 @@ MainWindow::showEvent(QShowEvent *event) monitors[0].mon_scrnsz_y = fixed_size_y; } if (window_remember && vid_resize == 1) { - ui->stackedWidget->setFixedSize(window_w, window_h); + const QSize target(window_w, window_h); + const QSize prevMin = ui->stackedWidget->minimumSize(); + const QSize prevMax = ui->stackedWidget->maximumSize(); + + ui->stackedWidget->setMinimumSize(target); + ui->stackedWidget->setMaximumSize(target); #ifndef Q_OS_MACOS QApplication::processEvents(); #endif this->adjustSize(); + + ui->stackedWidget->setMinimumSize(prevMin); + ui->stackedWidget->setMaximumSize(prevMax); + ui->stackedWidget->resize(target); } + + QTimer::singleShot(0, this, [this]{ + applyContentLayoutForCurrentState(); + }); } void @@ -1509,6 +1500,7 @@ MainWindow::on_actionFullscreen_triggered() { if (video_fullscreen > 0) { showNormal(); + QTimer::singleShot(0, this, [this]{ applyContentLayoutForCurrentState(); }); ui->menubar->show(); if (!hide_status_bar) ui->statusbar->show(); @@ -2146,16 +2138,7 @@ MainWindow::on_actionForce_4_3_display_ratio_triggered() { video_toggle_option(ui->actionForce_4_3_display_ratio, &force_43); - // When turning on Force 4:3 in resizable mode, immediately snap to 4:3 - if (vid_resize == 1 && !video_fullscreen) { - ui->stackedWidget->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); - if (force_43 > 0) { - adjustForForce43(size()); - } else { - // Turning off: refresh renderer scaling - ui->stackedWidget->onResize(width(), height()); - } - } + QTimer::singleShot(0, this, [this]{ applyContentLayoutForCurrentState(); }); } void @@ -2524,4 +2507,3 @@ void MainWindow::on_actionCGA_composite_settings_triggered() isNonPause = false; config_save(); } - diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index f562c2ea9..77e8fe7c4 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -179,8 +179,8 @@ private: std::shared_ptr mm; static bool s_adjustingForce43; // guard against recursion - void adjustForForce43(const QSize &newWinSize); - + void applyContentLayoutForCurrentState(); + void updateShortcuts(); void processKeyboardInput(bool down, uint32_t keycode); #ifdef Q_OS_MACOS From 3fd58dde8f5d619c24001c7c1bf4f3cd516326d7 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 27 Sep 2025 17:32:00 +0600 Subject: [PATCH 267/274] Implement YUV aperture on Mach64 VT2 (#6234) Some GDBSTUB fixes --- src/gdbstub.c | 4 + src/video/vid_ati_mach64.c | 336 ++++++++++++++++++++++++++----------- 2 files changed, 243 insertions(+), 97 deletions(-) diff --git a/src/gdbstub.c b/src/gdbstub.c index cbbf0ede7..6e1e9af26 100644 --- a/src/gdbstub.c +++ b/src/gdbstub.c @@ -879,6 +879,7 @@ e22: /* Read by qwords, then by dwords, then by words, then by bytes. */ i = 0; + cpl_override = 1; if (is386) { for (; i < (k & ~7); i += 8) { *((uint64_t *) buf) = readmemql(j); @@ -900,6 +901,7 @@ e22: buf[0] = readmembl(j++); gdbstub_client_respond_hex(client, buf, 1); } + cpl_override = 0; break; case 'M': /* write memory */ @@ -934,6 +936,7 @@ e22: /* Write by qwords, then by dwords, then by words, then by bytes. */ p = client->packet; i = 0; + cpl_override = 1; if (is386) { for (; i < (k & ~7); i += 8) { writememql(j, *((uint64_t *) p)); @@ -955,6 +958,7 @@ e22: writemembl(j++, p[0]); p++; } + cpl_override = 0; /* Respond positively. */ goto ok; diff --git a/src/video/vid_ati_mach64.c b/src/video/vid_ati_mach64.c index 6a9b9ea34..221af0d3f 100644 --- a/src/video/vid_ati_mach64.c +++ b/src/video/vid_ati_mach64.c @@ -39,6 +39,7 @@ #include <86box/vid_svga.h> #include <86box/vid_svga_render.h> #include <86box/vid_ati_eeprom.h> +#include <86box/bswap.h> #ifdef CLAMP # undef CLAMP @@ -80,6 +81,7 @@ enum { typedef struct mach64_t { mem_mapping_t linear_mapping; mem_mapping_t mmio_mapping; + mem_mapping_t linear_mapping_big_endian; mem_mapping_t mmio_linear_mapping; mem_mapping_t mmio_linear_mapping_2; @@ -98,6 +100,8 @@ typedef struct mach64_t { uint8_t pci_slot; uint8_t irq_state; + uint8_t on_board; + uint8_t pci_regs[256]; uint8_t int_line; @@ -272,7 +276,7 @@ typedef struct mach64_t { uint32_t cur_clr0; uint32_t cur_clr1; - uint32_t overlay_dat[1024]; + uint32_t overlay_dat[2048]; uint32_t overlay_graphics_key_clr; uint32_t overlay_graphics_key_msk; uint32_t overlay_video_key_clr; @@ -286,12 +290,17 @@ typedef struct mach64_t { uint32_t scaler_height_width; int scaler_format; int scaler_update; + int scaler_yuv_aper; uint32_t buf_offset[2]; uint32_t buf_pitch[2]; int overlay_v_acc; + uint32_t overlay_uv_addr; + uint32_t overlay_cur_y; + uint32_t overlay_base; + uint8_t thread_run; void *i2c; void *ddc; @@ -381,6 +390,9 @@ void mach64_ext_writeb(uint32_t addr, uint8_t val, void *priv); void mach64_ext_writew(uint32_t addr, uint16_t val, void *priv); void mach64_ext_writel(uint32_t addr, uint32_t val, void *priv); +uint8_t mach64_readb_be(uint32_t addr, void *priv); +void mach64_writeb_be(uint32_t addr, uint8_t val, void *priv); + #ifdef ENABLE_MACH64_LOG int mach64_do_log = ENABLE_MACH64_LOG; @@ -591,6 +603,7 @@ mach64_updatemapping(mach64_t *mach64) mach64_log("Update mapping - PCI disabled\n"); mem_mapping_disable(&svga->mapping); mem_mapping_disable(&mach64->linear_mapping); + mem_mapping_disable(&mach64->linear_mapping_big_endian); mem_mapping_disable(&mach64->mmio_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping_2); @@ -645,14 +658,16 @@ mach64_updatemapping(mach64_t *mach64) } } else { /*2*8 MB aperture*/ - mem_mapping_set_addr(&mach64->linear_mapping, mach64->linear_base, (8 << 20) - 0x4000); - mem_mapping_set_addr(&mach64->mmio_linear_mapping, mach64->linear_base + ((8 << 20) - 0x4000), 0x4000); - mem_mapping_set_addr(&mach64->mmio_linear_mapping_2, mach64->linear_base + ((16 << 20) - 0x4000), 0x4000); + mem_mapping_set_addr(&mach64->linear_mapping, mach64->linear_base, (8 << 20) - 4096); + mem_mapping_set_addr(&mach64->mmio_linear_mapping, mach64->linear_base + ((8 << 20) - 4096), 4096); + mem_mapping_set_addr(&mach64->linear_mapping_big_endian, mach64->linear_base + (8 << 20), (8 << 20) - 0x1000); + mem_mapping_set_addr(&mach64->mmio_linear_mapping_2, mach64->linear_base + ((16 << 20) - 0x1000), 0x1000); } } else { mem_mapping_disable(&mach64->linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping); mem_mapping_disable(&mach64->mmio_linear_mapping_2); + mem_mapping_disable(&mach64->linear_mapping_big_endian); } } @@ -2310,8 +2325,11 @@ mach64_vblank_start(svga_t *svga) svga->overlay.ena = (mach64->overlay_scale_cntl & OVERLAY_EN) && (overlay_cmp_mix != 1); - mach64->overlay_v_acc = 0; - mach64->scaler_update = 1; + mach64->overlay_v_acc = 0; + mach64->scaler_update = 1; + mach64->overlay_uv_addr = svga->overlay.addr; + mach64->overlay_cur_y = 0; + mach64->overlay_base = svga->overlay.addr; } uint8_t @@ -2389,6 +2407,10 @@ mach64_ext_readb(uint32_t addr, void *priv) case 0x4a: ret = mach64->scaler_format; break; + + case 0x4b: + ret = mach64->scaler_yuv_aper; + break; default: ret = 0xff; @@ -2558,7 +2580,7 @@ mach64_ext_readb(uint32_t addr, void *priv) case 0xc7: READ8(addr, mach64->dac_cntl); - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { ret &= 0xf9; if (i2c_gpio_get_scl(mach64->i2c)) ret |= 0x04; @@ -2957,7 +2979,8 @@ mach64_ext_readw(uint32_t addr, void *priv) if (!(addr & 0x400)) { mach64_log("mach64_ext_readw: addr=%04x\n", addr); - ret = 0xffff; + ret = mach64_ext_readb(addr, priv); + ret |= mach64_ext_readb(addr + 1, priv) << 8; } else switch (addr & 0x3ff) { case 0xb4: @@ -2986,7 +3009,8 @@ mach64_ext_readl(uint32_t addr, void *priv) if (!(addr & 0x400)) { mach64_log("mach64_ext_readl: addr=%04x\n", addr); - ret = 0xffffffff; + ret = mach64_ext_readw(addr, priv); + ret |= mach64_ext_readw(addr + 2, priv) << 16; } else switch (addr & 0x3ff) { case 0x18: @@ -3087,6 +3111,10 @@ mach64_ext_writeb(uint32_t addr, uint8_t val, void *priv) case 0x4a: mach64->scaler_format = val & 0xf; break; + + case 0x4b: + mach64->scaler_yuv_aper = val; + break; case 0x80: case 0x81: @@ -4106,76 +4134,113 @@ mach64_int_hwcursor_draw(svga_t *svga, int displine) } \ } while (0) -#define DECODE_VYUY422() \ - do { \ - for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x += 2) { \ - uint8_t y1, y2; \ - int8_t u, v; \ - int dR, dG, dB; \ - int r, g, b; \ - \ - y1 = src[0]; \ - u = src[1] - 0x80; \ - y2 = src[2]; \ - v = src[3] - 0x80; \ - src += 4; \ - \ - dR = (359 * v) >> 8; \ - dG = (88 * u + 183 * v) >> 8; \ - dB = (453 * u) >> 8; \ - \ - r = y1 + dR; \ - CLAMP(r); \ - g = y1 - dG; \ - CLAMP(g); \ - b = y1 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x] = (r << 16) | (g << 8) | b; \ - \ - r = y2 + dR; \ - CLAMP(r); \ - g = y2 - dG; \ - CLAMP(g); \ - b = y2 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x + 1] = (r << 16) | (g << 8) | b; \ - } \ +#define DECODE_VYUY422() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + y1 = src[0]; \ + u = src[1] - 0x80; \ + y2 = src[2]; \ + v = src[3] - 0x80; \ + src += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ } while (0) -#define DECODE_YVYU422() \ - do { \ - for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x += 2) { \ - uint8_t y1, y2; \ - int8_t u, v; \ - int dR, dG, dB; \ - int r, g, b; \ - \ - u = src[0] - 0x80; \ - y1 = src[1]; \ - v = src[2] - 0x80; \ - y2 = src[3]; \ - src += 4; \ - \ - dR = (359 * v) >> 8; \ - dG = (88 * u + 183 * v) >> 8; \ - dB = (453 * u) >> 8; \ - \ - r = y1 + dR; \ - CLAMP(r); \ - g = y1 - dG; \ - CLAMP(g); \ - b = y1 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x] = (r << 16) | (g << 8) | b; \ - \ - r = y2 + dR; \ - CLAMP(r); \ - g = y2 - dG; \ - CLAMP(g); \ - b = y2 + dB; \ - CLAMP(b); \ - mach64->overlay_dat[x + 1] = (r << 16) | (g << 8) | b; \ - } \ +#define DECODE_YVYU422() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + u = src[0] - 0x80; \ + y1 = src[1]; \ + v = src[2] - 0x80; \ + y2 = src[3]; \ + src += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ + } while (0) + +#define DECODE_YUV12_PACKED() \ + do { \ + for (x = 0; x < src_w; x += 1) { \ + uint8_t y1, y2; \ + int8_t u, v; \ + int dR, dG, dB; \ + int r, g, b; \ + \ + u = uvsrc[3] - 0x80; \ + y1 = src[0]; \ + v = uvsrc[2] - 0x80; \ + y2 = src[1]; \ + src += 4; \ + uvsrc += 4; \ + \ + dR = (359 * v) >> 8; \ + dG = (88 * u + 183 * v) >> 8; \ + dB = (453 * u) >> 8; \ + \ + r = y1 + dR; \ + CLAMP(r); \ + g = y1 - dG; \ + CLAMP(g); \ + b = y1 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[x * 2] = (r << 16) | (g << 8) | b; \ + \ + r = y2 + dR; \ + CLAMP(r); \ + g = y2 - dG; \ + CLAMP(g); \ + b = y2 + dB; \ + CLAMP(b); \ + mach64->overlay_dat[(x * 2) + 1] = (r << 16) | (g << 8) | b; \ + } \ } while (0) void @@ -4185,11 +4250,13 @@ mach64_overlay_draw(svga_t *svga, int displine) int x; int h_acc = 0; int h_max = (mach64->scaler_height_width >> 16) & 0x3ff; + int src_w = h_max; int h_inc = mach64->overlay_scale_inc >> 16; int v_max = mach64->scaler_height_width & 0x3ff; int v_inc = mach64->overlay_scale_inc & 0xffff; uint32_t *p; uint8_t *src = &svga->vram[svga->overlay.addr]; + uint8_t *uvsrc = src; int old_y = mach64->overlay_v_acc; int y_diff; int video_key_fn = mach64->overlay_key_cntl & 5; @@ -4198,6 +4265,11 @@ mach64_overlay_draw(svga_t *svga, int displine) p = &buffer32->line[displine][svga->x_add + mach64->svga.overlay_latch.x]; + if (mach64->overlay_cur_y >= 2) { + /* Avoid corrupt UV data on YUV12 packed modes */ + uvsrc = &svga->vram[mach64->overlay_base + svga->overlay.pitch * 2 * (!(mach64->overlay_cur_y & 1) ? (mach64->overlay_cur_y + 1) : mach64->overlay_cur_y)]; + } + if (mach64->scaler_update) { switch (mach64->scaler_format) { case 0x3: @@ -4209,6 +4281,9 @@ mach64_overlay_draw(svga_t *svga, int displine) case 0x6: DECODE_ARGB8888(); break; + case 0xa: + DECODE_YUV12_PACKED(); + break; case 0xb: DECODE_VYUY422(); break; @@ -4217,7 +4292,7 @@ mach64_overlay_draw(svga_t *svga, int displine) break; default: - mach64_log("Unknown Mach64 scaler format %x\n", mach64->scaler_format); + pclog("Unknown Mach64 scaler format %x\n", mach64->scaler_format); /*Fill buffer with something recognisably wrong*/ for (x = 0; x < mach64->svga.overlay_latch.cur_xsize; x++) mach64->overlay_dat[x] = 0xff00ff; @@ -4354,6 +4429,7 @@ mach64_overlay_draw(svga_t *svga, int displine) svga->overlay.addr += svga->overlay.pitch * 2 * y_diff; mach64->scaler_update = y_diff; + mach64->overlay_cur_y += y_diff; } static void @@ -4389,7 +4465,7 @@ mach64_io_remove(mach64_t *mach64) io_removehandler(0x01ce, 0x0002, mach64_in, NULL, NULL, mach64_out, NULL, NULL, mach64); if (mach64->block_decoded_io && mach64->block_decoded_io < 0x10000) - io_removehandler(mach64->block_decoded_io, 0x0400, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); + io_removehandler(mach64->block_decoded_io, 0x0100, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); } static void @@ -4429,7 +4505,7 @@ mach64_io_set(mach64_t *mach64) io_sethandler(0x01ce, 0x0002, mach64_in, NULL, NULL, mach64_out, NULL, NULL, mach64); if (mach64->use_block_decoded_io && mach64->block_decoded_io && mach64->block_decoded_io < 0x10000) - io_sethandler(mach64->block_decoded_io, 0x0400, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); + io_sethandler(mach64->block_decoded_io, 0x0100, mach64_block_inb, mach64_block_inw, mach64_block_inl, mach64_block_outb, mach64_block_outw, mach64_block_outl, mach64); } static uint8_t @@ -4478,7 +4554,6 @@ static void mach64_write_linear(uint32_t addr, uint8_t val, void *priv) { svga_t *svga = (svga_t *) priv; - cycles -= svga->monitor->mon_video_timing_write_b; addr &= svga->decode_mask; @@ -4507,10 +4582,38 @@ mach64_writew_linear(uint32_t addr, uint16_t val, void *priv) static void mach64_writel_linear(uint32_t addr, uint32_t val, void *priv) { - svga_t *svga = (svga_t *) priv; + svga_t *svga = (svga_t *) priv; + mach64_t *mach64 = (mach64_t *) svga->priv; cycles -= svga->monitor->mon_video_timing_write_l; + if (((mach64->scaler_yuv_aper >> 4) & 0xc) && !!(addr & 0x800000) == !(mach64->scaler_yuv_aper & 0x20)) { + uint32_t offset_from_base = addr & 0x7FFFFF; + if (addr & 0x800000) bswap32s(&val); + if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x4) { // Y plane + offset_from_base <<= 1; + svga->vram[offset_from_base & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 1) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 4) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 5) & svga->vram_mask] = ((val >> 24) & 0xFF); + } + else if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x8 || ((mach64->scaler_yuv_aper >> 4) & 0xc) == 0xc) { + offset_from_base <<= 2; + if (((mach64->scaler_yuv_aper >> 4) & 0xc) == 0x8) { // U plane + svga->vram[(offset_from_base + 3) & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 7) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 11) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 15) & svga->vram_mask] = ((val >> 24) & 0xFF); + } else { // V plane + svga->vram[(offset_from_base + 2) & svga->vram_mask] = (val & 0xFF); + svga->vram[(offset_from_base + 6) & svga->vram_mask] = ((val >> 8) & 0xFF); + svga->vram[(offset_from_base + 10) & svga->vram_mask] = ((val >> 16) & 0xFF); + svga->vram[(offset_from_base + 14) & svga->vram_mask] = ((val >> 24) & 0xFF); + } + } + return; + } + addr &= svga->decode_mask; if (addr >= svga->vram_max) return; @@ -4519,6 +4622,42 @@ mach64_writel_linear(uint32_t addr, uint32_t val, void *priv) *(uint32_t *) &svga->vram[addr] = val; } +uint8_t +mach64_readb_be(uint32_t addr, void *priv) +{ + return mach64_read_linear(addr, priv); +} + +uint16_t +mach64_readw_be(uint32_t addr, void *priv) +{ + return bswap16(mach64_readw_linear(addr, priv)); +} + +uint32_t +mach64_readl_be(uint32_t addr, void *priv) +{ + return bswap32(mach64_readl_linear(addr, priv)); +} + +void +mach64_writeb_be(uint32_t addr, uint8_t val, void *priv) +{ + return mach64_write_linear(addr, val, priv); +} + +void +mach64_writew_be(uint32_t addr, uint16_t val, void *priv) +{ + return mach64_writew_linear(addr, bswap16(val), priv); +} + +void +mach64_writel_be(uint32_t addr, uint32_t val, void *priv) +{ + return mach64_writel_linear(addr, bswap32(val), priv); +} + uint8_t mach64_pci_read(UNUSED(int func), int addr, void *priv) { @@ -4564,30 +4703,31 @@ mach64_pci_read(UNUSED(int func), int addr, void *priv) return mach64->linear_base >> 24; case 0x14: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return 0x01; /*Block decoded IO address*/ return 0x00; case 0x15: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 8; return 0x00; case 0x16: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 16; return 0x00; case 0x17: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) return mach64->block_decoded_io >> 24; return 0x00; + case 0x30: - return mach64->pci_regs[0x30] & 0x01; /*BIOS ROM address*/ + return (mach64->on_board) ? 0 : (mach64->pci_regs[0x30] & 0x01); /*BIOS ROM address*/ case 0x31: return 0x00; case 0x32: - return mach64->pci_regs[0x32]; + return (mach64->on_board) ? 0 : mach64->pci_regs[0x32]; case 0x33: - return mach64->pci_regs[0x33]; + return (mach64->on_board) ? 0 : mach64->pci_regs[0x33]; case 0x3c: return mach64->int_line; @@ -4619,7 +4759,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) break; case 0x12: - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) val = 0; mach64->linear_base = (mach64->linear_base & 0xff000000) | ((val & 0x80) << 16); mach64_updatemapping(mach64); @@ -4630,16 +4770,16 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) break; case 0x15: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); - mach64->block_decoded_io = (mach64->block_decoded_io & 0xffff0000) | ((val & 0xfc) << 8); + mach64->block_decoded_io = (mach64->block_decoded_io & 0xffff0000) | ((val & 0xff) << 8); if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_set(mach64); } break; case 0x16: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->block_decoded_io = (mach64->block_decoded_io & 0xff00fc00) | (val << 16); @@ -4648,7 +4788,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) } break; case 0x17: - if (mach64->type == MACH64_VT2) { + if (mach64->type >= MACH64_VT2) { if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->block_decoded_io = (mach64->block_decoded_io & 0x00fffc00) | (val << 24); @@ -4660,6 +4800,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) case 0x30: case 0x32: case 0x33: + if (mach64->on_board) return; mach64->pci_regs[addr] = val; if (mach64->pci_regs[0x30] & 0x01) { uint32_t biosaddr = (mach64->pci_regs[0x32] << 16) | (mach64->pci_regs[0x33] << 24); @@ -4679,7 +4820,7 @@ mach64_pci_write(UNUSED(int func), int addr, uint8_t val, void *priv) if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_remove(mach64); mach64->io_base = val & 0x03; - if (mach64->type == MACH64_VT2) + if (mach64->type >= MACH64_VT2) mach64->use_block_decoded_io = val & 0x04; if (mach64->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_IO) mach64_io_set(mach64); @@ -4700,7 +4841,7 @@ mach64_common_init(const device_t *info) svga = &mach64->svga; mach64->type = info->local & 0xff; - mach64->vram_size = device_get_config_int("memory"); + mach64->vram_size = (info->local & (1 << 20)) ? 4 : device_get_config_int("memory"); mach64->vram_mask = (mach64->vram_size << 20) - 1; if (mach64->type > MACH64_GX) @@ -4717,9 +4858,10 @@ mach64_common_init(const device_t *info) mach64_overlay_draw); mem_mapping_add(&mach64->linear_mapping, 0, 0, mach64_read_linear, mach64_readw_linear, mach64_readl_linear, mach64_write_linear, mach64_writew_linear, mach64_writel_linear, NULL, MEM_MAPPING_EXTERNAL, svga); + mem_mapping_add(&mach64->linear_mapping_big_endian, 0, 0, mach64_readb_be, mach64_readw_be, mach64_readl_be, mach64_writeb_be, mach64_writew_be, mach64_writel_be, NULL, MEM_MAPPING_EXTERNAL, svga); mem_mapping_add(&mach64->mmio_linear_mapping, 0, 0, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); mem_mapping_add(&mach64->mmio_linear_mapping_2, 0, 0, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); - mem_mapping_add(&mach64->mmio_mapping, 0xbc000, 0x04000, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); + mem_mapping_add(&mach64->mmio_mapping, 0xbf000, 0x1000, mach64_ext_readb, mach64_ext_readw, mach64_ext_readl, mach64_ext_writeb, mach64_ext_writew, mach64_ext_writel, NULL, MEM_MAPPING_EXTERNAL, mach64); mem_mapping_disable(&mach64->mmio_mapping); mach64_io_set(mach64); From 9ef1b194867a92ba681c4e482b08ad09c79702d7 Mon Sep 17 00:00:00 2001 From: Verloren50000 <110334428+Verloren50000@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:32:46 +0800 Subject: [PATCH 268/274] Add the BCM FR510 (Packard Bell/NEC OEM) ROM + update (#6231) * machine.h: FR510 added. * m_at_sockets7.c: BCM FR510 added, including the BIOS Selector. * machine_table.c: BCM FR510 is now added. * machine_table.c: fr510-generic * m_at_sockets7.c: AGP Bridge for BCM FR510 * machine_table.c: FR510 PCIOnly * m_at_socket7.c: Fixes at FR510 * m_at_sockets7.c: 510S228.BIN -> 510S128.BIN * machine_table.c: an update for FR510 --- src/include/86box/machine.h | 4 +++ src/machine/m_at_sockets7.c | 69 +++++++++++++++++++++++++++++++++++++ src/machine/machine_table.c | 48 ++++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index b88d8f492..4fd0e16e8 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1069,6 +1069,10 @@ extern int machine_at_m560_init(const machine_t *); /* m_at_sockets7.c */ /* ALi ALADDiN V */ extern int machine_at_p5a_init(const machine_t *); +#ifdef EMU_DEVICE_H +extern const device_t fr510_device; +#endif +extern int machine_at_fr510_init(const machine_t *); extern int machine_at_m579_init(const machine_t *); extern int machine_at_gwlucas_init(const machine_t *); extern int machine_at_5aa_init(const machine_t *); diff --git a/src/machine/m_at_sockets7.c b/src/machine/m_at_sockets7.c index a39b2cef4..a70bccc03 100644 --- a/src/machine/m_at_sockets7.c +++ b/src/machine/m_at_sockets7.c @@ -74,6 +74,75 @@ machine_at_p5a_init(const machine_t *model) return ret; } +static const device_config_t fr510_config[] = { + // clang-format off + { + .name = "bios", + .description = "BIOS Version", + .type = CONFIG_BIOS, + .default_string = "fr510", + .default_int = 0, + .file_filter = "", + .spinner = { 0 }, + .bios = { + { .name = "Award Modular BIOS v4.51PG - Revision 1.19 (01/15/1999)", .internal_name = "fr510_1999", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/FR510119.bin", "" } }, + { .name = "Award Modular BIOS v4.51PG - Revision SCC-128 (11/27/2002)", .internal_name = "fr510", .bios_type = BIOS_NORMAL, + .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/510S128.BIN", "" } }, + { .files_no = 0 } + }, + }, + { .name = "", .description = "", .type = CONFIG_END } + // clang-format on +}; + +const device_t fr510_device = { + .name = "BCM FR510 (Packard Bell/NEC OEM)", + .internal_name = "fr510_device", + .flags = 0, + .local = 0, + .init = NULL, + .close = NULL, + .reset = NULL, + .available = NULL, + .speed_changed = NULL, + .force_redraw = NULL, + .config = fr510_config +}; + +int +machine_at_fr510_init(const machine_t *model) +{ + int ret = 0; + const char* fn; + + /* No ROMs available */ + if (!device_available(model->device)) + return ret; + + device_context(model->device); + fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); + ret = bios_load_linear(fn, 0x000c0000, 262144, 0); + device_context_restore(); + + machine_at_common_init_ex(model, 2); + + pci_init(PCI_CONFIG_TYPE_1); + pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); + pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); + pci_register_slot(0x14, PCI_CARD_NORMAL, 1, 2, 3, 4); + pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1); + device_add(&ali1541_device); + device_add(&ali1543c_device); /* +0 */ + device_add(&sst_flash_39sf020_device); + spd_register(SPD_TYPE_SDRAM, 0x7, 512); + + if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)) + device_add(machine_get_snd_device(machine)); + + return ret; +} + int machine_at_m579_init(const machine_t *model) { diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 8c9055c9b..1ae51e5c7 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -12388,7 +12388,7 @@ const machine_t machines[] = { }, /* The BIOS sends KBC command B3 which indicates an AMI (or VIA VT82C42N) KBC. */ { - .name = "[i430FX] NEC PowerMate V", + .name = "[i430FX] NEC PowerMate Vxxx", .internal_name = "powermatev", .type = MACHINE_TYPE_SOCKET5, .chipset = MACHINE_CHIPSET_INTEL_430FX, @@ -15329,7 +15329,7 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, - /* This has the Phoenix MultiKey KBC firmware on the NSC Suepr I/O chip. */ + /* This has the Phoenix MultiKey KBC firmware on the NSC Super I/O chip. */ { .name = "[i430TX] Packard Bell PB790", .internal_name = "an430tx", @@ -16007,6 +16007,50 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, + /* OEM-only BCM FR510, has the ALi M1543C southbridge with on-chip KBC. */ + { + .name = "[ALi ALADDiN V] BCM FR510 (Packard Bell/NEC OEM)", + .internal_name = "fr510", + .type = MACHINE_TYPE_SOCKETS7, + .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, + .init = machine_at_fr510_init, + .p1_handler = machine_generic_p1_handler, + .gpio_handler = NULL, + .available_flag = MACHINE_AVAILABLE, + .gpio_acpi_handler = NULL, + .cpu = { + .package = CPU_PKG_SOCKET5_7, + .block = CPU_BLOCK_NONE, + .min_bus = 50000000, + .max_bus = 66666667, + .min_voltage = 2200, + .max_voltage = 3200, + .min_multi = 1.5, + .max_multi = 5.5 + }, + .bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB, /* Has internal video: ATI 3D Rage IIc AGP (Rage 2) */ + .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_SOUND | MACHINE_USB, + .ram = { + .min = 8192, + .max = 262144, + .step = 8192 + }, + .nvrmask = 255, + .jumpered_ecp_dma = 0, + .default_jumpered_ecp_dma = -1, + .kbc_device = NULL, + .kbc_params = 0x00000000, + .kbc_p1 = 0x00000cf0, + .gpio = 0xffffffff, + .gpio_acpi = 0xffffffff, + .device = &fr510_device, + .kbd_device = NULL, + .fdc_device = NULL, + .sio_device = NULL, + .vid_device = NULL, + .snd_device = &cs4235_onboard_device, + .net_device = NULL + }, /* M1534c kbc */ { .name = "[ALi ALADDiN V] Gateway Lucas", From f692c00c6d6ec4ae5c34c102be0b8c11875a325c Mon Sep 17 00:00:00 2001 From: OBattler Date: Sat, 27 Sep 2025 15:21:13 +0200 Subject: [PATCH 269/274] Remove the BCM FR510 because it turned out to be improperly implemented fixes #6236. --- src/include/86box/machine.h | 4 --- src/machine/m_at_sockets7.c | 69 ------------------------------------- src/machine/machine_table.c | 44 ----------------------- 3 files changed, 117 deletions(-) diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 4fd0e16e8..b88d8f492 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -1069,10 +1069,6 @@ extern int machine_at_m560_init(const machine_t *); /* m_at_sockets7.c */ /* ALi ALADDiN V */ extern int machine_at_p5a_init(const machine_t *); -#ifdef EMU_DEVICE_H -extern const device_t fr510_device; -#endif -extern int machine_at_fr510_init(const machine_t *); extern int machine_at_m579_init(const machine_t *); extern int machine_at_gwlucas_init(const machine_t *); extern int machine_at_5aa_init(const machine_t *); diff --git a/src/machine/m_at_sockets7.c b/src/machine/m_at_sockets7.c index a70bccc03..a39b2cef4 100644 --- a/src/machine/m_at_sockets7.c +++ b/src/machine/m_at_sockets7.c @@ -74,75 +74,6 @@ machine_at_p5a_init(const machine_t *model) return ret; } -static const device_config_t fr510_config[] = { - // clang-format off - { - .name = "bios", - .description = "BIOS Version", - .type = CONFIG_BIOS, - .default_string = "fr510", - .default_int = 0, - .file_filter = "", - .spinner = { 0 }, - .bios = { - { .name = "Award Modular BIOS v4.51PG - Revision 1.19 (01/15/1999)", .internal_name = "fr510_1999", .bios_type = BIOS_NORMAL, - .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/FR510119.bin", "" } }, - { .name = "Award Modular BIOS v4.51PG - Revision SCC-128 (11/27/2002)", .internal_name = "fr510", .bios_type = BIOS_NORMAL, - .files_no = 1, .local = 0, .size = 262144, .files = { "roms/machines/fr510/510S128.BIN", "" } }, - { .files_no = 0 } - }, - }, - { .name = "", .description = "", .type = CONFIG_END } - // clang-format on -}; - -const device_t fr510_device = { - .name = "BCM FR510 (Packard Bell/NEC OEM)", - .internal_name = "fr510_device", - .flags = 0, - .local = 0, - .init = NULL, - .close = NULL, - .reset = NULL, - .available = NULL, - .speed_changed = NULL, - .force_redraw = NULL, - .config = fr510_config -}; - -int -machine_at_fr510_init(const machine_t *model) -{ - int ret = 0; - const char* fn; - - /* No ROMs available */ - if (!device_available(model->device)) - return ret; - - device_context(model->device); - fn = device_get_bios_file(machine_get_device(machine), device_get_config_bios("bios"), 0); - ret = bios_load_linear(fn, 0x000c0000, 262144, 0); - device_context_restore(); - - machine_at_common_init_ex(model, 2); - - pci_init(PCI_CONFIG_TYPE_1); - pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 0, 0, 0, 0); - pci_register_slot(0x07, PCI_CARD_SOUTHBRIDGE, 1, 2, 3, 4); - pci_register_slot(0x14, PCI_CARD_NORMAL, 1, 2, 3, 4); - pci_register_slot(0x12, PCI_CARD_NORMAL, 2, 3, 4, 1); - device_add(&ali1541_device); - device_add(&ali1543c_device); /* +0 */ - device_add(&sst_flash_39sf020_device); - spd_register(SPD_TYPE_SDRAM, 0x7, 512); - - if ((sound_card_current[0] == SOUND_INTERNAL) && machine_get_snd_device(machine)) - device_add(machine_get_snd_device(machine)); - - return ret; -} - int machine_at_m579_init(const machine_t *model) { diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 1ae51e5c7..cd68127e1 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -16007,50 +16007,6 @@ const machine_t machines[] = { .snd_device = NULL, .net_device = NULL }, - /* OEM-only BCM FR510, has the ALi M1543C southbridge with on-chip KBC. */ - { - .name = "[ALi ALADDiN V] BCM FR510 (Packard Bell/NEC OEM)", - .internal_name = "fr510", - .type = MACHINE_TYPE_SOCKETS7, - .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, - .init = machine_at_fr510_init, - .p1_handler = machine_generic_p1_handler, - .gpio_handler = NULL, - .available_flag = MACHINE_AVAILABLE, - .gpio_acpi_handler = NULL, - .cpu = { - .package = CPU_PKG_SOCKET5_7, - .block = CPU_BLOCK_NONE, - .min_bus = 50000000, - .max_bus = 66666667, - .min_voltage = 2200, - .max_voltage = 3200, - .min_multi = 1.5, - .max_multi = 5.5 - }, - .bus_flags = MACHINE_PS2_PCI | MACHINE_BUS_USB, /* Has internal video: ATI 3D Rage IIc AGP (Rage 2) */ - .flags = MACHINE_IDE_DUAL | MACHINE_APM | MACHINE_ACPI | MACHINE_SOUND | MACHINE_USB, - .ram = { - .min = 8192, - .max = 262144, - .step = 8192 - }, - .nvrmask = 255, - .jumpered_ecp_dma = 0, - .default_jumpered_ecp_dma = -1, - .kbc_device = NULL, - .kbc_params = 0x00000000, - .kbc_p1 = 0x00000cf0, - .gpio = 0xffffffff, - .gpio_acpi = 0xffffffff, - .device = &fr510_device, - .kbd_device = NULL, - .fdc_device = NULL, - .sio_device = NULL, - .vid_device = NULL, - .snd_device = &cs4235_onboard_device, - .net_device = NULL - }, /* M1534c kbc */ { .name = "[ALi ALADDiN V] Gateway Lucas", From 1fc4dda73e623686ef0a7bda9b3a1aaf9676602e Mon Sep 17 00:00:00 2001 From: Maxwell Scott Date: Sat, 27 Sep 2025 20:43:45 +0700 Subject: [PATCH 270/274] Add MSI name for Gateway Lucas (#6235) * Fix bus speeds for FR510 + add MSI name for Lucas * Removed table for FR510 --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index cd68127e1..2462ccd7a 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -16009,7 +16009,7 @@ const machine_t machines[] = { }, /* M1534c kbc */ { - .name = "[ALi ALADDiN V] Gateway Lucas", + .name = "[ALi ALADDiN V] Gateway Lucas (MSI MS-5185)", .internal_name = "gwlucas", .type = MACHINE_TYPE_SOCKETS7, .chipset = MACHINE_CHIPSET_ALI_ALADDIN_V, From c22bd0e63ce79beb81dd8e26c2c945f90538e18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= <13226155+dhrdlicka@users.noreply.github.com> Date: Sat, 27 Sep 2025 15:55:54 +0200 Subject: [PATCH 271/274] Update pull request template [skip ci] Added a checklist item for local testing validation. --- .github/pull_request_template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e09b5636f..9e5521cf4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,6 +5,7 @@ _Briefly describe what you are submitting._ Checklist ========= * [ ] Closes #xxx +* [ ] I have tested my changes locally and validated that the functionality works as intended * [ ] I have discussed this with core contributors already * [ ] This pull request requires changes to the ROM set * [ ] I have opened a roms pull request - https://github.com/86Box/roms/pull/changeme/ From 58b4af4689f364c9d4c1a9d9106e6398166e5029 Mon Sep 17 00:00:00 2001 From: Maxwell Scott Date: Sat, 27 Sep 2025 21:26:31 +0700 Subject: [PATCH 272/274] Slightly name correction to Vectra VL 5 + added codename (#6237) * Fix bus speeds for FR510 + add MSI name for Lucas * Removed table for FR510 * Name changes to Vectra VL 5 Series 4 Also added codename. --- src/machine/machine_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 2462ccd7a..8c8dc6b91 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -12973,7 +12973,7 @@ const machine_t machines[] = { /* Has a SM(S)C FDC37C932 Super I/O chip with on-chip KBC with AMI MegaKey (revision '5') KBC firmware. */ { - .name = "[i430FX] HP Vectra VL 5 Series 4", + .name = "[i430FX] HP Vectra VL 5/xxx Series 4 (Chimay)", .internal_name = "vectra54", .type = MACHINE_TYPE_SOCKET7_3V, .chipset = MACHINE_CHIPSET_INTEL_430FX, From 74aa15644d177d0b16df075885fb5ef808d55105 Mon Sep 17 00:00:00 2001 From: andresdelcampo <33843515+andresdelcampo@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:53:45 +0200 Subject: [PATCH 273/274] Fix remember size and position regression in which vertical size could grow when reopening the VM (#6239) * Change window resizing logic when using 4:3 aspect ratio Change window resizing logic when using 4:3 aspect ratio to resize content only. Fixed issues in Remember size and position that are derived from the change. There is a slight flicker while resizing with force 4:3 aspect ratio. * Fix regression that adds vertical size when showing toolbar and statusbar and remembering size and po The recent rework on the force aspect 4:3 which needed to revisit the remembering of size and position introduced a regression that would save the size without considering the non-content window elements that could be showed or hidden. Now it calculates it accordingly. --- src/qt/qt_mainwindow.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index fbc2d8a47..e88bedd90 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -936,8 +936,11 @@ MainWindow::closeEvent(QCloseEvent *event) const bool wasMax = isMaximized(); QRect normal = wasMax ? this->normalGeometry() : this->geometry(); // Save WINDOW size (not the content widget’s 4:3 box) + const int chromeHeight = geometry().height() - ui->stackedWidget->height(); window_w = normal.width(); - window_h = normal.height(); + window_h = normal.height() - chromeHeight; + if (window_h < 0) + window_h = 0; if (!QApplication::platformName().contains("wayland")) { window_x = normal.x(); window_y = normal.y(); From bc41f8bbb6472809a878d2233e472b8baf8f289a Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Sat, 27 Sep 2025 23:57:12 +0600 Subject: [PATCH 274/274] Fix sign position of DDA accumulator registers (#6241) --- src/video/vid_s3_virge.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/video/vid_s3_virge.c b/src/video/vid_s3_virge.c index d9e1d91ef..ac96aedef 100644 --- a/src/video/vid_s3_virge.c +++ b/src/video/vid_s3_virge.c @@ -1952,9 +1952,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) break; case 0x8190: virge->streams.sec_ctrl = val; - virge->streams.dda_horiz_accumulator = val & 0xfff; - if (val & 0x1000) - virge->streams.dda_horiz_accumulator |= ~0xfff; + virge->streams.dda_horiz_accumulator = val & 0x7ff; + if (val & 0x800) + virge->streams.dda_horiz_accumulator |= ~0x7ff; virge->streams.sdif = (val >> 24) & 7; break; @@ -2030,9 +2030,9 @@ s3_virge_mmio_write_l(uint32_t addr, uint32_t val, void *priv) virge->streams.k2_vert_scale |= ~0x7ff; break; case 0x81e8: - virge->streams.dda_vert_accumulator = val & 0xfff; - if (val & 0x1000) - virge->streams.dda_vert_accumulator |= ~0xfff; + virge->streams.dda_vert_accumulator = val & 0x7ff; + if (val & 0x800) + virge->streams.dda_vert_accumulator |= ~0x7ff; svga_recalctimings(svga); svga->fullchange = changeframecount;

  • 2YA-8Nu_`sP@&Wovx+F^a}d|*x>8S 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 diff --git a/samples/TeacFD_55GFR_5.25_1.2MB_seekupdown_80_tracks1100ms_48000_16_1_PCM.wav b/samples/TeacFD_55GFR_5.25_1.2MB_seekupdown_80_tracks1100ms_48000_16_1_PCM.wav deleted file mode 100644 index 0b6be1124929835052eb17fd76498307d2de4334..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106992 zcmX8c3A_*W_XhBpv4$wJXP14CN|v-(OG1cLlq890qmr_;P$@;0N`5IRm6Q@9NueZC zp)4h$B7}-NGynJ8|LeZ4xcB>=3HIaUc@ zXZcAr?`{w@3u*;5{Ht+LC-=BjyuPxZ*K_omzH4~*SwRKwui$vuyJ|R6ZRcwivYC_g36F zD!HnPx%XM#8JYwaIbOSYgTcWN--Cl2g6o27gI+;L*ZBYU-OcYi1edttY&Fie3azbAed|@i zde!t@+0`}(ng-o+e}1u*YVGwG`sq2jI}-%Q(ju<$jG&Rb?%+rrgH~SA+6r_GIy>7f z!F|D9!EHgG;Bx1<&>mF_&bBj6y#7kBZ|Bd8g7bog&eFmWvUk>S-e!(d%_^6&HpTs< zitDK8eI=c7XZln6o5(xq-UIiY*WMgTccfqW`Hu8}-~44=PNt_>&wSRQu6wE8^CI^K3{zOPBgu>)1Q}Y`gz1@%??e zG52?`rH`dgrBA1m(kcFYE`1?=BYiXdz`H(8m!~VzZ_;hv^}W9@_P!PAyXkt@T)_3_ zb!R1fmkN^fQ2MhoZcjHk@|yJX^y~Cn@7v}O`{{f?`6>Ouz3om{rmuU==5(v? z&3}$W^PH}g)$m=ZiOy@1m~Ypk2yIOUi*pR->6n z%oqHUe&;y*t!TE6kymX?SEsL}&TdT zuBV+<=xJSs1osBF2X_Pm(|_Mw~$#^ z2R8=~%BdT@YK-q8!NdN0OEA92(s_PMD_Qw&R{1U{bZIU!W;|8heQ$M?chlF>Me=rz=>17<9+Cys z?N(3M{8%tc++XSMJw-xM(NWU%XM7j9i(k?oUHzvrC(H3=^5DJPEMFlmK9;8+<;K<# z(N)aZG9<0-{8zh*>+NE1JJJ&JRmTvG#8?ZfnIUfjyz&q1D(M~$inrskIL8&WlPeW+ zqjViqm}{Vj{`QQ{!u(LY?@I(Ph`yWDK=Gdw-$dS6bTw=T0?-_EoqU`z;KQn<0t{%nHNzlzH;;w~pQ4#VJxf0wk4Ry< zw0(Mg+AX~xEt_smo=OHKHzgyI*ORxCeMuv~9So6H*so)7G}8q|Vea+8xL|g$Jtz^L z8U7pO3I7Y;lo2)ULKyre!j8$OajL4X1?fhi`;Wg?EP+L$vnc^Wl$S zfv9FwJ32i&60Q$tgk8gm;qKsVQ8F1)^vcDYkLA)WR=RxJByE-6m(CFNE1=H~XW1sM zKa&Y7{Wn9Q-!X1IimJP)8|Q0=hn$RczL5*2*@JZ*r!ip!!k>rcyz2vQAL$W9N*Y_96++<4fZt`bRFg=-suC2Li zt7&gqrWdE}(yPSCSbF4Jcv?wbz6#M#!0|t==yHfQ&3Q96e+-Jxcm79ZS4(?UHTWhS zp0-Z^O+HH&C6khY$*|->@%2P9D|t0}-+4DB-zR@0#nQZK{xHL8*E6nYyB&Sgu?~vZbFD*j*^(F6FN0zC z$%tWTo%FBdzofqX_|$8zmAlVCj-x?Na3DAp91DJS>>uFC2f;rSa0i_j%6qz%#)Nx!6GQY~qk zT$zkb7A9%ZLe6(hZ%jvu$G7BmLov2E_%CP@o^S2**xN8XJ3KFJ>X>JQQCL{n_HS^+ zD;kGY!oR)tE_v7)&Ymul{=;)$y6d~rtJA9Kf#l<4LeeF zkHFviF;Z(BcY$kKjW;%nvQ=X5MYwy79qwd>`(gDr#Q%8g`J8q8F>Mb6rvzJqKH;ix zTljZ)Mzjl>^$%|kM~5TA$DLtu_+3~ex+v-$HIL4YPK)+Kx?y3*@bvIA`}8slJ|Abk z;nLiTH+_rvM^&bT&L&HST&*nmZA;h&juXeq3C2xg6x z&+oe1AgnLiYWO-MJP{m%FZZ9UqC_Z{k#Gea8zmi8~!-C;A z_Izh>QgqjZ>{rR8vwVGHAFiS*7QyQi$^4{!QX(mwoR;j2Uyh%MUyA=1&y2s0%Zsn4 ztU%%P?6eu&td_Qcm8)b#gpVF_e>27HL-6rdh}D@oJByl3UBOOO*mj(n<>^McrX>{o zPIfL!wk7Ae()l9uU@jAEp+W}O%TlnkXxaikXS@20j60H+gJ2bNwbF6B|CTuT48{#g z8>R*9Mm}-+7alJu&X1&5%BElFg~@ng4juf7m~S7>36F)XqLI-9(Us9T(Qo0svM_I$ zFRX;W3Wqr~%`0-DHRfN5$NQ!iriIgUXocUBJZU3WJr;YGfuWbEvg5QYP8x=XXF1cM z+A#+y&$(94|Z8#-nfc0MQAMLSfso|WD4 zxz@8?dRf{fy-v1{7YDs*gsxV)0nW&mo=Qqt_tvuLF?w|b-Eh6Qn^6N~vcFN9=R}~C^WW|D?;Q!-})Jd%Ug%!Ied6M<#_#^dVM{E0{J%8HwJX|<0c+Q`nI?GRReIMo+ zL;0T(%y+GW#L5x4P~2CQv|Rc-R=!kIfhwb_^6!JK6(iY3tq^Hhz+#T*>L~^UF=%2illuqx)aeLE7H1RMz`V;-S zpMotH77vSt{{;ENI(Fi6NYgSb5pJhhFXUfrvy#uGJ?Xc6^57eIw*bC9EbC@cb7j-p z#Nj&8Tu22nS43R{KdMCiqp{H|(fsIt(UZ}n=$hy}+NfD{u^8(SHH}I}`J?UON3g87 zjNj)>wSygadJyEeRqT&~Uv1>@osjGmO8n2X1Rd8McHSMl7nFom^TPw-PvMgAi*RSS zD?AeBjeZY54<}I#2QbAVubM5xyTZn1BI-1iMi9IwYo2w?9<)Y>w4Qak01rGOYnG;y z;X<=?A71(-7tfEwv|p3G$uIWzu%G_oPAjGLU0q+Xc$N3vuSzd(MSf1U;`2r_u056Y z28?~&?^}q8dh(>I7%rHWlqW4Hl>OB1amUJZ*0;F0QSxv66&#roe-LktPsMeTdN`|# zJ^KKPH5c#Ssfn6li+gfa$YF}RF(mFFN2Bm_(R3yJ$P>IpsjWz^O-dyv;v?~2@xO7o zq#G8Vga=0?^O7xaW4wJl-5QRR11p1ngQj6m>Y*2oFHJQp44zVTolP%oR%yPk^0`4) zRZLexw|kO4khW2BSu#9%I+-f+mnR#ky4hCh@8nwCe+HE_U9GWeG2dwICYO?OCvX1JsVvy)Q>(Ma0$$6%RUx%MK>VWV&d+oRlcN1 zQZ~7Tnx39~C;pP867_YvqdyPpAEHs7!$V6|7Kv(qncrM1K3ih!tDOHGXFi%%fa zRM+RERbcIHs*^!heyGZ1tetpOYz^T>>~Y^S#8u&RtD{~=2VRhzo_rS{i!0HmHGNg^ zvysV*$>yXkb^4%{-6~T0L!TR6SHiWpIoKc;o)5poT6@Aj!!Y_F>>eJI2}OfB?(JMy zS)LN9B)iXoslT}D1pi$k{@12;sh1KMeu#a3U3~5c_o~}=`tP3bH(w{ibD&g>s8F;e zd{+dogfN3)LPPwSIf7>IBO`TtAXQBV3e#1dq4~>`Y&wS1_R7L4uA@?VEVo|&EO}YXje!82sQH8Owm3=# zB&*eJyMNl7LUq>3uqO- z6n+Ik4%_*);fIjq7V7gmHTzX|wHjUZkJ|hVSy)WO9!~P5r>BQy!f!BdPukdB4WnfD z20zj|4XE_19dAWAEnF5BjXFlHqiRu;Xms>kG$MLBdO}n!gDH=NH8Ipyj?iSPEu~R% zpl3a(vE2SVD@H%DM<4S8zM<8>cD3cb@|^Hyx@>V+EGiOZF}qz2FpL9n2_^MWv@Kc} zy@*@agl)rZT%h{FUe|mw$#3@`wfnu&K6bxnZguHGY{quRsZZ{msRD)SG~^o=b28%Jk-hV|m^ zE0H&v4k<_@o}!JH(F~vP7&@!{l6ZUkL%ccuCf*sRabqaZi{`l{8ESt9*}23HO;iWn zD5G`+1yu!&u-#+f8aeu<`lquRcPF&!z-d|u6%QrvB=;wkRG7cV`{FO-U6^kOeX=l_ zgQ=D$XJPC6q4W&-(vB7%hx-nxU%SBktaDg9*TuY0hMmoO{8KhPjJc0u=r)diX}Tmi zkLE7~YX{KL^|1IzS-*rj&*#0Fs%TG9{u>nL4m1o~i~qSWscUq#{my5{uMgYMR826_ zBK}c}V0Eq!@E^YGg6jub`P%YmsP!6Y_y0{V;oqDYP6%HXk#(YZ(Z*;|^i=e`{hb?q z5Pcf0jJ8JaMz=@zME6E@q9x%K*zPA8vBB}TP;*PMS~GQJ1P|Z0Uj^;aWcd3EuDev! z&$k8-cvUsm+8)o{%)jb{Pd~z{&(P_M{q1At{#q1&fNwj8OF2mwa#;VS1T#Oqu$uoA ze|Mk$!~kn_g_>_^@}!+?%V8+%doop!?fsi}rk}5&_O-a&zY=D)hMEu4P#3}KK~Sq3 zt^KzuxhhrG3I1KHKEH(az08qspm$etMAulI&iM99s8|AnovGTdmexxDl;tnri=i@m zot6BNpY$d5d{@#!zAuGkfxUb}<@uqnHzDjJIJ%mh?_nK&R)-FvD4TQW7t@<%RId5b zRgO`HGB0bVZ^Ot_pjHRx8YO;K(wTB#eb{yO_oOhs5)Eq1XxtE9VmBE5_Q zU4yP~M;*74O_{59RrpRW=IsqH6;EGBKSbs76wmWh^rNp2qsO90qQ|4VqZ^~MqUXaf zTtp{yq!wz6f|7JuTX%aV9akFqpBb!?{lkOx>gVQ^-zDLVA}J62>mBvMd)=dQIQo7+ zJ#3|}v3k3`zP0##76;u=ZCr1K8`8*^*`Y0v^(U-yGgcXeS35fX2CMoqE}H<0e^Lqj zC5N83lPBfN1bn$iv>l<*znAT|JK{wc?Go#8x3zi74!#EYp33c?JO^W6!KiaZ)UEb+ z5Z#q==>2M^SK!xrySL4eKc+l>rIJQt%*;XCMMs=Qn`XFK8~>k5E*AY+XYO>n(2Xm4 zuB;pGt5Wb;ZhvFDy?EQZ`@_MJl;UtXK7kieO$JmGYu7pR|3uU}`eqefa@?OWHrTEE zkhqFb^fm=bi9X8mJ#+`dBr5PPmpIu!mZ1rGHc~IN;3hIo9d! zth+jMfS;Y_9&e$fFQv9eV9o<@Vc8*!Z~N$_=-FsY^q_tJO;xrJYVQxrL~HR< zcjv4aCaQx!@a3Dhqcm^xS?^!NEq~14XN5O}JA(4zg78=G{4V^)H8xd;Ugm5UL%Ewo z&^cnioBHBJaEU6YNO;^)+JzOuPek}j9Ee}7+pqR_K96uK7AYz|>(CxQ+Rr_5cLztM zC_gsis1-QjRm!1&Ju7IX-k>pSP$%=KogGOtcyvix+p5l_T;@W8;r6mAEnf;UjTAeX zt2720mA9j#?CpJa@}2Zv3T(EBuI#6Ey*fjzf08C}?M5;9wEx}>=dV@yoGl`zE_Iy~?ciW4?mz16dsw#!#tlmDfVuUl=E17*UatF; zDzBe=Ers0{Q%v(zV~MpM0xJf?mn%3>!__@+_}NQ39h=4PP^kAOw%f+NT;j7g~Yu@W`2}ncG!)8?f?M!TZ5)-0z0sXBy6H!zT`O!t&U`({c81&K`ts zVV(`NLKU5%OwA2cV^p;EyLG(wQn9VowVwrL?BJ5HYSfB?xhAR-ok1^WTDf<06W05b zd(h7B>PPd!Ys64r8hZzZ-L5v96Q0XADiSVt+y{Au_oy`fq{p_1hU;m^KY~ge-Ah%6 z6-D{?!PmUxDje<#s^?2w`N!dZVZrDy2XqJR)<@($!3!zE6}k(;Po@>JIgt)_rZS%Y z4NhE`erlCIr!!|lopaR!saoaTVN+i7JVwo^tinA+*l}mgF@SbR|`1j1>$Iv zT5~%D>ZL9$=v8f?!iUy%zm=_Rr|a_>i;C+@+{;W_t(TRrq$Bbbbovg@e#K$YN{n`WDc; z5hYND?_kB<~+|2Cqjf{ z_T)F{@{pKX=bRsl*pB$-KWMoI=4ajJPh~|O-qcR@S5rRi6e~Llwmjy2o3UhJuEPbs zYe$u%pCD7A=-g0L>7)m1@6Hh0_Yvx&(q`uq1p@#HVV`x->gH~H~Hq#-hC?oPl-*Q&&!rr~;!i}oa zXXN!v=-EK;_eN1r6l>0dumfq#!B%{+4DLZMy(*gS@!uJG2m9sZIy;{FTNxR-pL5X~ z9%mD7=V0Pzp+aZzv53A}?e&Lf=UK362wa<|8`FhmYArU-lf$Plz;~kXM_Ol?xVnrM zYwnfPIH{p}|5n$M{r)f9n@{KNLufVA-fqT`SGdB5@Zct>`XasZvHfnFo<{ZcOipsG zS|-=q=Qr(p3Fvr`-and5pqn0tAY0X{l{tf#!k*u)P1&%J%C|^Z%kCz+*z4)AgR1i8 z_V;iNIxIx@Fz9N1)-XHf;FP!K1dz1UrNoR_W=63y8 zbzJvcRa}cY9ve-doEJtNXyof@-HPGQ6!K7!yTjUCsVW*P!_J{Vj_QcCvZ7bO$tZYT zyk8VN?zNS@>mqu%4jsQeY|ImQ3jegmOy&8Pt)o>`(@oK^Xr}Kwqlci^715e#yzcK3 zitRO8hn?ArZJ*b$$FuM}${t=#j2b&+|Uf!HMB5KMc%i{U*+kA|Z zaVUO2h&RMnIr{x7hAXJQWtcrwBlUop%jEnHep4Q5_;L7ADEfu!c>)uSQKOB70bi== zcBvZQ7w1Lj;+A@QPx~&&?b?$z(FvKR_DXZ__BkHIPG~fNce_svG`9kIqZ^{O6!HPs zb+dYFHCH>Uy}qIM>&cNWP^BDH+8vx`wXWt-JQt3mrrToKL>*U<9-1QBri=cDT=wp2 z#n+v%2$@7(e+A2)z-%}8*+NV+QN}+94dzltAJLu1 zRgf=H2RCAcgYf$sx?n-lI(Z0JOn?k8d(Be%V4-eA*6+C^mwi9c>3WoQX>IjuQe!>s z`Z~BZ%`TLKI7QQM^+Lbk9zF@PminHre>2Lf>eE9x&~Ie&7`NsIUA4Xx&IEqW#w3qw z?4P8yF2rmrU6I~yq>h;2YZ`tiMjZ}>$Cs*U8iX^kQqf?pXw0LQo2oWUdfLvH z2)AH`N>*tmhig7hqHwqYLT<5gueq9RzUmb}YoYJ{gih!bxqYD>douFbA+Jdf!Tep8ERrFf{zb^I<|hu7XSX5Aq=A(|G8yGml}dI?7)t9c}H`&8__1GiQr zm-1}N!;;gv660v&mC&mquc46`ENuUdQg3U_Jv4UYyCDC$knt{_;0iHynL8Nn>f6%0 zBcOIizdI=cE>GWfv?$5zxFzxRR&Ld544p?^c!nzRfNQ9QbNlfb`l|;*@0zG4Sm(ax zsY_0(Og{CmmGa^l-Ri5w=DTroQ8-RkG{s5vWyxkSzdMOU+8nj}nRG~9RZl^cdviFj zkrsIoQVi0UI1`He7F@)qnTb!2$?U4Z?+~m%)V>GmHh@YyW$KeuMkoE2+x__t7wl3w zvfREE=Kvg|evXDsq6^h+PepU0{n2(E+82F4%B`NGf7dsf>Nm|X+@WY-G(Ei2*`{#0 zo}+CVhCk9vD@4=v^yO2yZK$f_I?nlBuJ%l9*-I_3F_;xL;CrtM%R$i6QBPW|a+Jfp z@2_6iNTv6roQ}DdGbsA@-dT<3@UCv@*SNE{oV=X=UT4RDrEeZnK{cc;Gso~{98<}i z*1~cPMAvh6X)d(+HmyTZlroL;4-DQ;!^~7keFZ%}q_wu#r=#Mb5Y64qt~OSW{oRKKJFer6JH+>^zZ5M zNZspdV(pvc^>hFvS;$SEB!|!8ANJRWf586Uq4K&!#q})wxLpNQjO$l8cthp#uGOe1 zZ@%JNeX8eI9IE|5d)I`I?J4nPX;rG?9@qH1$$}55orfUYNxT+D>tN0h(b87jjEPQ0 z3-T1o`$?X6@_d@-mOO_znR7W!Z;OXnqG^#>T*7}ZLJ>5OH-rx#VzO$ks#%vWf(lS> zwQ8~yl&>WpvL4H(@ZYeezH(u)RL$(d3iB-M>AWGX=T5H0wI-?hb15AOR=KF>KSq@H&4DOJl?=6$ni!o#%EFtvXNn)(SI z%lr2HH6FtO|LrMSE~jw@`~6egq;7V;Gv>^v19d>8T}D;>lw0S0F8g2hniugycZ^t0 zPL}1>x1+!3QrTJWZd$Gm{}tPflFixE9bb$4Z_EZ2=6&tX)Zjl{!{yF5 z8&>2`TPH=6AbBsoB<>i$6+fgqQ#ej@Zi?r|&(h#G^6yKF;vbT$xhi*?1pJM@xyULP zpjVDS?ECrQm+NyJHYxc8box5n0%u+~IralwO5By#nJGV%jxjCqcTk8IHG$H|6D|<%mF!FQ)XzpdbVBC7$|2Zd&gK9wZx$tR zgIMW{u^!^H9MK;stQs#*e^{H`@L1#RRr*I<8aq6bO+Ryd+DGNF5pQ#8h@~ZuirIz>^rk^mDu9-rm?SyRIUBy_A-ZLthy^wSr zv|mo`4blH7Yvy7Bh4Yx*_*(o$`0G<%z)e)w{&)xk`6-@kr|UtHP4Rbe7kqSrlK(g9 zC$by!Hg>_+B2-N++?c8Fm+7!scJoqSRsFm`@R1s`38(%I{iNyYhp$~-XPuOf<7M&Z z@#6To__ug~Zr@AXlmd9~;@p1Z$K1HAmdU2$&&Is3@d_jE(jYzfYxy%*g|CZ_Vfd>s zt{t_i6pvtddezOZFAk*z4yE3-AiVHb2{8V(D;(DfGYg3tB zbclCRVpm|s_sl5V$8WlpQYssj=VR=3#I4RU1y^>Z{91DjdYIWfsq)=|gI}P-!f>Ss zn2AN-(-S-(=U3X>Ke^eZ?93M0<)9kAQMD;aLe@z`vras>rH` ziC@PNm+QR0Da!hak%zJVWEfR64S3Hzsj(Ay^Jjj+Gv)}2tL=VNzZXdA=5_$baUF`% zBA@F^w8b}9@cj-!yZ7zQF!_2FZFi@sy(f5MS!Lf?<(TkQkHEMqL~CznIcg$dC3O1E z?p~B^kJrS-RqP#1k8IYL&A9Mh`F=Ci_cZ?N;|jA$5vS6Eon)wEX8T_t?V#h~2PDs=L zo}6y){}i!@lS!0AgY*dstvau$63u^^`o5Z8ay3fyu$8^vl}@G?4v78l@O>jJ^n!Cd zWdAOr!}jtZCaTIu+UGv-ZM7P{GF4xL@_tGkx;B|E4|Y<>1%g*;u=7Rg5ANY^6~pyV zqAJ~YhWprPZ*PTKmryc=c_Aes>9Ax4T)QtW6!(ZHQi=6pS9s3zJJwzCg+nUK55nn#%6s9Jijow(anUlr>8Jznrk{i1uV;N?8F z8t%Ol)w&P29Sbhg=~(EyVEDFUcC-qc)%KyTTobX;55f%Qh8Dp{f9U$O{^8=5Bv;Yf6=`=2|qu8l2e*(T24yAW)x9&t8VnrZLL#91+WHqLK1VAIdx-CK120o|?@BJ3A2c9!Fw zG66MA{@?7C4~zHX)LKUl=?^&fGz`7PQA^Qwm4YdbTb6G6hUajn4)H?}1mAQc$n7Wot#MWTjl)LX_$N6%wf8VCVqt8{U<2OA9{{^^crvK zwJ;8QQuGy~hMq_H7~A!QR=1nTyk9qNDn2S`KH)cvI)UbHM43H6rqCdHopNPTv;gjA!ougTfuhE!~aRW~7g@I;@ zpK3bKZT$Q|P*7b#*5kfBW~~2mmwhn(BzbVQC+B8T#DmivvVMaKxIdNC-8AtaI=v92 ztf2ptO&{FI|6B=^@5l6=P3biWUr=+7GY2$We7z#V22c}qVEQ0a!sn+89p{jlfJg22 zuW+j=B)oxMyG0}|gihaMsy|G;%u6o!(`_c1tBRk;^f}g=5`Ijd;v3m8%f7V5dxPNM zE_jf2#ri?iRjRMQ;|1}zCN5u#ua0lG^ULGaCR?tf!Upj&M!Vt6v#92{KdDPI2Y0pT zAFHap&0UlX?#7s%;pYl`IV#uLE+Zb(xCalT0#45Ou4J;&6!Opf%5(T7PwU-8;;*{y zPeU0yUwoV+PKU_f!ZiJ-*zSBCmG)fkYMj5JBB~p%uE`nul>fg@7oeD`Cx7rICfJQ* ztLSTlu)$tz(3VGigRj|C&&%qxoo2=7)81t<-O=z0PUA4%q(zPdpie{H5ij#uDZ2j8% zxo=%W(<&}fHvckM{vOq3&7SiqNGZPMj&Buv#e)f$>nr~MI?Ce*Q_-WXQZuh=jEhb> z!g~Fd3U=s-SNx~mzf#Ym7=C-3f4P<~87Iv--i!Tf7boZ!aa$acm9p9c=&y<}BZ7e0 zlSqy1^@DV6SDD(!S;o2E>cJ#9H<`b%-VSdwzjOm!YDTBiG+#v={bGB5xxf7YBa4|&tnTXmp%xd>+zpb~;`ig@@h|GN=AK^2FRK3L zgcg%qlSI(DIO%=wTcCF7Xq6_p#;){ej@qrOUAvSgQ%|>kZ*VSObrR$sN~`SDS2*eJ zwJ++!@<(k~$1`xY3ba+Hm zUS}HU7`2^W-Pv6%6$^gWQQIY!X7iHn)EzmHG^7#|u6GseHk~I>%Krby3u*%cPNNvc zinPNbb9#CM{On}P=4JevO>91bYkLPf#rpYmt<{|AfFDrM zo{xj1pW~dvB4V^!Ype70rvExYo~(nP1KVEk-`YIkkD%O0Zs<_Deli?=o@y$Pj-`nv z&_Ugk*qrX!$%eQ^d~SRuZ(vJ&8vY!uCo}}|?xHRCV!My%tD>%BsJm_@M=!w*JHr~e zwfQ0X=_~8{nt9HhH1ONjsE#f{jdU~*^pLfg=O_iO>ozO>BQNG9dhRMd_$kM!Nq^)s z^Zpk7{56N50N(kAv#?XuwJoftYc`Fen8KZ5IwZgGz;BC|<<85!z`LBqgUS=_q?}7( zxO;KrY@Br=kKhe%|C64-Yk=+6@)@e~Ez5`R+tqp8rt{TrZMY4eMup8seXpZWq;K==9s zTK6V7`(|>cD(q?Z-vzHcC?c;69-|pYsi*RaiDsPJZ`0RwfOcY|Sl##l{`ds*KW2Kd zG)$YRsylA=mnR{F`i>6Yo<8cX>Wb>B_H&ckW}$hJb@;C#R(w+Rb*-PZq}~2??6o=| zuUhQ`I=+)2Qkq_k!M~+~o_CEq?Q>0&4ZD-l>E}4=0hRprc&(}Jj!O=qRlS~@I;2Glnu*jb%4pw-UU*tkVYbWpk?)J0nI=j`!?}@sscsZkagM^D zoPe`TmEXWy%?X=vS$`H4MRaavV8qQPbgDbrTzfi7L_KL=AA}~u@ml_H0+qajyH*f# z4dg$h9P(;n^BP^YN!I!|{8kOCoo{{e1&`3c{Z!IT#Y;nbafTJxYX!2;5%^I)zK!P! zTAzn0nVT_3Z5+6d5^7?VpTV31Dd-{msy-s5sVAToQ6!Jst#@@)YQm+?<^7o}|uQMpJ%CC-s4z>#b2!2wuhdE%8Ye zqwv-|`mAKQ$l9Hz4n8}WMn`6!Igm}01+qQ+oS{s~jfS?9+|v)~v#_HO)%%Z0l_S*d z-yGJFqUtty@UZL;JxO_luUM8>v`Po~5QXzF<(g^g&Gh&CUa?J0xLLjT47}Nbmj*+y zx}M6}3`H)+f8D93EjrI-_3~0)d0VkH6Ndc@0q<~(EoKtxMEk?BjTLETUGT}h?V(}evJM7k$QeXoZTtg`oNdZ?Bg0fb@o)=xh>{TXLd0&-btO@M3gST zT0>QYr@5E&d8>Ia{iVshW=zhHqqutfuxI{8#`na1JfE;Ho~*)a#v3>dO0MCY)Y1u> zPgUll5jF*_O)3^P9a4`nIWuhHdGBWK=5$zCk+VJ3d+MYw^QwMVgKWjeXHxDxc_sU} za4TrX>GHA(kf<=$no0>(iq15n){p}*lLOV7#<>vRJ*8?K0Fk<2 zyuqSiEo7SIDi%4$hgfDq&_dN;Kb-2`=hF8D=kitwK31z#PV zrGB*6)Jka%_;kE_GMCYA_AKmw6w@=%^?7?T7z$6!tq<;@mClDDzuWDP^eNsl-*g0* zj8hq}F^%}64n#A!)6sP6U?|fb1H1y6y3(w7!{0G>p(I^7Nj=w0uASrsG&Y6udhU#C zcOCpivU@#mdXd$iMsrqzOGltlZTt4ES?|iZ9%37Q(Z{)p;SB!dBzW-<7xgw7Iub8s zk=WGnf5&v$lhRep1sqCJ6KNHB)D_fS*)x(y;{LiDq1lt|@i}z(8x->A&N-c$Zq0#T zu44aFHU2R4C>TywhpvJY^C8_EFz-9j-^Y$Nz;CyiF}RKgP?T3yG^ynYzb9~8eO9*q-ii}Ps05mDkw(&@t9cTmp-~;|wbl{4 zK&8hZP$Bqsh*Q~9gmrSobsX;yC!z!e_Ib3I2D>;~tq(LFM$U$y59+Uets>4VmdZGO z-tZ}RRnNa(c8)KC#+Y%g2+clo;4mFFmj;=mL%R?|-K3||0s7v=DR_fU$U0{qr9J7` zH&nNqpvllQC&{M%4^ZcWT=jaWyo-~SJ+*!g?sx=q^_P3wO=@OODwL(WtJCxKvDr-B zm(Ut#PmaFCwLGM%Yrt2|%Z-{Q#s{Ku5mmZ8{#ylE&`mX3pSX2$FsB)Sa>-lnEa~irSRpa@hd4xuJebpts8FX%t+aJ3uFzCGn$1OC{5P`VEt@MwB4>8(bb07*a5TN>`# z?$;xHgE#UHpI~<8*kYZx#m`|K{ru6*G|@mPa*1Ba1NMJ2pRuHRt9&>{-1hY~nj_H} zi~Wg-cHyLu%C1ZOGI-CfDlvXuFA&N*svs zdWi2**o)=(SZI`&bCh3~X8@Pzq&$BT^8JTFHwXQ=21{xB{LWTQ#nhC3I7`3le^zFf zTDmkY=q_hpro+c_3_eI_Tc?%yr-?`^L~rzfn|*neS?|3KWN8i8AGAKx=%ik1xoyzx zN;4sMB**N2)$}=i_^YUq2RMEg(6N7s!@}ys>+SYPF*{29wxz#Io09DzM=OZ!+o+Et z>7{C~o8)&fyj;hm!&otWfc7p873!xOxKzLB^L}Ys@)9v1St#Rdum4*0`vCOG7j~5=6UCAV zLu%(K-*u?L(v;LSW&uvnQP~XTCb=~X&vRXEo%>I0H3l+03ZM2;9gRfcrI_tHe;?s_+hw%V zFw8N6YWh%QeI>S9atgLN_urVLwJUqW{#I5!?lp7sW^PyJb$B;e&3FJKw-(REU}6)s zX1!!;+$r9WbAQgdoc5+pn(zkNz@?3GU%u)Le!vL4-wB$vz|+^dwg$5IlpX73|F1E3 z-#T1qMb5% zx@JDkxj9-6FCKy@bE2xzhg^VFo=UlqZayR0ZO^}fH(kTocHwb2)>$OnF8*4n*R#r^ zGrqWsAoRaSvehw_A|5kf;`?TVmsil9$0i5bi7kl|21AX zk+h=L8>F*gLbi`PMNW@oYkVgc==9|Mcwu}ckNsVII*zA)T+i=h(m_o=O*Ok+eUwk$ zc8~`XtU>m(ci;YeS`H^sl^e37#w=XlPEoU-u^{{0rs zG}LqNpK^z?IG6|>%c(1mQFJAPhv|uT(<$=4D7SH*eYh0cjm_;TeCsa1%+&(hyy}pu z;9j4S@B_!bkbdY9S=GWU*&m#N`i|O1?KVh+Y*lSnHszRLnVrFMiZ~B7v@UE!i=WbS zo6Qk#udC4zRvZk>*J7-@#M?CLd8PQ-PmMh8KCh<4t2$x_s%fY=uj+TBX{^Jv(H~;* zb)5Ett3KfM`>6Gies+3vGxu{f1y;`l)uYg=OX#y1X_n7aO8@e)XQ>m)=oUgy70o1( zItTvj=NBC38q{*M`kw9CM9Zw>yX=4#@5;vOQ_Xs(k}a?u#y<12R{6f&o|QfcT#SXcq@~< zavWZ4B8IZhIlV#D&CzrFHMs~6W>2|%!|D2ivVKiO=1b?O6*|C(V(Azik2XnHvuXpq zBh^72FFw|bs4Yng>bNHS_%EHs_PM9#RJqBOx3%;`F&db2*LuLUFi@m-wpt!33 z29bH6`mloNoXZislz!S{|G(soFQ=Su7gO_bSn+TLkMDH7yyYs4Y+B`eXWvQ1=2hu+ zc17jk)ld9{4Pgb7P#g4zXLy2hnP)c7Rk!Uj8GJ~8?qZ($Jbpl%FjWce4@z_L*HT6W z!&R#PY!+;Y=&Y_kTNHcb*Pr{wdD=kabDgtzSc}ItjbeHcRy?YzSS?>Silm?Er&W~T zC7u;{1rt5bHMvlhB+w~)Y9{+E){c(555DB{uP7J_h3e1(b%?;p6BE7haP~QLKdPAv@Br;p zaG9u_$R#f+idy^RuOT=iPcq8q3+#-~giuvPz{2<+YG-;}(OJ53Jxkf!GdMT@;p~_2 z$aooBQHNs{we+_-JqI_2BK@9ZH?4F=@|Oy+zuC%){O{2w%6nnEZ}oYP#fRb!D!L6K z{3&&36^`v@nl48NdZg9Poa7?mi*#i_ZdShFi?l6|rG>8MUGAn#Fi+2FoLKMd&Np}( zeo=C#2+B+SUF}67p7wO?Y<-UF^;asGEa~dGf$K3>s!Hr3s_Lr+e^wVxRUQ7rPs+ih zOHF59pmKZ1QSTC;f9cHCfq*5wzYbMhfxkV$`u7z}uhMf{U|u86S!s7Zk!JXu_gFx+ zk$r|s7ysR8|NivE&TJ~?aQZMud^zlzjJsFzFZ(#kEI!}Iynzn1z)PI;cOlzc(bXSE zWlz?Q6gw^XPEBCZ-S+t+@v~JO-5r_~#(+hz-SBi1hNwFPQ z@dkKzAqV~;8t(!TT0pE`U}q}ppUuQc{T!p8{lAxzA4SF2NS4ME&8&S#EkEGtquca6 z#&fb>;1axm^A0C|)yK1La9A#glbg7A@Yw54d0i7(~+b7R( zmukj;k`w=&OZJoozzpEh+WUX)bL&6msRuc(y$G`gQ$OoVSsn>FXR&cHuTx%K~^htA*O`Y6oeqg>%-dytn^+n*x@*mzYi?^|YzlOH z6fd1Gi-O>7OnxsPA^S|PzMfsW#)R$--1Dn>0Y97B93H{>JqA2O+?8jMR?a`CF6?6;D&ChBCDHL*k}1wKtc?^t@;9 z7H~AiaL;yd#j?rvnizdcIytx32hU-x&b$Wm zPWMyhhfTc<<7lll-BK@4lRWci{WYfZ-l4Uwhfp73xQ;xZ9aMB<7_`h>@_hTAxgy6z z*?8Z5to1jLYk?!)4<*;pz58%`_UQ(hTo_*Hz!@TS!>6g&ynWRoKl=FRl z{BKT&oO^Sc#%INga(3rbk3Y@XlQT!hZfo3t` zjo-_4ol&Jq^SZzP2 z>PHdQ&Tq$wv#T8CW-PhPoKr>BUmPWY7bsj)s4i@J6&}rvHB3s=;G@Itk^=|U=#dr>ll%$aCfp9$K7MC z-WNsLRFz2Oq-+W*<- zos{EldDI89R^Zc?PR~krnOCWlZZ+#&LHy33ojULXu8q6$zkkm;oO70`^1R6@&nom2 zp@CIuhCMroiPz*(8ONF`0-nvC9jPPxr$F%;e%cDxE#_MmlEJ^x5rs_kWS`V=GY0tF zG~b&d^Ly*mNgcCI#;$YjhK~0HUwfnpmJVFyNow=N{%7-(r<*VO1~U!dT7IB%udJ@0 zq>lR2oIcDk6Z(nTaV}&%C1XE;tB2^r$LZ+L#bbTFncfhu zsi!T&AJ_~S&OaiA^Q}sfmZiV$8YPo zq($L({D;Xj)skF&(1s?zo6Z=|8z|{nh02_PJe1{o`ZD*6wT3XZg^G25G8u!k_TFWV zR-JO`OADVXVpjV+rVl-d(mhI4{_j#b`^3t7D!Kk<&kJ(4e(_Z2ZujU7_8Tx_R$AP78kb4OnJ|-0Pm) zWT)ogsLFit7hzOI>fj{JG(`PYiRvE8m7Yr}_2v}scBPlXjci(~FAcs;=5LVuqtu1d zXzJJad$*_sKY*RrP|&ZMJ<6U)$se!7MXTd(FseP(^e<1L49}~p9*qA}fED=3g#CHp z7tVDLujxMAI$M>|OFfz|c${I5OQvTIzQpC=dEQV`?6;OK9Z*;&4A2=*dwgF&kFa7NBM9s zQIw56Yo5mM@vRv)yD|&wa@jb4$DuvW-p_=_~F;K(4zM%R#;nPSC>gNA$f?$KG zrX`ejJucB_YO7CmhCd5-aRRP02fEcR0KrVm0HkpBXJsQD&cSP@M*QL@m~iX2|k?wf$eM zZ!<$}*HvF-Gv$?EmcHv+CRyVpD$51DsczQ11+DcMERXrhH|Ks{Xe~YR|HNHavGl8> zW&PprtzY*4%Gsz(p8ekouH=wrpH8+*49ETIgp^sECZ>g@csl6(WHCOIGCt4c8v&*?f*_^>)^DB?hzdwkjNz`{sdoqT0zSYlf z@o9#)`1#w(YO1M=+OoMSZG`i~>HH{TNW1O!d&io3}wo$pig<=1ZC8wK;cteda zfC|4XDVAIwFU)x%XGqSYIgRk#<2hgEbcq{@*ekGT2>J4+Yq0m(6y#j|G>`&6LF;6n zm{*1de>~U!I~Z(}_4}!&BWW-0+aS4_J!R2RzGdjYK#%(ivDH&f-eX-ul9Q{6NPQgB^1yg-!CYzUOTgW7L}b@aIJHOh{G3^$bu?{w@xBQKiG|;w%{T z0A-o|zl6S`P_s|C-bXKX;wWSYG=t)}39gS5k?-4$t|I47Yco(}bfw8(g4~Z&1;0^A zJE(&1V9F9`@soL`neuv*owyiY9kwgSlm7aoEqE4NRYc|C<@8(yKgm%t$8@ao$FhBg zKU?Xoy)L@X=F)uPxWAjq-NRYmsaG~noplYCD@QlQxxKYgp2b_`rz>f*6+Ha)G~aZ% zTo->&gQvIQlYy9gEk*ST1Uuw)9eB~5Fy2|o2WH88(oId{|8kDyl;&eL_I&5?q%@`f z7CiaHJ&n!n-xQ+c^IFfd!abs>1m5Yb7XLw>E>)8}2X%`Ev-F|fhOgPDtyQ527g@_s zMgNr$@gZNYC#`Y!Kv!`Y|6p)>F)jKke#+eRk739XmD`KZJ?U$X+j|XHH=C%jM`Q?%E?}lur3`3}qA9@7|(yA2s%o-~S$TfMUzxSaCYOC+BmY6}$r8T&!0<&5n$LDc|Tx7BV&UjCi{g zj%J;>roq$nRW{N4k-WLVRK-Dfd%3xyPgJz?{3cPS-;R_2Qfof$9X~tHlXCq9aXAe; zw~~Lq>tv3AMT=?g*?40(&6IsY#iJ1ATiwjpxzx||be2%YN0Pg#>-%yMa|7k|KBg=0 z2-##B){ zht+lclQX#Ew~ODEqV^U~h*ol~Z{=3Y`*lXks5&m-Wo1+0%486wv{!W*+6%xD~hH4851WST}oSv^5^Cr-OK&>BQ@J638 zxd3{9t&%v+eP#a_wO7@1*HRHTc}C$3&vhP-Z-K6_B<;lWMD_2>c)5YtoKGiC6z`SA zz;IJ<{dM&3ir$HKN8kF~(&;>c!{Nozc3ASXUfLG!)aS6{UOIM<`o6pw-K*eA8!Bg+ zJAQ%Va;wVl2AJ~^27HX)a3fEw2VGv<-d_&O+ECfI`s|BM_%&7U4H9v$c~uQnzpt`0pN(3;xr3X4~&F^W)#E%>t~RdipgGA^S# zE{0Q&a0Ncnzgq)+_ELs}MRjj0S2oPcz4=1KU1EmrMzQq;2Vw{;Z)@7Q5KX%`u3_zq z`3#8a{@a~m?j@4`(_gvLx=s_LciV>#Mfgrp`ZK<(E!W1YWB>8@`zfl@I$INI_d=qn z7Tmd42lpP_R!*j0PRZZuGXZXm>&6FjR^%MWc`>f-Ro7Ac>+I5TUUgGDw9S0=R8C4Q z++Ua%at7?!rRMrU3_nEi{e`b8LDJX#9~ZWmHh7=nZcd9mtEcjQ&gz`Hah>>2_|z%x z;;G)cV&O)v!{=6|k39QB5BXaB-bw8Aq9{hu9#7$#KC<|2dHbSDH+v2$n^3A1Jd;*W zr(2Id`03l@a`Iz?35!v5KyjXLdDY7hN1PSBmHT-W?M$nE#Ob-;dS6f3Z@@&)+1-Ka zyngETFDdV1?yVhd@q)>sKXf6_vfEEXvFtN7Kc|d7v0r`t>|0N@Tthc)qu@95tIE<} zZ^5PIR^xFR^*A=lX4c*fI(heU_|z?$tX4eiy$$tCvQPGZ#a(r?%RghGTWR=vTt|y= z4P8^*yzv8i49i7xfpD6=nQ8j#>%|ZG~y*Nf~^s7%7I2g~t zODp4tRY=V!k&7vS2xc!0<&iSKeq{xAba*{8$5jl9vr01 z9*{@*|EcKzNV*TW8_WL>;OCqtLfNCtNJzHqk-b9p zEV7b4BV|O$h_XXQMzVJbm1GrCi1M?^mWm`i=bZohe*UlLrHtpf&wbz5_xgN4pU?Mu zU03RaP4;ElV2#sTaBe*mdQGI2w+?fYeS2zfEbkiAZy{edUpx3p!7e}lLZgrArBArk zJLRi~=Hf`5eKXC^-lF+4j%)UCZ?iNlhPsV|QVVvx&mHeAR=&44!E2a#Mp5>UziS6g z_KShcctUWFO@FHFp|@mJr9b~9>o4)Pa>kcpNnguDJ9O$#ljSyg|G&efG~WMgO!j}? z-91dUg15VXV!Wa=`aygW*0|AV2j{llgkE!8c^d`(xA)%%PFAMv7GdziaOM%X#s}QP zs`T|%Rh;@zDZ4c*$vH-q@b$GaZ4>O~pI8m}8m!hjtD{vGe=cAVjQ8-*vz|f;b`N&+fV#!j9bZFz0B&5 z)C#Msr&Q2Ox0wU5)Sh6maJk4;IWDageQ9NuD*7zDi=xdu%c^$gX={J+(~)UbKwaaZ z-!snlA^Kk;=>P^^ljjh@rWSFVGV)gP;Yvru)&wzkGn86X7lKrOL#0OICOE0Bmr<)F z=k@2!>=21{>F510eYq#iWef)4$gJt;%CRpTZ)&(aHxZf z&{y^AL$1mLd9x;$=?sKBf{7R9u79WlCD`*}jCb&rIq(jod%{-N|s!$nO`ZcqBJPke+Hf+KKgVpn&iKG{JaE)Q@K78x4 zs?}6zG}Nwifjiz^Z6~OjkM$kF*=w^!R!V+CQOsp-_yUIf3{0x1J94$&rqTK>-`Cl+ zUDtLSZc00&*a~+G&MNySl#*lcj4{6;mWqeJl`Vd@BD549^|HIWWA*Pc94W6z$%7j` zC93M_!Rv=ntr1sq@%m;-b-x*U@g7&vtH)^=f_wa@O813$+9*=bg*xDKS@kD8!gna< zIbWsla>|vT>Kt08m+=aBwl_a0IDz4`C%x}E6RkUJs6*rj`$eoYV|L;n56qatF!qM| z@H>tWoOb*&F8hVb{~!@Dg1%d*S~V#?J{Hs<5{%Kabaz^NN>qUy>HT+QEWP|SN&Td@ zcW_%remQUCUMTQRf;W;qyp)gnBgZ)K7dpV8HSV)9PO%W)Et20}RO=lgPFFy^6HX?W ziP=P=)uMHHnA`F2-gH->NXwD*ws7TVrF%~X&TD&v&^c_s@12V+!!4B z5ad~FO-gey|_-nGr zLKyQNhx!6mGems8OtB3X3kUSH-P3EikrSD~H~88dei4#qFs@yFY6Jd2X;{@j56dV~ zunA|H%X9i0dz!`~epgYy$yB*uA=Io)^R@~#=S=+vu~I>) zuiQa#-}4M*S0{V|KT0P?oAZ;O5m$A^XnmCi?*^~F4gFxmn~CemB6Xq&O6yK`$H$u;KVr{4&G%bksye2V zHvYArzY0qxxWW;+Z@2uG5991Yy={Y#o8|VB=1XvX%?CVypShK{L~w9IaTh;dS45mt zUrnZ0BRy=JirY-azK7EPpKz|1ceIDpKRR< z{`RBjI_OGxm15{9VuM}FlKWi)J6Vo{%N2MJL#*T-ExT<=%%4~c2T!MWu79)~o?l7S zzA5uP!pE8>*KOkOPIQ-Z<*9l6l)SKdS3HMzy@^`+MeYfn?LA)H=ibhr^7ucP(?j#_ zFO}j`qBq!crF?vud9h!;>5#6S*UX{*vPQln&$79&S+l~ zCpB@Se9)y#_$oerGjvRD`XY2xR$1VuYU&wSCkKxdjqm7v%c{@rOPx`_h10;gMUh7n z&PO&=iCe6=Tq_^8=ajdC6_c$}C>VL5FQG36RK^;Kj4I~mWRR}v35$GX#+`4=+bj8Y zqj8hO8q z$}rB&Bi>aWz3^4>o8X+uO5!Llzi=iNe2N#aPG-Ah52mH~(@I(BOKx^gd8vv>c-m*o z!%gq#T?{%;p2EP7Vh_QtUVHRc-FDY$IJBAYvPZZm!{xn#*!Wg&U#8aFWT-fziO^H`ca4RWA5%z)r|k-(mMQ%li?n;TLJ6OgFT`9I+N!kar74&cu_T)KLlxkozy+lG2UUg@j zAi?ZCsWN_<%D%4#|1oY?A&F~brH*nKk5|mUMWS;MhiEBe?}uegQ#qcA&1}TKn{t1u zK$7KUjyEy*=>$z{q-1*>6utK2*7Souf z%ddl=v^UUEbk#GPvg(1n&Xp`+SIk&6vmK?5M{{6(4YNo}k*X~N)K^tWrIWUMBo-N$&@U zv+@A530(8#RNE$1+IRR6@9~~q^o+qt*m95+D$APX zWQC)A*9@{*51+PD+%*t$4`tlcqOqH}DiG-?%2tWRhnUz|UrSV1QsYaFX{M*3K`S1` zGOlAOG0-0Ch0^-C>hR{;Sa+D)D4pS~1t$QW(gEB_O#a4EtCqN}hg)iq9~ zbd_|IQP0D4E92BSM(_^Pndc3$ut&}1nrb9};bV7=^E!@DE?9HMe*E)wYu^ZE<%u-m zxBVrqrpw)1&EI6+Zx{H}mzEo0{tkgMzlzaO`oMeg{U-2|f7OL_SiPf^6#yI5eosXs zG+jHaKfjnOND&O-*#;~B{s{Gi;mwRtEq+$uZv-84Lt)}@ZfjrF;W)LHiFbKgm9AH` zbToA|6um6dJ!J2?Hp0v8jzLar($0HiU zr^HaOqAa5kOBo)`n_b~;X5*7B^L}Hxu}0FKTSfdsT?xZw!xU6 zJrddJs=?~Sbsdr?;CVlI{zQDTxp+>! zHXXG=>B`Gj`IkBIwuheKiinE&-DxrKKS)?Gd>hi_vFGScv+oPlhjwB!=xzH(y>}Yc zbkj&Il20D>Nhjscc^rx??y$eR&K;f)Nsm!S&BS|aF?xr$eh{bM$9K2^-EzxYd$@-M zUG+ulKGMYwMz?YlSLnQ)tk3NUk=hW~f8XaEkIli_cdLA*HQwiW)+;$NOSlAgRAeXN zPAznO<-o?XQ*}LLx|{gjGJNO*T?rGcpL&venbxkB6{D5>KbM}~PFQnpDEKhi$c|Ed zaJZ|gJLzbO6W(KA2tAfU?Qh=oblqTw@gcaxOq?J%10e-3YKPn!oD|-IpSMJp&jRnE zkY`&jO3%=}}(c*ylj+AXp1>L^Z3H6;@Mt2y;E( zWm$R&&3sg~Za9yloGQ;kOln)KptsrzmLH{$QtG|=mCnuN?v7E;b=5|)gfBsWQ|8>a z6z^hr>H^$3t?Q_e4!1_Q(ioMy-SSc@{!w|OKAAR4t6p_L9{HM1eoywhqc{8@@AIhr zT1x4S3ObE9$atUnfA=JQ)=G8`b{IH`c_-6>@_?W9BY$coE%|)-uGzN}VjRKe^2uf2 zQpmeic{lJ#Yvbn&MD+w?-&+0X9}2ga40st^ERKz?lv9exOJix@gWlWge)qYje4Hz` z3^E-s`!+)K^L(aE5bt%mIH&->?QO1s_i01JL|>-(CQ%cdMll+*c@qyGfQJO4Ew{Q>;p35a2;R*lXR+p%?jTq@ul{k;{Rru&TvG=}Q^!0fBn^@T%Or@vqZe`uY5WNL6brC!a zw~HLpKM?Gsu?|lP_E>q6pSc6uucXK26ui5DPld6xS$?jNNuu~|`!BX-nP{xL@{n_DTln%mrYB9+!r zW%JSaMmUtlE+uJsOz&|RR)~a}^!|$~<-PIFSya|9l-nMxqb1H&Meey5swEFsGS4<9 z>GqPU9`nrO#r(hEoU;PB&>?q}hct6Ly?XA3w>^NF*Mow=vp#NeyRTqDTO(kPnIZHM4G4->*PAxUx zh4ScsT%nx!SSi0&VB%juz4v^|XT0!d;m!+2b%411OmuGKnXI60TARbUWTabqWWSJ| z%7j&Ap;dWntbV~Cf)m%f>qf|B=3Q4&$wY%h?E|#W*cCM5&(l)#yWH44KNNHZ)(M6Oh;xbIjpLVll#1ZqdLgHrUTy*Q*B{h zbHCrsY4}t`H4A^N?sLeTy@AOb6@i;VBg0MXVG#J5b7a+8oVurtN>@)&!(A;C?P;mk zRoKaNoc;t)V6_ag-;9arE$t`sRo0uaS|>?uJpfCgN-y=QXJhA86ShM4Wk$YcD8co9 zuy0E{j(?iS;P4J!<160NHE(gP7(VZv-p82F@`2`2blpYLK|at%yJ_9DJKJD!8Jtje zRDCA_BEAcWf}Ki^;F3-(Pl~Zty z@v0N|@um+{Xxr=Mo)t~5qS^#gs;0Kn&(Bq)Q+vR`ZMxi>xau5tddsiPW^I1F>^9W+ znXg?#oTkMS�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 C@z8+) diff --git a/samples/mitsumi_offseek_80_tracks_1000ms_48000_16_1_PCM.wav b/samples/mitsumi_offseek_80_tracks_1000ms_48000_16_1_PCM.wav deleted file mode 100644 index afd95b8d0ffb7610c1fb7d7e8348bc23a1c653e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94412 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 HnhE~{nqONQ diff --git a/samples/mitsumi_seek_80_tracks_380ms_48000_16_1_PCM.wav b/samples/mitsumi_seek_80_tracks_380ms_48000_16_1_PCM.wav deleted file mode 100644 index a5516cae85937ee59531250ab494afaad7d36ce5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37754 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 diff --git a/samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav b/samples/mitsumi_spindle_motor_loop_48000_16_1_PCM.wav deleted file mode 100644 index 190dfda3e9eb1731d2ce446b085dd28d1c2fdedf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172700 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 J647yy{{aKOSGoWI diff --git a/samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav b/samples/mitsumi_spindle_motor_start_48000_16_1_PCM.wav deleted file mode 100644 index 2595ed0892860e4926697fe01b02c2f83b2d60a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44172 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%0DVBq<}1<6>!q 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