mirror of
https://github.com/86Box/86Box.git
synced 2026-02-22 09:35:32 -07:00
Merge branch 'master' into master
This commit is contained in:
@@ -139,7 +139,15 @@ 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((ARCH STREQUAL "arm64"))
|
||||
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()
|
||||
option(NEW_DYNAREC "Use the PCem v15 (\"new\") dynamic recompiler" OFF)
|
||||
@@ -184,9 +192,12 @@ 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)
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
{
|
||||
"name": "debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"NV_LOG": "ON",
|
||||
"NV_LOG_ULTRA": "ON"
|
||||
|
||||
},
|
||||
"inherits": "base"
|
||||
},
|
||||
|
||||
1
doc/nvidia_notes/2025-01-24.txt
Normal file
1
doc/nvidia_notes/2025-01-24.txt
Normal file
@@ -0,0 +1 @@
|
||||
THIS IS FIFOSERVICE!!!!!!!!!!!!
|
||||
1
doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt
Normal file
1
doc/nvidia_notes/B = BUFFER. REMEMBER THESE!!!.txt
Normal file
@@ -0,0 +1 @@
|
||||
B = BUFFER. REMEMBER THESE!!!
|
||||
@@ -0,0 +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
|
||||
* Get coding
|
||||
* Alcohol
|
||||
81
doc/nvidia_notes/Memory map (RIVA 128).txt
Normal file
81
doc/nvidia_notes/Memory map (RIVA 128).txt
Normal file
@@ -0,0 +1,81 @@
|
||||
Memory map (RIVA 128)
|
||||
|
||||
32 megabytes of MMIO starting at <nvAddr> 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
|
||||
54
doc/nvidia_notes/NV3 DMA Engine.txt
Normal file
54
doc/nvidia_notes/NV3 DMA Engine.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
29
doc/nvidia_notes/NV_PFB_CONFIG_0.txt
Normal file
29
doc/nvidia_notes/NV_PFB_CONFIG_0.txt
Normal file
@@ -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
|
||||
|
||||
38
doc/nvidia_notes/PFIFO RAMHT RAMRO.txt
Normal file
38
doc/nvidia_notes/PFIFO RAMHT RAMRO.txt
Normal file
@@ -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 <subchannel start within RAMHT> + 4*16 = name
|
||||
Store in RAMHT at <subchannel start within RAMHT> + 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!
|
||||
|
||||
26
doc/nvidia_notes/RIVA fansites.txt
Normal file
26
doc/nvidia_notes/RIVA fansites.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
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
|
||||
8
doc/nvidia_notes/Versions.txt
Normal file
8
doc/nvidia_notes/Versions.txt
Normal file
@@ -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
|
||||
|
||||
|
||||
63
doc/nvidia_notes/gpucompanies.txt
Normal file
63
doc/nvidia_notes/gpucompanies.txt
Normal file
@@ -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
|
||||
|
||||
|
||||
21
doc/nvidia_notes/hardware cursor.txt
Normal file
21
doc/nvidia_notes/hardware cursor.txt
Normal file
@@ -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
|
||||
BIN
doc/nvidia_notes/multithreading.pdn
Normal file
BIN
doc/nvidia_notes/multithreading.pdn
Normal file
Binary file not shown.
BIN
doc/nvidia_notes/multithreading.png
Normal file
BIN
doc/nvidia_notes/multithreading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 114 KiB |
112
doc/nvidia_notes/nv3 driver init status.txt
Normal file
112
doc/nvidia_notes/nv3 driver init status.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
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
|
||||
90
doc/nvidia_notes/nv3 driver init status_2025-02-10.txt
Normal file
90
doc/nvidia_notes/nv3 driver init status_2025-02-10.txt
Normal file
@@ -0,0 +1,90 @@
|
||||
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
|
||||
37
doc/nvidia_notes/nv3_object_classes.txt
Normal file
37
doc/nvidia_notes/nv3_object_classes.txt
Normal file
@@ -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 = Point w/zeta buffer
|
||||
0x19 = INVALID
|
||||
0x1A = INVALID
|
||||
0x1B = INVALID
|
||||
0x1C = Image in memory
|
||||
0x1D = INVALID
|
||||
0x1E = INVALID
|
||||
0x1F = INVALID
|
||||
|
||||
161
doc/nvidia_notes/nv3d3d_t.txt
Normal file
161
doc/nvidia_notes/nv3d3d_t.txt
Normal file
@@ -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
|
||||
<very big structure - add when we add this structure>
|
||||
|
||||
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
|
||||
9
doc/nvidia_notes/old nouveau wiki.txt
Normal file
9
doc/nvidia_notes/old nouveau wiki.txt
Normal file
@@ -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
|
||||
|
||||
|
||||
|
||||
2238
doc/nvidia_notes/rivatv_riva128.txt
Normal file
2238
doc/nvidia_notes/rivatv_riva128.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
doc/nvidia_notes/status.xlsx
Normal file
BIN
doc/nvidia_notes/status.xlsx
Normal file
Binary file not shown.
1179
src/include/86box/nv/classes/vid_nv3_classes.h
Normal file
1179
src/include/86box/nv/classes/vid_nv3_classes.h
Normal file
File diff suppressed because it is too large
Load Diff
64
src/include/86box/nv/render/vid_nv3_render.h
Normal file
64
src/include/86box/nv/render/vid_nv3_render.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> 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);
|
||||
178
src/include/86box/nv/vid_nv.h
Normal file
178
src/include/86box/nv/vid_nv.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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 <mario64crashed@gmail.com>
|
||||
*
|
||||
* 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
|
||||
167
src/include/86box/nv/vid_nv1.h
Normal file
167
src/include/86box/nv/vid_nv1.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 <mario64crashed@gmail.com>
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
1764
src/include/86box/nv/vid_nv3.h
Normal file
1764
src/include/86box/nv/vid_nv3.h
Normal file
File diff suppressed because it is too large
Load Diff
149
src/include/86box/nv/vid_nv4.h
Normal file
149
src/include/86box/nv/vid_nv4.h
Normal file
@@ -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.
|
||||
*
|
||||
* Riva TNT hardware defines
|
||||
*
|
||||
* Authors: Connor Hyde <mario64crashed@gmail.com>
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#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);
|
||||
4291
src/include/86box/nv/vid_nv4_defines.h
Normal file
4291
src/include/86box/nv/vid_nv4_defines.h
Normal file
File diff suppressed because it is too large
Load Diff
23
src/include/86box/utils/video_stdlib.h
Normal file
23
src/include/86box/utils/video_stdlib.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 <mario64crashed@gmail.com> <nomorestarfrost@gmail.com>
|
||||
*
|
||||
* 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);
|
||||
@@ -630,6 +630,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;
|
||||
|
||||
@@ -171,7 +171,15 @@ 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_gpudebug_visualnv.cpp
|
||||
qt_gpudebug_visualnv.hpp
|
||||
qt_gpudebug_visualnv.ui
|
||||
|
||||
qt_harddrive_common.cpp
|
||||
qt_harddrive_common.hpp
|
||||
|
||||
177
src/qt/qt_gpudebug_visualnv.cpp
Normal file
177
src/qt/qt_gpudebug_visualnv.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 <cstdbool>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
/* Qt includes*/
|
||||
#include <QDebug>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QFormLayout>
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QFrame>
|
||||
#include <QLineEdit>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QLabel>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
#include <QFileDialog>
|
||||
#include <qt_gpudebug_visualnv.hpp>
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
46
src/qt/qt_gpudebug_visualnv.hpp
Normal file
46
src/qt/qt_gpudebug_visualnv.hpp
Normal file
@@ -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.
|
||||
*
|
||||
* GPU Debugging Tools - VRAM Viewer headers
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: starfrost
|
||||
*
|
||||
* Copyright 2025 starfrost
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
};
|
||||
213
src/qt/qt_gpudebug_visualnv.ui
Normal file
213
src/qt/qt_gpudebug_visualnv.ui
Normal file
@@ -0,0 +1,213 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VisualNVDialog</class>
|
||||
<widget class="QDialog" name="VisualNVDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>620</width>
|
||||
<height>350</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>620</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>620</width>
|
||||
<height>350</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>RIVA 128 Realtime Debugger</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="4" column="0">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>0</y>
|
||||
<width>201</width>
|
||||
<height>171</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>PFIFO State</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>220</x>
|
||||
<y>0</y>
|
||||
<width>361</width>
|
||||
<height>171</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>PGRAPH State</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>180</y>
|
||||
<width>571</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>VRAM Control</string>
|
||||
</property>
|
||||
<widget class="QPlainTextEdit" name="fbStartAddress">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>160</x>
|
||||
<y>30</y>
|
||||
<width>104</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>30</y>
|
||||
<width>151</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>VRAM Visual Aperture Start</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>390</x>
|
||||
<y>30</y>
|
||||
<width>91</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>FB Start Address:</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>480</x>
|
||||
<y>30</y>
|
||||
<width>81</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:700;">PLACEHOLDER</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="plainTextEdit_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>160</x>
|
||||
<y>60</y>
|
||||
<width>104</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>60</y>
|
||||
<width>151</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Pixel Depth</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnLoadSavestate">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>110</y>
|
||||
<width>231</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load nvplay savestate from real hardware</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="BPITCH">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>390</x>
|
||||
<y>60</y>
|
||||
<width>61</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>BPITCH[0]:</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="bPitch0Value">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>460</x>
|
||||
<y>60</y>
|
||||
<width>104</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="bPitch1Value">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>460</x>
|
||||
<y>90</y>
|
||||
<width>104</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="BPITCH_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>390</x>
|
||||
<y>90</y>
|
||||
<width>61</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>BPITCH[1]:</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
54
src/qt/qt_gpudebug_vram.cpp
Normal file
54
src/qt/qt_gpudebug_vram.cpp
Normal file
@@ -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 <cstdbool>
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
/* Qt includes*/
|
||||
#include <QDebug>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QFormLayout>
|
||||
#include <QSpinBox>
|
||||
#include <QCheckBox>
|
||||
#include <QFrame>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
#include <qt_gpudebug_vram.hpp>
|
||||
#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()
|
||||
{
|
||||
|
||||
}
|
||||
40
src/qt/qt_gpudebug_vram.hpp
Normal file
40
src/qt/qt_gpudebug_vram.hpp
Normal file
@@ -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 <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class GPUDebugVRAMDialog;
|
||||
}
|
||||
|
||||
class GPUDebugVRAMDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GPUDebugVRAMDialog(QWidget *parent = nullptr);
|
||||
~GPUDebugVRAMDialog();
|
||||
protected:
|
||||
private:
|
||||
Ui::GPUDebugVRAMDialog* ui;
|
||||
|
||||
|
||||
};
|
||||
42
src/qt/qt_gpudebug_vram.ui
Normal file
42
src/qt/qt_gpudebug_vram.ui
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GPUDebugVRAMDialog</class>
|
||||
<widget class="QDialog" name="GPUDebugVRAMDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>VRAM Viewer</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="5" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout_2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#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"
|
||||
@@ -106,6 +108,8 @@ 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"
|
||||
@@ -285,29 +289,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));
|
||||
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);
|
||||
onHardResetCompleted();
|
||||
});
|
||||
|
||||
connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::QueuedConnection);
|
||||
@@ -908,6 +890,50 @@ 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)
|
||||
{
|
||||
@@ -2525,3 +2551,29 @@ 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();
|
||||
}
|
||||
@@ -17,6 +17,11 @@
|
||||
|
||||
extern QTimer discordupdate;
|
||||
|
||||
// NON-modal dialogs
|
||||
#include "qt_gpudebug_vram.hpp"
|
||||
#include "qt_gpudebug_visualnv.hpp"
|
||||
|
||||
|
||||
class MediaMenu;
|
||||
class RendererStack;
|
||||
|
||||
@@ -74,6 +79,8 @@ signals:
|
||||
public slots:
|
||||
void showSettings();
|
||||
void hardReset();
|
||||
void onHardResetCompleted();
|
||||
|
||||
void togglePause();
|
||||
void initRendererMonitorSlot(int monitor_index);
|
||||
void destroyRendererMonitorSlot(int monitor_index);
|
||||
@@ -136,7 +143,9 @@ 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);
|
||||
@@ -175,6 +184,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<MachineStatus> status;
|
||||
std::shared_ptr<MediaMenu> mm;
|
||||
|
||||
|
||||
@@ -251,11 +251,19 @@
|
||||
<addaction name="actionAbout_86Box"/>
|
||||
<addaction name="actionAbout_Qt"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuDebug">
|
||||
<property name="title">
|
||||
<string>&Debugging Tools</string>
|
||||
</property>
|
||||
<addaction name="actionDebug_GPUDebug_VRAM"/>
|
||||
<addaction name="actionDebug_GPUDebug_VisualNv"/>
|
||||
</widget>
|
||||
<addaction name="menuAction"/>
|
||||
<addaction name="menuView"/>
|
||||
<addaction name="menuMedia"/>
|
||||
<addaction name="menuTools"/>
|
||||
<addaction name="menuAbout"/>
|
||||
<addaction name="menuDebug"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
@@ -919,7 +927,27 @@
|
||||
<action name="actionCGA_composite_settings">
|
||||
<property name="text">
|
||||
<string>&CGA composite settings...</string>
|
||||
</property>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDebug_GPUDebug_VRAM">
|
||||
<property name="text">
|
||||
<string>GPU Debug - VRAM Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDebug_GPUDebug_VisualNv">
|
||||
<property name="text">
|
||||
<string>RIVA 128 Realtime Debugger</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDebug_GPUDebug_VRAM">
|
||||
<property name="text">
|
||||
<string>GPU Debug - VRAM Viewer</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDebug_GPUDebug_VisualNv">
|
||||
<property name="text">
|
||||
<string>RIVA 128 Realtime Debugger</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Full_screen_stretch_gl">
|
||||
<property name="checkable">
|
||||
@@ -1049,6 +1077,54 @@
|
||||
<string>&8x</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Full_screen_stretch_gl">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Full screen stretch</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_4_3_gl">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&4:3</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Square_pixels_keep_ratio_gl">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Square pixels (Keep ratio)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Integer_scale_gl">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Integer scale</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action4_3_Integer_scale_gl">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>4:&3 Integer scale</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_1x">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&1x</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,7 @@ add_library(utils OBJECT
|
||||
ini.c
|
||||
log.c
|
||||
random.c
|
||||
|
||||
# VIDEO
|
||||
video/video_rop.c
|
||||
)
|
||||
|
||||
@@ -44,6 +44,7 @@ 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. */
|
||||
@@ -219,9 +220,18 @@ 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 (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 (log->log_cycles == 1) {
|
||||
/*
|
||||
@@ -265,10 +275,13 @@ 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, ...)
|
||||
{
|
||||
|
||||
790
src/utils/video/video_rop.c
Normal file
790
src/utils/video/video_rop.c
Normal file
@@ -0,0 +1,790 @@
|
||||
#include <stdint.h>
|
||||
#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;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
# Authors: David Hrdlička, <hrdlickadavid@outlook.com>
|
||||
#
|
||||
# Copyright 2020-2021 David Hrdlička.
|
||||
# Copyright 2025 starfrost
|
||||
# Copyright 2025 Connor Hyde / starfrost
|
||||
#
|
||||
|
||||
add_library(vid OBJECT
|
||||
@@ -136,12 +136,77 @@ add_library(vid OBJECT
|
||||
# Matrox
|
||||
vid_mga.c
|
||||
|
||||
# NVidia (pending)
|
||||
# NVidia - Core
|
||||
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
|
||||
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")
|
||||
|
||||
110
src/video/nv/nv1/nv1_core.c
Normal file
110
src/video/nv/nv1/nv1_core.c
Normal file
@@ -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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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_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,
|
||||
};
|
||||
133
src/video/nv/nv1/nv1_core_config.c
Normal file
133
src/video/nv/nv1/nv1_core_config.c
Normal file
@@ -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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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_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
|
||||
}
|
||||
};
|
||||
50
src/video/nv/nv3/classes/nv3_class_001_beta_factor.c
Normal file
50
src/video/nv/nv3/classes/nv3_class_001_beta_factor.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
44
src/video/nv/nv3/classes/nv3_class_002_rop.c
Normal file
44
src/video/nv/nv3/classes/nv3_class_002_rop.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
54
src/video/nv/nv3/classes/nv3_class_003_chroma_key.c
Normal file
54
src/video/nv/nv3/classes/nv3_class_003_chroma_key.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x02 (Chroma/color key)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
40
src/video/nv/nv3/classes/nv3_class_004_plane_mask.c
Normal file
40
src/video/nv/nv3/classes/nv3_class_004_plane_mask.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x02 (Plane mask)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
49
src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c
Normal file
49
src/video/nv/nv3/classes/nv3_class_005_clipping_rectangle.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
86
src/video/nv/nv3/classes/nv3_class_006_pattern.c
Normal file
86
src/video/nv/nv3/classes/nv3_class_006_pattern.c
Normal file
@@ -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: Methods for class 0x06 (Pattern)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
69
src/video/nv/nv3/classes/nv3_class_007_rectangle.c
Normal file
69
src/video/nv/nv3/classes/nv3_class_007_rectangle.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
40
src/video/nv/nv3/classes/nv3_class_008_point.c
Normal file
40
src/video/nv/nv3/classes/nv3_class_008_point.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x08 (Point)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
40
src/video/nv/nv3/classes/nv3_class_009_line.c
Normal file
40
src/video/nv/nv3/classes/nv3_class_009_line.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x09 (Line)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
src/video/nv/nv3/classes/nv3_class_00a_lin.c
Normal file
41
src/video/nv/nv3/classes/nv3_class_00a_lin.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
40
src/video/nv/nv3/classes/nv3_class_00b_triangle.c
Normal file
40
src/video/nv/nv3/classes/nv3_class_00b_triangle.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x0B (Basic triangle)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
285
src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c
Normal file
285
src/video/nv/nv3/classes/nv3_class_00c_win95_gdi_text.c
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
94
src/video/nv/nv3/classes/nv3_class_00d_m2mf.c
Normal file
94
src/video/nv/nv3/classes/nv3_class_00d_m2mf.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x0E (Get image from vram and scale it)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
60
src/video/nv/nv3/classes/nv3_class_010_blit.c
Normal file
60
src/video/nv/nv3/classes/nv3_class_010_blit.c
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
67
src/video/nv/nv3/classes/nv3_class_011_image.c
Normal file
67
src/video/nv/nv3/classes/nv3_class_011_image.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
src/video/nv/nv3/classes/nv3_class_012_bitmap.c
Normal file
41
src/video/nv/nv3/classes/nv3_class_012_bitmap.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
41
src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c
Normal file
41
src/video/nv/nv3/classes/nv3_class_014_transfer2memory.c
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x15 (stretched image from cpu to memory)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
* NV3: Methods for class 0x17 (Direct3D 5.0 accelerated triangle with zeta buffer)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
42
src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c
Normal file
42
src/video/nv/nv3/classes/nv3_class_018_point_zeta_buffer.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
99
src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c
Normal file
99
src/video/nv/nv3/classes/nv3_class_01c_image_in_memory.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
67
src/video/nv/nv3/classes/nv3_class_names.c
Normal file
67
src/video/nv/nv3/classes/nv3_class_names.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.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>
|
||||
|
||||
/* 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",
|
||||
};
|
||||
67
src/video/nv/nv3/classes/nv3_class_shared_methods.c
Normal file
67
src/video/nv/nv3/classes/nv3_class_shared_methods.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
}
|
||||
1567
src/video/nv/nv3/nv3_core.c
Normal file
1567
src/video/nv/nv3/nv3_core.c
Normal file
File diff suppressed because it is too large
Load Diff
213
src/video/nv/nv3/nv3_core_arbiter.c
Normal file
213
src/video/nv/nv3/nv3_core_arbiter.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
// STANDARD NV3 includes
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
// 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) {};
|
||||
240
src/video/nv/nv3/nv3_core_config.c
Normal file
240
src/video/nv/nv3/nv3_core_config.c
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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
|
||||
}
|
||||
};
|
||||
229
src/video/nv/nv3/render/nv3_render_blit.c
Normal file
229
src/video/nv/nv3/render/nv3_render_blit.c
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
|
||||
|
||||
}
|
||||
869
src/video/nv/nv3/render/nv3_render_core.c
Normal file
869
src/video/nv/nv3/render/nv3_render_core.c
Normal file
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
355
src/video/nv/nv3/render/nv3_render_primitives.c
Normal file
355
src/video/nv/nv3/render/nv3_render_primitives.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 Connor Hyde
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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/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;
|
||||
}
|
||||
255
src/video/nv/nv3/subsystems/nv3_pbus.c
Normal file
255
src/video/nv/nv3/subsystems/nv3_pbus.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email dataess ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
// 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
|
||||
}
|
||||
274
src/video/nv/nv3/subsystems/nv3_pbus_dma.c
Normal file
274
src/video/nv/nv3/subsystems/nv3_pbus_dma.c
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* NV3 PBUS DMA: DMA & Notifier Engine
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
161
src/video/nv/nv3/subsystems/nv3_pextdev.c
Normal file
161
src/video/nv/nv3/subsystems/nv3_pextdev.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3 PEXTDEV - External Devices
|
||||
* Including straps
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
204
src/video/nv/nv3/subsystems/nv3_pfb.c
Normal file
204
src/video/nv/nv3/subsystems/nv3_pfb.c
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
// 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");
|
||||
|
||||
}
|
||||
985
src/video/nv/nv3/subsystems/nv3_pfifo.c
Normal file
985
src/video/nv/nv3/subsystems/nv3_pfifo.c
Normal file
@@ -0,0 +1,985 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#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
|
||||
}
|
||||
632
src/video/nv/nv3/subsystems/nv3_pgraph.c
Normal file
632
src/video/nv/nv3/subsystems/nv3_pgraph.c
Normal file
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
274
src/video/nv/nv3/subsystems/nv3_pmc.c
Normal file
274
src/video/nv/nv3/subsystems/nv3_pmc.c
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* NV3 PMC - Master control for the chip
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
136
src/video/nv/nv3/subsystems/nv3_pme.c
Normal file
136
src/video/nv/nv3/subsystems/nv3_pme.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
458
src/video/nv/nv3/subsystems/nv3_pramdac.c
Normal file
458
src/video/nv/nv3/subsystems/nv3_pramdac.c
Normal file
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
// nv3_pramdac.c: NV3 RAMDAC
|
||||
// Todo: Allow overridability using 68050C register...
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
515
src/video/nv/nv3/subsystems/nv3_pramin.c
Normal file
515
src/video/nv/nv3/subsystems/nv3_pramin.c
Normal file
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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
|
||||
#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
|
||||
}
|
||||
40
src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c
Normal file
40
src/video/nv/nv3/subsystems/nv3_pramin_ramfc.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3 PFIFO RAMFC area: Stores context for unused DMA channels
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
56
src/video/nv/nv3/subsystems/nv3_pramin_ramht.c
Normal file
56
src/video/nv/nv3/subsystems/nv3_pramin_ramht.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
40
src/video/nv/nv3/subsystems/nv3_pramin_ramro.c
Normal file
40
src/video/nv/nv3/subsystems/nv3_pramin_ramro.c
Normal file
@@ -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.
|
||||
*
|
||||
* NV3 PFIFO ram runout area (you fucked up)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
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);
|
||||
}
|
||||
227
src/video/nv/nv3/subsystems/nv3_ptimer.c
Normal file
227
src/video/nv/nv3/subsystems/nv3_ptimer.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
159
src/video/nv/nv3/subsystems/nv3_pvideo.c
Normal file
159
src/video/nv/nv3/subsystems/nv3_pvideo.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
70
src/video/nv/nv3/subsystems/nv3_user.c
Normal file
70
src/video/nv/nv3/subsystems/nv3_user.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
388
src/video/nv/nv4/nv4_core.c
Normal file
388
src/video/nv/nv4/nv4_core.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* 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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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_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,
|
||||
};
|
||||
92
src/video/nv/nv4/nv4_core_config.c
Normal file
92
src/video/nv/nv4/nv4_core_config.c
Normal file
@@ -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.
|
||||
*
|
||||
* Provides NV4 configuration
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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_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
|
||||
}
|
||||
};
|
||||
1283
src/video/nv/nv4/nv4_core_io.c
Normal file
1283
src/video/nv/nv4/nv4_core_io.c
Normal file
File diff suppressed because it is too large
Load Diff
46
src/video/nv/nv4/nv4_debug_register_list.c
Normal file
46
src/video/nv/nv4/nv4_debug_register_list.c
Normal file
@@ -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, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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>
|
||||
#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
|
||||
84
src/video/nv/nv4/subsystems/nv4_pramdac.c
Normal file
84
src/video/nv/nv4/subsystems/nv4_pramdac.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* NV4/Riva TNT - RAMDAC
|
||||
*
|
||||
*
|
||||
* Authors: Connor Hyde, <mario64crashed@gmail.com> I need a better email address ;^)
|
||||
*
|
||||
* Copyright 2024-2025 starfrost
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.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_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)
|
||||
{
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user