First attempt at M2MF. It sucks

This commit is contained in:
starfrost013
2025-08-12 23:30:27 +01:00
parent e7f63f6df2
commit 12a56203fa
6 changed files with 141 additions and 72 deletions

View File

@@ -124,12 +124,8 @@ typedef enum nv3_pgraph_class_e
#define NV3_M2MF_FORMAT 0x0324
// M2MF formats (IN and OUT ORed together)
#define NV3_M2MF_FORMAT_INPUT_INC_1 0x1
#define NV3_M2MF_FORMAT_INPUT_INC_2 0x2
#define NV3_M2MF_FORMAT_INPUT_INC_4 0x4
#define NV3_M2MF_FORMAT_OUTPUT_INC_1 0x100
#define NV3_M2MF_FORMAT_OUTPUT_INC_2 0x200
#define NV3_M2MF_FORMAT_OUTPUT_INC_4 0x400
#define NV3_M2MF_FORMAT_INPUT 0
#define NV3_M2MF_FORMAT_OUTPUT 8
#define NV3_M2MF_NOTIFY 0x0328
@@ -596,8 +592,8 @@ typedef struct nv3_object_class_00D
uint32_t offset_out;
uint32_t pitch_in;
uint32_t pitch_out;
uint32_t line_length_in; // Stride?
uint32_t line_count;
uint32_t scanline_length; // Stride?
uint32_t num_scanlines;
uint8_t format; // input increment 1 2 or 4, output increment 1 2 or 4 (represented by << 8)
uint32_t buffer_notify; // Notify the Buffedr
} nv3_memory_to_memory_format_t;

View File

@@ -58,4 +58,7 @@ void nv3_render_blit_screen2screen(nv3_grobj_t grobj);
/* GDI */
void nv3_render_gdi_transparent_bitmap(bool clip, uint32_t color, uint32_t bitmap_data, nv3_grobj_t grobj);
void nv3_render_gdi_1bpp_bitmap(uint32_t color0, uint32_t color1, uint32_t bitmap_data, nv3_grobj_t grobj); /* GDI Type-E: Clipped 1bpp colour-expanded bitmap */
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);

View File

@@ -1054,10 +1054,10 @@ typedef struct nv3_pfb_s
#define NV3_NOTIFICATION_INFO_ADJUST 0 // Wut
#define NV3_NOTIFICATION_PT_PRESENT 16 // Determines if the pagetable exists.
#define NV3_NOTIFICATION_TARGET 24 // Determines where this notification goes
#define NV3_NOTIFICATION_TARGET_NVM 0 // VRAM target for DMA
#define NV3_NOTIFICATION_TARGET_CART 1 // "Cartridge" target for dma, only mentioned in a few places, !!! NV2 LEFTOVER !!!
#define NV3_NOTIFICATION_TARGET_PCI 2 // Send the data to the host system over PCI
#define NV3_NOTIFICATION_TARGET_AGP 3 // Send the data to the host system over AGP
#define NV3_DMA_TARGET_NODE_VRAM 0 // VRAM target for DMA
#define NV3_DMA_TARGET_NODE_CART 1 // "Cartridge" target for dma, only mentioned in a few places, !!! NV2 LEFTOVER !!!
#define NV3_DMA_TARGET_NODE_PCI 2 // Send the data to the host system over PCI
#define NV3_DMA_TARGET_NODE_AGP 3 // Send the data to the host system over AGP
#define NV3_NOTIFICATION_PAGE_IS_PRESENT 0 // Determines if the page really exists
#define NV3_NOTIFICATION_PAGE_ACCESS 1 // Determines the page access type

View File

@@ -49,16 +49,21 @@ void nv3_class_00d_method(uint32_t param, uint32_t method_id, nv3_ramin_context_
nv_log("Method Execution: M2MF Pitch Out = 0x%08x", param);
break;
case NV3_M2MF_SCANLINE_LENGTH_IN_BYTES:
nv3->pgraph.m2mf.line_length_in = param;
nv3->pgraph.m2mf.scanline_length = param;
nv_log("Method Execution: M2MF Scanline Length in Bytes = 0x%08x", param);
break;
case NV3_M2MF_NUM_SCANLINES:
nv3->pgraph.m2mf.line_count = param;
nv3->pgraph.m2mf.num_scanlines = param;
nv_log("Method Execution: M2MF Num Scanlines = 0x%08x", param);
break;
case NV3_M2MF_FORMAT:
nv3->pgraph.m2mf.format = param;
nv_log("Method Execution: M2MF Format = 0x%08x", param);
// Format Done - start m2mf
nv3_perform_dma_m2mf(grobj);
break;
case NV3_M2MF_NOTIFY:
/* This is technically its own thing, but I don't know if it's ever a problem with how we've designed it */

View File

@@ -803,7 +803,7 @@ uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop)
uint32_t patch_config_rop = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F;
/* || patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0*/
// TODO: Blending
if (patch_config_rop == NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS) // 0x00 is used for "nothing here" it seems.
return VIDEO_ROP_SRC_COPY;
@@ -864,28 +864,6 @@ uint8_t nv3_render_translate_nvrop(nv3_grobj_t grobj, uint32_t rop)
res |= 1 << i;
}
}
return res;
/*
uint32_t patch_config = (grobj.grobj_0 >> NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG) & 0x1F;
// Wtf do these even do?
switch (patch_config)
{
// don't do anything
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_SRC_BYPASS:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD0:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD1:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_RSVD2:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD0:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD1:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD2:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD3:
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_BLEND_RSVD4:
return src;
case NV3_PGRAPH_CTX_SWITCH_PATCH_CONFIG_PAT_SRC_DST: // S2SB
break;
}
*/
}

View File

@@ -21,6 +21,7 @@
#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!!!
@@ -31,40 +32,126 @@
/* Nvidia DMA Engine */
void nv3_perform_dma_m2mf(nv3_grobj_t grobj)
{
switch (method_id)
{
// notify object base=grobj_1 >> 12
uint32_t notify_obj_base = grobj.grobj_1 >> 12;
uint32_t notify_obj_info = nv3_ramin_read32(notify_obj_base, nv3);
uint32_t notify_obj_limit = nv3_ramin_read32(notify_obj_base + 0x04, nv3);
uint32_t notify_obj_page = nv3_ramin_read32(notify_obj_base + 0x08, nv3);
/* extract some important information*/
uint32_t info_adjust = notify_obj_info & 0xFFF;
bool info_pt_present = (notify_obj_info >> NV3_NOTIFICATION_PT_PRESENT) & 0x01;
uint8_t info_dma_target = (notify_obj_info >> NV3_NOTIFICATION_TARGET) & 0x03;
/* paging information */
bool page_is_present = notify_obj_page & 0x01;
bool page_is_readwrite = (notify_obj_page >> NV3_NOTIFICATION_PAGE_ACCESS);
uint32_t frame_base = notify_obj_page & 0xFFFFF000;
// This code is temporary and will probably be moved somewhere else
// Print torns of debug info
#ifdef DEBUG
nv_log_verbose_only("******* WARNING: IF THIS OPERATION FUCKS UP, RANDOM MEMORY WILL BE CORRUPTED, YOUR ENTIRE SYSTEM MAY BE HOSED *******\n");
nv_log_verbose_only("M2MF DMA Information:\n");
nv_log_verbose_only("Adjust Value: 0x%08x\n", info_adjust);
(info_pt_present) ? nv_log_verbose_only("Pagetable Present: True\n") : nv_log_verbose_only("Pagetable Present: False\n");
switch (info_dma_target)
{
/* mthdCreate in software(?)*/
case NV3_ROOT_HI_IM_OBJECT_MCOBJECTYFACE:
//nv_log("mthdCreate obj_name=0x%08x\n", param);
nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING);
case NV3_DMA_TARGET_NODE_VRAM:
nv_log_verbose_only("Notification Target: VRAM\n");
break;
// set up the current notification request/object
// and check for double notifiers.
case NV3_SET_NOTIFY:
if (nv3->pgraph.notify_pending)
case NV3_DMA_TARGET_NODE_CART:
nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n");
break;
case NV3_DMA_TARGET_NODE_PCI:
(nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n");
break;
case NV3_DMA_TARGET_NODE_AGP:
(nv3->nvbase.bus_generation == nv_bus_agp_1x
|| nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n");
break;
}
nv_log_verbose_only("Limit: 0x%08x\n", notify_obj_limit);
(page_is_present) ? nv_log_verbose_only("Page is present\n") : nv_log_verbose_only("Page is not present\n");
(page_is_readwrite) ? nv_log_verbose_only("Page is read-write\n") : nv_log_verbose_only("Page is read-only\n");
nv_log_verbose_only("Pageframe Address: 0x%08x\n", frame_base);
#endif
// set up the dma transfer. we need to translate to a physical address.
uint32_t final_address = 0;
/* M2MF DMA only uses HW type */
final_address = frame_base + info_adjust;
/* send the notification off */
nv_log("About to send M2MF DMA to 0x%08x (Check target)\n", final_address);
uint32_t offset_in = (nv3->pgraph.m2mf.offset_in);
uint32_t offset_out = (nv3->pgraph.m2mf.offset_out);
uint32_t pitch_in = nv3->pgraph.m2mf.pitch_in;
uint32_t pitch_out = nv3->pgraph.m2mf.pitch_out;
// pitch out surely can't be 0
if (pitch_out == 0)
pitch_out = pitch_in;
uint32_t bytes_per_scanline = nv3->pgraph.m2mf.scanline_length;
uint8_t increment_in = (nv3->pgraph.m2mf.format) & 0x07;
uint8_t increment_out = (nv3->pgraph.m2mf.format >> NV3_M2MF_FORMAT_INPUT) & 0x07;
for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++)
{
for (uint32_t pixel = offset_in; pixel < (offset_in + bytes_per_scanline); pixel += increment_in)
{
nv3->nvbase.svga.vram[offset_out] = nv3->nvbase.svga.vram[offset_in];
offset_out += increment_out;
}
offset_in += pitch_in;
offset_out += pitch_out;
}
/*
switch (info_dma_target)
{
// for M2MF only NVM target node is used.
case NV3_DMA_TARGET_NODE_VRAM:
uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram;
break;
case NV3_DMA_TARGET_NODE_PCI:
case NV3_DMA_TARGET_NODE_AGP:
// Idk how to implement increments of more than 1 but only 1 increments seem to be used with these.
uint32_t size_in = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_in;
uint32_t size_out = nv3->pgraph.m2mf.num_scanlines * nv3->pgraph.m2mf.pitch_out;
uint8_t* page_in = calloc(1, size_in);
for (uint32_t scanline = 0; scanline < nv3->pgraph.m2mf.num_scanlines; scanline++)
{
nv_log("Executed method NV3_SET_NOTIFY with nv3->pgraph.notify_pending already set. param=0x%08x, method=0x%04x, grobj=0x%08x 0x%08x 0x%08x 0x%08x\n");
nv_log("IF THIS IS A DEBUG BUILD, YOU SHOULD SEE A CONTEXT BELOW");
nv3_debug_ramin_print_context_info(param, context);
nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY);
// disable
nv3->pgraph.notify_pending = false;
nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_DOUBLE_NOTIFY);
/* may need to disable fifo in this state */
return;
}
// set a notify as pending.
nv3->pgraph.notifier = param;
nv3->pgraph.notify_pending = true;
dma_bm_read(offset_in, page_in, size_in, size_in);
dma_bm_write(offset_out, page_in, size_out, size_out);
break;
default:
nv_log("Shared Generic Methods: Invalid or Unimplemented method 0x%04x", method_id);
nv3_pgraph_interrupt_invalid(NV3_PGRAPH_INTR_1_SOFTWARE_METHOD_PENDING);
return;
}
*/
// we're done
nv3->pgraph.notify_pending = false;
}
@@ -121,16 +208,16 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t
switch (info_notification_target)
{
case NV3_NOTIFICATION_TARGET_NVM:
case NV3_DMA_TARGET_NODE_VRAM:
nv_log_verbose_only("Notification Target: VRAM\n");
break;
case NV3_NOTIFICATION_TARGET_CART:
case NV3_DMA_TARGET_NODE_CART:
nv_log_verbose_only("VERY BAD WARNING: Notification detected with Notification Target: Cartridge. THIS SHOULD NEVER HAPPEN!!!!!\n");
break;
case NV3_NOTIFICATION_TARGET_PCI:
case NV3_DMA_TARGET_NODE_PCI:
(nv3->nvbase.bus_generation == nv_bus_pci) ? nv_log_verbose_only("Notification Target: PCI Bus\n") : nv_log_verbose_only("Notification Target: PCI Bus (On AGP card?)\n");
break;
case NV3_NOTIFICATION_TARGET_AGP:
case NV3_DMA_TARGET_NODE_AGP:
(nv3->nvbase.bus_generation == nv_bus_agp_1x
|| nv3->nvbase.bus_generation == nv_bus_agp_2x) ? nv_log_verbose_only("Notification Target: AGP Bus\n") : nv_log_verbose_only("Notification Target: AGP Bus (On PCI card?)\n");
break;
@@ -166,7 +253,7 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t
switch (info_notification_target)
{
case NV3_NOTIFICATION_TARGET_NVM:
case NV3_DMA_TARGET_NODE_VRAM:
uint32_t* vram_32 = (uint32_t*)nv3->nvbase.svga.vram;
@@ -176,8 +263,8 @@ void nv3_notify_if_needed(uint32_t name, uint32_t method_id, nv3_ramin_context_t
vram_32[final_address + 2] = notify.info32;
vram_32[final_address + 3] = (notify.info16 | notify.status);
break;
case NV3_NOTIFICATION_TARGET_PCI:
case NV3_NOTIFICATION_TARGET_AGP:
case NV3_DMA_TARGET_NODE_PCI:
case NV3_DMA_TARGET_NODE_AGP:
dma_bm_write(final_address, (uint8_t*)&notify, sizeof(nv3_notification_t), 4);
break;
}