start working on visual gpu debugging window; rename nv3_position_32 to nv3_coord_32; first pass at hardware cursor

This commit is contained in:
starfrost013
2025-04-27 17:04:24 +01:00
parent bd1829f02e
commit 2007e3d0e5
13 changed files with 294 additions and 47 deletions

View 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

View File

@@ -306,11 +306,11 @@ typedef struct nv3_clip_16_s
} nv3_clip_16_t;
/* In case your positions weren't HIGH PRECISION enough */
typedef struct nv3_position_32_s
typedef struct nv3_coord_32_s
{
uint32_t x;
uint32_t y;
} nv3_position_32_t;
} nv3_coord_32_t;
// COLOUR FORMATS
@@ -459,7 +459,7 @@ typedef struct nv3_object_class_008
uint32_t set_notify; // Set notifier
nv3_color_expanded_t color; // argb?
nv3_coord_16_t point[16]; // Boring points
nv3_position_32_t point32[16]; // Allows you to have points with full 32-bit precision
nv3_coord_32_t point32[16]; // Allows you to have points with full 32-bit precision
nv3_object_class_008_cpoint_t cpoint[16]; // Allows you to have c o l o r f u l points!
} nv3_point_t;
@@ -549,7 +549,7 @@ typedef struct nv3_object_class_00B
uint32_t x2;
nv3_coord_16_t mesh[32]; // Some kind of mesh format. I guess a list of vertex positions?
nv3_position_32_t mesh32[16];
nv3_coord_32_t mesh32[16];
nv3_color_and_coord_16_t ctriangle[3]; // Triangle with colour
nv3_color_and_coord_16_t ctrimesh[16]; // Some kind of mesh format. I guess a list of vertex positions? with colours
} nv3_triangle_t;

View File

@@ -31,7 +31,7 @@ uint32_t nv3_render_read_pixel_32(nv3_coord_16_t position, nv3_grobj_t grobj);
/* Address */
uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj);
uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer);
uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer);
/* Colour Conversion */
uint32_t nv3_render_get_palette_index(uint8_t index); // Get a colour for a palette index. (The colours are 24 bit RGB888 with a 0xFF alpha added for some purposes.)

View File

@@ -697,6 +697,11 @@ extern const device_config_t nv3t_config[]; // Confi
#define NV3_PVIDEO_END 0x6802FF
#define NV3_PRAMDAC_START 0x680300
#define NV3_PRAMDAC_CURSOR_START 0x680300
#define NV3_PRAMDAC_CURSOR_SIZE_X 32
#define NV3_PRAMDAC_CURSOR_SIZE_Y 32
#define NV3_PRAMDAC_CLOCK_MEMORY 0x680504
#define NV3_PRAMDAC_CLOCK_MEMORY_VDIV 7:0
#define NV3_PRAMDAC_CLOCK_MEMORY_NDIV 15:8
@@ -759,30 +764,30 @@ extern const device_config_t nv3t_config[]; // Confi
// control structures for dma'd in graphics objects from pfifo
// these all have configurable sizes, define them here
#define NV3_RAMIN_START 0x1C00000
#define NV3_RAMIN_START 0x1C00000
#define NV3_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects
#define NV3_RAMIN_RAMHT_END 0x1C00FFF
#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF
#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF
#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF
#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF
#define NV3_RAMIN_RAMHT_START 0x1C00000 // Hashtable for storing submitted objects
#define NV3_RAMIN_RAMHT_END 0x1C00FFF
#define NV3_RAMIN_RAMHT_SIZE_0 0xFFF
#define NV3_RAMIN_RAMHT_SIZE_1 0x1FFF
#define NV3_RAMIN_RAMHT_SIZE_2 0x3FFF
#define NV3_RAMIN_RAMHT_SIZE_3 0x7FFF
/* OBSOLETE AREA for AUDIO probably. DO NOT USE! */
#define NV3_RAMIN_RAMAU_START 0x1C01000
#define NV3_RAMIN_RAMAU_END 0x1C01BFF
#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels
#define NV3_RAMIN_RAMFC_END 0x1C01DFF
#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF
#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF
#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions
#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF
#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF
#define NV3_RAMIN_RAMRO_END 0x1C01FFF
#define NV3_RAMIN_RAMRM_START 0x1C02000
#define NV3_RAMIN_RAMRM_END 0x1C02FFF
#define NV3_RAMIN_RAMAU_START 0x1C01000
#define NV3_RAMIN_RAMAU_END 0x1C01BFF
#define NV3_RAMIN_RAMFC_START 0x1C01C00 // context for unused PFIFO DMA channels
#define NV3_RAMIN_RAMFC_END 0x1C01DFF
#define NV3_RAMIN_RAMFC_SIZE_0 0x1FF
#define NV3_RAMIN_RAMFC_SIZE_1 0xFFF
#define NV3_RAMIN_RAMRO_START 0x1C01E00 // Runout area for invalid submissions
#define NV3_RAMIN_RAMRO_SIZE_0 0x1FF
#define NV3_RAMIN_RAMRO_SIZE_1 0x1FFF
#define NV3_RAMIN_RAMRO_END 0x1C01FFF
#define NV3_RAMIN_RAMRM_START 0x1C02000
#define NV3_RAMIN_RAMRM_END 0x1C02FFF
#define NV3_RAMIN_END 0x1FFFFFF
#define NV3_RAMIN_END 0x1FFFFFF
// not done
@@ -814,6 +819,7 @@ extern const device_config_t nv3t_config[]; // Confi
#define NV3_CRTC_REGISTER_PRESETROWSCAN 0x08
#define NV3_CRTC_REGISTER_MAXSCAN 0x09
#define NV3_CRTC_REGISTER_CURSOR_START 0x0A
#define NV3_CRTC_REGISTER_CURSOR_START_DISABLED 5
#define NV3_CRTC_REGISTER_CURSOR_END 0x0B
#define NV3_CRTC_REGISTER_STARTADDR_HIGH 0x0C
#define NV3_CRTC_REGISTER_STARTADDR_LOW 0x0D
@@ -846,6 +852,9 @@ extern const device_config_t nv3t_config[]; // Confi
#define NV3_CRTC_REGISTER_HEB 0x2D // HRS most significant bit
#define NV3_CRTC_REGISTER_CURSOR_ADDR0 0x30 // Cursor high
#define NV3_CRTC_REGISTER_CURSOR_ADDR1 0x31 // Cursor low (1:0 = enable)
#define NV3_CRTC_REGISTER_PIXELMODE_VGA 0x00 // vga textmode
#define NV3_CRTC_REGISTER_PIXELMODE_8BPP 0x01
#define NV3_CRTC_REGISTER_PIXELMODE_16BPP 0x02
@@ -1084,7 +1093,10 @@ typedef struct nv3_pramdac_s
uint8_t user_pixel_mask; // pixel mask for DAC lookup
uint32_t user_read_mode_address; // user read mode address
uint32_t user_write_mode_address; // user write mode address
uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entriesxr,g,b = 768 bytes
uint8_t palette[NV3_USER_DAC_PALETTE_SIZE]; // Palette Info/CLUT - 256 entries, 1 byte for r,g,b = 768 bytes
uint32_t cursor_address; // cursor address start
nv3_coord_16_t cursor_start;
} nv3_pramdac_t;
/* Holds DMA channel context information */
@@ -1473,6 +1485,7 @@ extern nv3_t* nv3;
void* nv3_init(const device_t *info);
void nv3_close(void* priv);
void nv3_speed_changed(void *priv);
void nv3_draw_cursor(svga_t* svga, int32_t drawline);
void nv3_recalc_timings(svga_t* svga);
void nv3_force_redraw(void* priv);

View File

@@ -151,7 +151,11 @@ add_library(ui STATIC
qt_newfloppydialog.ui
qt_harddiskdialog.cpp
qt_harddiskdialog.hpp
qt_harddiskdialog.ui
qt_harddiskdialog.ui
qt_gpudebug_vram.cpp
qt_gpudebug_vram.hpp
qt_gpudebug_vram.ui
qt_harddrive_common.cpp
qt_harddrive_common.hpp

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,18 @@
#pragma once
#include <QDialog>
namespace Ui
{
class GPUDebugVRAMDialog;
}
class Ui::GPUDebugVRAMDialog : public QDialog
{
public:
void Init();
protected:
private:
};

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GPUDebugVRAMDialog</class>
<widget class="QDialog" name="HarddiskDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>269</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>421</width>
<height>269</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>421</width>
<height>269</height>
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="9" column="0" colspan="6">
<widget class="QProgressBar" name="progressBar">
<property name="visible">
<bool>false</bool>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Sectors:</string>
</property>
</widget>
</item>
</ui>

View File

@@ -222,11 +222,19 @@
<addaction name="actionAbout_86Box"/>
<addaction name="actionAbout_Qt"/>
</widget>
<widget class="QMenu" name="menuDebug">
<property name="title">
<string>&amp;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">
@@ -899,6 +907,16 @@
<string>Pen</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>GPU Debug - NV3 Visual Debugger</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -725,18 +725,18 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv)
switch (crtcreg)
{
case NV3_CRTC_REGISTER_READ_BANK:
nv3->nvbase.cio_read_bank = val;
if (nv3->nvbase.svga.chain4) // chain4 addressing (planar?)
nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 15;
else
nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 13; // extended bank numbers
nv3->nvbase.cio_read_bank = val;
if (nv3->nvbase.svga.chain4) // chain4 addressing (planar?)
nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 15;
else
nv3->nvbase.svga.read_bank = nv3->nvbase.cio_read_bank << 13; // extended bank numbers
break;
case NV3_CRTC_REGISTER_WRITE_BANK:
nv3->nvbase.cio_write_bank = val;
if (nv3->nvbase.svga.chain4)
nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 15;
else
nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 13;
if (nv3->nvbase.svga.chain4)
nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 15;
else
nv3->nvbase.svga.write_bank = nv3->nvbase.cio_write_bank << 13;
break;
case NV3_CRTC_REGISTER_RMA:
nv3->pbus.rma.mode = val & NV3_CRTC_REGISTER_RMA_MODE_MAX;
@@ -769,6 +769,15 @@ void nv3_svga_write(uint16_t addr, uint8_t val, void* priv)
i2c_gpio_set(nv3->nvbase.i2c, scl, sda);
break;
}
/* [6:0] contains cursorAddr [23:17] */
case NV3_CRTC_REGISTER_CURSOR_ADDR0:
nv3->pramdac.cursor_address |= val << 17; //bit7 technically ignored, but nv don't care, so neither do we
break;
/* [7:2] contains cursorAddr [16:11] */
case NV3_CRTC_REGISTER_CURSOR_ADDR1:
nv3->pramdac.cursor_address |= (val >> 2) << 13; // bit0 and 1 aren't part of the address
break;
}
@@ -852,11 +861,109 @@ void nv3_draw_cursor(svga_t* svga, int32_t drawline)
// sanity check
if (!nv3)
return;
// if cursor disabled is set, return
if ((nv3->nvbase.svga.crtc[NV3_CRTC_REGISTER_CURSOR_START] >> NV3_CRTC_REGISTER_CURSOR_START_DISABLED) & 0x01)
return;
// On windows, this shows up using NV_IMAGE_IN_MEMORY.
// Do we need to emulate it?
uint32_t vram_cursor_base = nv3->pramdac.cursor_address;
/* let's just assume buffer 0 here...that code needs to be totally rewritten*/
nv3_coord_16_t start_position = nv3->pramdac.cursor_start;
nv_log("nv3_draw_cursor drawline=0x%04x", drawline);
/* refuse to draw if thge cursor is offscreen */
if (start_position.x >= nv3->nvbase.svga.hdisp
|| start_position.y >= nv3->nvbase.svga.dispend)
{
return;
}
nv_log("nv3_draw_cursor start=0x%04x,0x%04x", start_position.x, start_position.y);
uint32_t final_position = nv3_render_get_vram_address_for_buffer(start_position, 0);
uint16_t* vram_16 = (uint16_t*)nv3->nvbase.svga.vram;
uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram;
/*
We have to get a 32x32, "A"1R5G5B5-format cursor
out of video memory. The alpha bit actually means - XOR with display pixel if 0, replace if 1
Technically these are expanded to RGB10, but I don't see why this needs to happen. And our pipeline isn't set up for it anyway.
*/
for (int32_t y = 0; y < NV3_PRAMDAC_CURSOR_SIZE_Y; y++)
{
for (int32_t x = 0; x < NV3_PRAMDAC_CURSOR_SIZE_X; x++)
{
uint16_t current_pixel = vram_16[vram_cursor_base << 1];
bool replace_bit = (current_pixel & 0x8000);
switch (nv3->nvbase.svga.bpp)
{
/* this is indexed colour but... lol */
case 8:
if (replace_bit)
{
uint8_t final = current_pixel ^ nv3->nvbase.svga.vram[final_position];
nv3->nvbase.svga.vram[final_position] = final;
}
else // just override
nv3->nvbase.svga.vram[final_position] = current_pixel;
case 15 ... 16: // easy case (our cursor is 15bpp format)
uint32_t index_16 = final_position >> 1;
if (replace_bit)
{
uint16_t final = current_pixel ^ vram_16[index_16];
vram_16[index_16] = final;
}
else // just override
vram_16[index_16] = current_pixel;
case 32:
uint32_t index_32 = final_position >> 2;
if (replace_bit)
{
uint16_t final = current_pixel ^ vram_32[index_32];
vram_32[index_32] = final;
}
else // just override
vram_32[index_32] = nv3->nvbase.svga.conv_16to32(&nv3->nvbase.svga, current_pixel, 15); // 565_MODE doesn't seem to matter here
break;
}
// increment vram position
vram_cursor_base += 2;
// go
switch (nv3->nvbase.svga.bpp)
{
case 8:
final_position++;
case 15 ... 16:
final_position += 2;
break;
case 32:
final_position += 4;
break;
}
start_position.x++;
}
start_position.y++;
start_position.x = nv3->pramdac.cursor_start.x;
// reset at the end of each line so we "jump" to the start x
final_position = nv3_render_get_vram_address_for_buffer(start_position, 0);
}
nv3_coord_16_t size = {0};
size.x = size.y = 32;
nv3_grobj_t dummy = {0}; // need to clean it up
/* do we need to update here? */
nv3_render_current_bpp(&nv3->nvbase.svga, start_position, size, dummy, false, false);
}
// MMIO 0x110000->0x111FFF is mapped to a mirror of the VBIOS.

View File

@@ -161,7 +161,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj)
{
buf_position = (nv3->pgraph.blit.size.x * y);
/* shouldn't matter in non-wtf mode */
vram_position = nv3_render_get_vram_address_for_buffer(old_position, grobj, src_buffer);
vram_position = nv3_render_get_vram_address_for_buffer(old_position, src_buffer);
memcpy(&nv3_s2sb_line_buffer[buf_position], &nv3->nvbase.svga.vram[vram_position], size_x);
old_position.y++;
@@ -172,7 +172,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj)
for (int32_t y = 0; y < nv3->pgraph.blit.size.y; y++)
{
buf_position = (nv3->pgraph.blit.size.x * y);
vram_position = nv3_render_get_vram_address_for_buffer(new_position, grobj, dst_buffer);
vram_position = nv3_render_get_vram_address_for_buffer(new_position, dst_buffer);
memcpy(&nv3->nvbase.svga.vram[vram_position], &nv3_s2sb_line_buffer[buf_position], size_x);
new_position.y++;

View File

@@ -260,7 +260,7 @@ uint32_t nv3_render_get_vram_address(nv3_coord_16_t position, nv3_grobj_t grobj)
/* Combine the current buffer with the pitch to get the address in the video ram for a specific position relative to a specific framebuffer */
uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, nv3_grobj_t grobj, uint32_t buffer)
uint32_t nv3_render_get_vram_address_for_buffer(nv3_coord_16_t position, uint32_t buffer)
{
uint32_t vram_x = position.x;
uint32_t vram_y = position.y;
@@ -663,7 +663,7 @@ void nv3_render_8bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj,
{
/* re-set the vram address because we are basically "jumping" halfway across a line here */
if (use_destination_buffer)
vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now
vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now
else
vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask;
@@ -704,7 +704,7 @@ void nv3_render_15bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj
{
/* re-set the vram address because we are basically "jumping" halfway across a line here */
if (use_destination_buffer)
vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now
vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now
else
vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask;
@@ -745,7 +745,7 @@ void nv3_render_16bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj
{
/* re-set the vram address because we are basically "jumping" halfway across a line here */
if (use_destination_buffer)
vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now
vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now
else
vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask;
@@ -786,7 +786,7 @@ void nv3_render_32bpp(nv3_coord_16_t pos, nv3_coord_16_t size, nv3_grobj_t grobj
{
/* re-set the vram address because we are basically "jumping" halfway across a line here */
if (use_destination_buffer)
vram_base = nv3_render_get_vram_address_for_buffer(pos, grobj, 0); // hardcode to zero for now
vram_base = nv3_render_get_vram_address_for_buffer(pos, 0); // hardcode to zero for now
else
vram_base = nv3_render_get_vram_address(pos, grobj) & nv3->nvbase.svga.vram_display_mask;

View File

@@ -182,7 +182,7 @@ void nv3_pramdac_set_pixel_clock(void)
nv3->nvbase.svga.clock = cpuclock / frequency;
double time = 1000000.0 / (double)frequency; // needs to be a double for 86box
double time = 1000000.0 / (double)frequency; // needs to be a double for 86box
nv_log("Pixel clock = %.2f MHz\n", frequency / 1000000.0f);
@@ -205,6 +205,7 @@ void nv3_pramdac_set_pixel_clock(void)
// NULL means handle in read functions
nv_register_t pramdac_registers[] =
{
{ NV3_PRAMDAC_CURSOR_START, "PRAMDAC - Cursor Start Position"},
{ NV3_PRAMDAC_CLOCK_PIXEL, "PRAMDAC - NV3 GPU Core - Pixel clock", nv3_pramdac_get_pixel_clock_register, nv3_pramdac_set_pixel_clock_register },
{ NV3_PRAMDAC_CLOCK_MEMORY, "PRAMDAC - NV3 GPU Core - Memory clock", nv3_pramdac_get_vram_clock_register, nv3_pramdac_set_vram_clock_register },
{ NV3_PRAMDAC_COEFF_SELECT, "PRAMDAC - PLL Clock Coefficient Select", NULL, NULL},
@@ -320,6 +321,9 @@ uint32_t nv3_pramdac_read(uint32_t address)
ret = nv3->pramdac.palette[nv3->pramdac.user_read_mode_address];
nv3->pramdac.user_read_mode_address++;
break;
case NV3_PRAMDAC_CURSOR_START:
ret = (nv3->pramdac.cursor_start.y << 16) | nv3->pramdac.cursor_start.x;
break;
}
}
@@ -345,8 +349,6 @@ void nv3_pramdac_write(uint32_t address, uint32_t value)
// if the register actually exists
if (reg)
{
// on-read function
if (reg->on_write)
reg->on_write(value);
@@ -434,6 +436,13 @@ void nv3_pramdac_write(uint32_t address, uint32_t value)
nv3->pramdac.user_write_mode_address++;
break;
/* cursor start location */
case NV3_PRAMDAC_CURSOR_START:
// only 12 bits are used here instead of 16 for some stupid reason
nv3->pramdac.cursor_start.y = (value >> 16) & 0xFFF;
nv3->pramdac.cursor_start.x = (value) & 0xFFF;
nv3_draw_cursor(&nv3->nvbase.svga, 0);//drawline doesn't matter here
break;
}
}