diff --git a/src/devices/video/EmuNV2A.cpp b/src/devices/video/EmuNV2A.cpp index 40631c8a2..e017d4ff3 100644 --- a/src/devices/video/EmuNV2A.cpp +++ b/src/devices/video/EmuNV2A.cpp @@ -52,7 +52,6 @@ namespace xboxkrnl #include // For PKINTERRUPT, etc. }; - #ifdef _MSC_VER // Check if MS Visual C compiler # pragma comment(lib, "opengl32.lib") // Compiler-specific directive to avoid manually configuration //# pragma comment(lib, "glu32.lib") // Link libraries @@ -61,10 +60,6 @@ namespace xboxkrnl #include // For uint32_t #include // For std::string -#include -#include -#include -#include #include "CxbxKrnl\CxbxKrnl.h" #include "CxbxKrnl\Emu.h" @@ -73,481 +68,56 @@ namespace xboxkrnl #include "CxbxKrnl\HLEIntercept.h" #include "EmuNV2A.h" +#include "nv2a.h" // For NV2AState #include "nv2a_int.h" // from https://github.com/espes/xqemu/tree/xbox/hw/xbox -#include +//#include #include #include #include //#include -// Public Domain ffs Implementation -// See: http://snipplr.com/view/22147/stringsh-implementation/ -int ffs(int v) -{ - unsigned int x = v; - int c = 1; - - /* - * adapted from from - * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch - * - * a modified binary search algorithm to count 0 bits from - * the right (lsb). This algorithm should work regardless - * of the size of ints on the platform. - * - */ - - /* a couple special cases */ - if (x == 0) return 0; - if (x & 1) return 1; /* probably pretty common */ - - c = 1; - while ((x & 0xffff) == 0) { - x >>= 16; - c += 16; - } - if ((x & 0xff) == 0) { - x >>= 8; - c += 8; - } - if ((x & 0x0f) == 0) { - x >>= 4; - c += 4; - } - if ((x & 0x03) == 0) { - x >>= 2; - c += 2; - } - - c -= (x & 1); - c += 1; /* ffs() requires indexing bits from 1 */ - /* ie., the lsb is bit 1, not bit 0 */ - return c; -} - -inline int GET_MASK(int v, int mask) { - return (((v) & (mask)) >> (ffs(mask) - 1)); -}; - -inline int SET_MASK(int v, int mask, int val) { - const unsigned int __val = (val); - const unsigned int __mask = (mask); - - (v) &= ~(__mask); - return (v) |= ((__val) << (ffs(__mask)-1)) & (__mask); -} - -#define CASE_4(v, step) \ - case (v): \ - case (v)+(step) : \ - case (v)+(step) * 2: \ - case (v)+(step) * 3 - - -enum FifoMode { - FIFO_PIO = 0, - FIFO_DMA = 1, -}; - -enum FIFOEngine { - ENGINE_SOFTWARE = 0, - ENGINE_GRAPHICS = 1, - ENGINE_DVD = 2, -}; - -typedef struct RAMHTEntry { - uint32_t handle; - xbaddr instance; - enum FIFOEngine engine; - unsigned int channel_id : 5; - bool valid; -} RAMHTEntry; - -typedef struct DMAObject { - unsigned int dma_class; - unsigned int dma_target; - xbaddr address; - xbaddr limit; -} DMAObject; - -typedef struct VertexAttribute { - bool dma_select; - xbaddr offset; - - /* inline arrays are packed in order? - * Need to pass the offset to converted attributes */ - unsigned int inline_array_offset; - - float inline_value[4]; - - unsigned int format; - unsigned int size; /* size of the data type */ - unsigned int count; /* number of components */ - uint32_t stride; - - bool needs_conversion; - uint8_t *converted_buffer; - unsigned int converted_elements; - unsigned int converted_size; - unsigned int converted_count; - - float *inline_buffer; - - GLint gl_count; - GLenum gl_type; - GLboolean gl_normalize; - - GLuint gl_converted_buffer; - GLuint gl_inline_buffer; -} VertexAttribute; - -typedef struct Surface { - bool draw_dirty; - bool buffer_dirty; - bool write_enabled_cache; - unsigned int pitch; - - xbaddr offset; -} Surface; - -typedef struct SurfaceShape { - unsigned int z_format; - unsigned int color_format; - unsigned int zeta_format; - unsigned int log_width, log_height; - unsigned int clip_x, clip_y; - unsigned int clip_width, clip_height; - unsigned int anti_aliasing; -} SurfaceShape; - -typedef struct TextureShape { - bool cubemap; - unsigned int dimensionality; - unsigned int color_format; - unsigned int levels; - unsigned int width, height, depth; - - unsigned int min_mipmap_level, max_mipmap_level; - unsigned int pitch; -} TextureShape; - -typedef struct TextureKey { - TextureShape state; - uint64_t data_hash; - uint8_t* texture_data; - uint8_t* palette_data; -} TextureKey; - -typedef struct TextureBinding { - GLenum gl_target; - GLuint gl_texture; - unsigned int refcnt; -} TextureBinding; - -typedef struct KelvinState { - xbaddr dma_notifies; - xbaddr dma_state; - xbaddr dma_semaphore; - unsigned int semaphore_offset; -} KelvinState; - -typedef struct ContextSurfaces2DState { - xbaddr dma_image_source; - xbaddr dma_image_dest; - unsigned int color_format; - unsigned int source_pitch, dest_pitch; - xbaddr source_offset, dest_offset; - -} ContextSurfaces2DState; - -typedef struct ImageBlitState { - xbaddr context_surfaces; - unsigned int operation; - unsigned int in_x, in_y; - unsigned int out_x, out_y; - unsigned int width, height; - -} ImageBlitState; - -typedef struct GraphicsObject { - uint8_t graphics_class; - union { - ContextSurfaces2DState context_surfaces_2d; - - ImageBlitState image_blit; - - KelvinState kelvin; - } data; -} GraphicsObject; - -typedef struct GraphicsSubchannel { - xbaddr object_instance; - GraphicsObject object; - uint32_t object_cache[5]; -} GraphicsSubchannel; - -typedef struct GraphicsContext { - bool channel_3d; - unsigned int subchannel; -} GraphicsContext; - -typedef struct CacheEntry { - unsigned int method : 14; - unsigned int subchannel : 3; - bool nonincreasing; - uint32_t parameter; -} CacheEntry; - -typedef struct Cache1State { - unsigned int channel_id; - FifoMode mode; - - /* Pusher state */ - bool push_enabled; - bool dma_push_enabled; - bool dma_push_suspended; - xbaddr dma_instance; - - bool method_nonincreasing; - unsigned int method : 14; - unsigned int subchannel : 3; - unsigned int method_count : 24; - uint32_t dcount; - - bool subroutine_active; - xbaddr subroutine_return; - xbaddr get_jmp_shadow; - uint32_t rsvd_shadow; - uint32_t data_shadow; - uint32_t error; - - bool pull_enabled; - enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS]; - enum FIFOEngine last_engine; - - /* The actual command queue */ - std::mutex mutex; - std::condition_variable cache_cond; - std::queue cache; - std::queue working_cache; -} Cache1State; - -typedef struct ChannelControl { - xbaddr dma_put; - xbaddr dma_get; - uint32_t ref; -} ChannelControl; - -struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t regs[NV_PMC_SIZE]; // TODO : union -} pmc; - -struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - std::thread puller_thread; - Cache1State cache1; - uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union -} pfifo; - -struct { - uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union -} pvideo; - - -struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t numerator; - uint32_t denominator; - uint32_t alarm_time; - uint32_t regs[NV_PTIMER_SIZE]; // TODO : union -} ptimer; - -struct { - uint32_t regs[NV_PFB_SIZE]; // TODO : union -} pfb; - -struct { - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - uint32_t start; - uint32_t regs[NV_PCRTC_SIZE]; // TODO : union -} pcrtc; - -struct { - uint32_t core_clock_coeff; - uint64_t core_clock_freq; - uint32_t memory_clock_coeff; - uint32_t video_clock_coeff; - uint32_t regs[NV_PRAMDAC_SIZE]; // TODO : union -} pramdac; - -struct { - intptr_t memory; - uint32_t regs[NV_PRAMIN_SIZE]; // TODO : union -} pramin; - -struct { - ChannelControl channel_control[NV2A_NUM_CHANNELS]; -} user; - -// PRMCIO (Actually the VGA controller) -struct { - uint8_t cr_index; - uint8_t cr[256]; /* CRT registers */ -} prmcio; - -struct { - std::mutex mutex; - - uint32_t pending_interrupts; - uint32_t enabled_interrupts; - std::condition_variable interrupt_cond; - - xbaddr context_table; - xbaddr context_address; - - - unsigned int trapped_method; - unsigned int trapped_subchannel; - unsigned int trapped_channel_id; - uint32_t trapped_data[2]; - uint32_t notify_source; - - bool fifo_access; - std::condition_variable fifo_access_cond; - std::condition_variable flip_3d; - - unsigned int channel_id; - bool channel_valid; - GraphicsContext context[NV2A_NUM_CHANNELS]; - - xbaddr dma_color, dma_zeta; - Surface surface_color, surface_zeta; - unsigned int surface_type; - SurfaceShape surface_shape; - SurfaceShape last_surface_shape; - - xbaddr dma_a, dma_b; - //GLruCache *texture_cache; - bool texture_dirty[NV2A_MAX_TEXTURES]; - TextureBinding *texture_binding[NV2A_MAX_TEXTURES]; - - //GHashTable *shader_cache; - //ShaderBinding *shader_binding; - - bool texture_matrix_enable[NV2A_MAX_TEXTURES]; - - /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ - float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */ - - // wglContext *gl_context; - GLuint gl_framebuffer; - GLuint gl_color_buffer, gl_zeta_buffer; - GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS]; - - xbaddr dma_report; - xbaddr report_offset; - bool zpass_pixel_count_enable; - unsigned int zpass_pixel_count_result; - unsigned int gl_zpass_pixel_count_query_count; - GLuint* gl_zpass_pixel_count_queries; - - xbaddr dma_vertex_a, dma_vertex_b; - - unsigned int primitive_mode; - - bool enable_vertex_program_write; - - //uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; - - uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; - bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS]; - - /* lighting constant arrays */ - uint32_t ltctxa[NV2A_LTCTXA_COUNT][4]; - bool ltctxa_dirty[NV2A_LTCTXA_COUNT]; - uint32_t ltctxb[NV2A_LTCTXB_COUNT][4]; - bool ltctxb_dirty[NV2A_LTCTXB_COUNT]; - uint32_t ltc1[NV2A_LTC1_COUNT][4]; - bool ltc1_dirty[NV2A_LTC1_COUNT]; - - // should figure out where these are in lighting context - float light_infinite_half_vector[NV2A_MAX_LIGHTS][3]; - float light_infinite_direction[NV2A_MAX_LIGHTS][3]; - float light_local_position[NV2A_MAX_LIGHTS][3]; - float light_local_attenuation[NV2A_MAX_LIGHTS][3]; - - //VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES]; - - unsigned int inline_array_length; - uint32_t inline_array[NV2A_MAX_BATCH_LENGTH]; - GLuint gl_inline_array_buffer; - - unsigned int inline_elements_length; - uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH]; - - unsigned int inline_buffer_length; - - unsigned int draw_arrays_length; - unsigned int draw_arrays_max_count; - - /* FIXME: Unknown size, possibly endless, 1000 will do for now */ - GLint gl_draw_arrays_start[1000]; - GLsizei gl_draw_arrays_count[1000]; - - GLuint gl_element_buffer; - GLuint gl_memory_buffer; - GLuint gl_vertex_array; - - uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union -} pgraph; - -static void update_irq() +static void update_irq(NV2AState *d) { /* PFIFO */ - if (pfifo.pending_interrupts & pfifo.enabled_interrupts) { - pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO; + if (d->pfifo.pending_interrupts & d->pfifo.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PFIFO; } else { - pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO; + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PFIFO; } /* PCRTC */ - if (pcrtc.pending_interrupts & pcrtc.enabled_interrupts) { - pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC; + if (d->pcrtc.pending_interrupts & d->pcrtc.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PCRTC; } else { - pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC; + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PCRTC; } /* PGRAPH */ - if (pgraph.pending_interrupts & pgraph.enabled_interrupts) { - pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH; + if (d->pgraph.pending_interrupts & d->pgraph.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PGRAPH; } else { - pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH; + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PGRAPH; } /* TODO : PBUS * / - if (pbus.pending_interrupts & pbus.enabled_interrupts) { - pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS; + if (d->pbus.pending_interrupts & d->pbus.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_PBUS; } else { - pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS; + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_PBUS; } */ /* TODO : SOFTWARE * / - if (user.pending_interrupts & user.enabled_interrupts) { - pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE; + if (d->user.pending_interrupts & d->.enabled_interrupts) { + d->pmc.pending_interrupts |= NV_PMC_INTR_0_SOFTWARE; } else { - pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE; + d->pmc.pending_interrupts &= ~NV_PMC_INTR_0_SOFTWARE; } */ - if (pmc.pending_interrupts && pmc.enabled_interrupts) { + if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) { HalSystemInterrupts[3].Assert(true); } else { HalSystemInterrupts[3].Assert(false); @@ -566,13 +136,13 @@ static void update_irq() #define DEBUG_WRITE32(DEV) DbgPrintf("X86 : Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Handled %s]\n", addr, value, DebugNV_##DEV##(addr)) #define DEBUG_WRITE32_UNHANDLED(DEV) { DbgPrintf("X86 : Wr32 NV2A " #DEV "(0x%08X, 0x%08X) [Unhandled %s]\n", addr, value, DebugNV_##DEV##(addr)); return; } -#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(xbaddr addr) +#define DEVICE_READ32(DEV) uint32_t EmuNV2A_##DEV##_Read32(NV2AState *d, xbaddr addr) #define DEVICE_READ32_SWITCH() uint32_t result = 0; switch (addr) -#define DEVICE_READ32_REG(dev) result = dev.regs[addr] +#define DEVICE_READ32_REG(dev) result = d->dev.regs[addr] #define DEVICE_READ32_END(DEV) DEBUG_READ32(DEV); return result -#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(xbaddr addr, uint32_t value) -#define DEVICE_WRITE32_REG(dev) dev.regs[addr] = value +#define DEVICE_WRITE32(DEV) void EmuNV2A_##DEV##_Write32(NV2AState *d, xbaddr addr, uint32_t value) +#define DEVICE_WRITE32_REG(dev) d->dev.regs[addr] = value #define DEVICE_WRITE32_END(DEV) DEBUG_WRITE32(DEV) static inline uint32_t ldl_le_p(const void *p) @@ -585,11 +155,11 @@ static inline void stl_le_p(uint32_t *p, uint32 v) *p = v; } -static DMAObject nv_dma_load(xbaddr dma_obj_address) +static DMAObject nv_dma_load(NV2AState *d, xbaddr dma_obj_address) { - assert(dma_obj_address < NV_PRAMIN_SIZE); + assert(dma_obj_address < d->pramin.ramin_size); - uint32_t *dma_obj = (uint32_t*)(pramin.memory + dma_obj_address); + uint32_t *dma_obj = (uint32_t*)(d->pramin.ramin_ptr + dma_obj_address); uint32_t flags = ldl_le_p(dma_obj); uint32_t limit = ldl_le_p(dma_obj + 1); uint32_t frame = ldl_le_p(dma_obj + 2); @@ -603,11 +173,11 @@ static DMAObject nv_dma_load(xbaddr dma_obj_address) return object; } -static void *nv_dma_map(xbaddr dma_obj_address, xbaddr *len) +static void *nv_dma_map(NV2AState *d, xbaddr dma_obj_address, xbaddr *len) { - assert(dma_obj_address < NV_PRAMIN_SIZE); + assert(dma_obj_address < d->pramin.ramin_size); - DMAObject dma = nv_dma_load(dma_obj_address); + DMAObject dma = nv_dma_load(d, dma_obj_address); /* TODO: Handle targets and classes properly */ printf("dma_map %x, %x, %x %x\n", @@ -620,1529 +190,27 @@ static void *nv_dma_map(xbaddr dma_obj_address, xbaddr *len) return (void*)(MM_SYSTEM_PHYSICAL_MAP + dma.address); } -static void pgraph_context_switch(unsigned int channel_id) -{ - bool valid = false; - - // Scope the lock so that it gets unlocked at end of this block - { - std::lock_guard lk(pgraph.mutex); - - valid = pgraph.channel_valid && pgraph.channel_id == channel_id; - if (!valid) { - pgraph.trapped_channel_id = channel_id; - } - } - - if (!valid) { - printf("puller needs to switch to ch %d\n", channel_id); - - //qemu_mutex_lock_iothread(); - pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; - update_irq(); - - std::unique_lock lk(pgraph.mutex); - //qemu_mutex_unlock_iothread(); - - while (pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) { - pgraph.interrupt_cond.wait(lk); - } - } -} - -static void pgraph_wait_fifo_access() { - std::unique_lock lk(pgraph.mutex); - - while (!pgraph.fifo_access) { - pgraph.fifo_access_cond.wait(lk); - } -} - -static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter) { - static unsigned int last = 0; - static unsigned int count = 0; - - if (last == 0x1800 && method != last) { - printf("pgraph method (%d) 0x%08X * %d", subchannel, last, count); - } - if (method != 0x1800) { - const char* method_name = NULL; - unsigned int nmethod = 0; - switch (graphics_class) { - case NV_KELVIN_PRIMITIVE: - nmethod = method | (0x5c << 16); - break; - case NV_CONTEXT_SURFACES_2D: - nmethod = method | (0x6d << 16); - break; - default: - break; - } - /* - if (nmethod != 0 && nmethod < ARRAY_SIZE(nv2a_method_names)) { - method_name = nv2a_method_names[nmethod]; - } - if (method_name) { - printf("pgraph method (%d): %s (0x%x)\n", - subchannel, method_name, parameter); - } - else { - */ - printf("pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n", - subchannel, graphics_class, method, parameter); - //} - - } - if (method == last) { count++; } - else { count = 0; } - last = method; -} - -static void load_graphics_object(xbaddr instance_address, GraphicsObject *obj) -{ - uint8_t *obj_ptr; - uint32_t switch1, switch2, switch3; - - assert(instance_address < NV_PRAMIN_SIZE); - obj_ptr = (uint8_t*)(pramin.memory + instance_address); - - switch1 = ldl_le_p((uint32_t*)obj_ptr); - switch2 = ldl_le_p((uint32_t*)(obj_ptr + 4)); - switch3 = ldl_le_p((uint32_t*)(obj_ptr + 8)); - - obj->graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS; - - /* init graphics object */ - switch (obj->graphics_class) { - case NV_KELVIN_PRIMITIVE: - // kelvin->vertex_attributes[NV2A_VERTEX_ATTR_DIFFUSE].inline_value = 0xFFFFFFF; - break; - default: - break; - } -} - -static GraphicsObject* lookup_graphics_object(xbaddr instance_address) -{ - int i; - for (i = 0; i lk(pgraph.mutex); - -// int i; - GraphicsSubchannel *subchannel_data; - GraphicsObject *object; - - unsigned int slot; - - assert(pgraph.channel_valid); - subchannel_data = &pgraph.subchannel_data[subchannel]; - object = &subchannel_data->object; - - ContextSurfaces2DState *context_surfaces_2d = &object->data.context_surfaces_2d; - ImageBlitState *image_blit = &object->data.image_blit; - KelvinState *kelvin = &object->data.kelvin; - - pgraph_method_log(subchannel, object->graphics_class, method, parameter); - - if (method == NV_SET_OBJECT) { - subchannel_data->object_instance = parameter; - - //qemu_mutex_lock_iothread(); - load_graphics_object(parameter, object); - //qemu_mutex_unlock_iothread(); - return; - } - - switch (object->graphics_class) { - case NV_CONTEXT_SURFACES_2D: { - switch (method) { - case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: - context_surfaces_2d->dma_image_source = parameter; - break; - case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN: - context_surfaces_2d->dma_image_dest = parameter; - break; - case NV062_SET_COLOR_FORMAT: - context_surfaces_2d->color_format = parameter; - break; - case NV062_SET_PITCH: - context_surfaces_2d->source_pitch = parameter & 0xFFFF; - context_surfaces_2d->dest_pitch = parameter >> 16; - break; - case NV062_SET_OFFSET_SOURCE: - context_surfaces_2d->source_offset = parameter & 0x07FFFFFF; - break; - case NV062_SET_OFFSET_DESTIN: - context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF; - break; - default: - EmuWarning("EmuNV2A: Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X\n", method); - } - - break; - } - - case NV_IMAGE_BLIT: { - switch (method) { - case NV09F_SET_CONTEXT_SURFACES: - image_blit->context_surfaces = parameter; - break; - case NV09F_SET_OPERATION: - image_blit->operation = parameter; - break; - case NV09F_CONTROL_POINT_IN: - image_blit->in_x = parameter & 0xFFFF; - image_blit->in_y = parameter >> 16; - break; - case NV09F_CONTROL_POINT_OUT: - image_blit->out_x = parameter & 0xFFFF; - image_blit->out_y = parameter >> 16; - break; - case NV09F_SIZE: - image_blit->width = parameter & 0xFFFF; - image_blit->height = parameter >> 16; - - /* I guess this kicks it off? */ - if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { - - printf("NV09F_SET_OPERATION_SRCCOPY\n"); - - GraphicsObject *context_surfaces_obj = lookup_graphics_object(image_blit->context_surfaces); - assert(context_surfaces_obj); - assert(context_surfaces_obj->graphics_class == NV_CONTEXT_SURFACES_2D); - - ContextSurfaces2DState *context_surfaces = &context_surfaces_obj->data.context_surfaces_2d; - - unsigned int bytes_per_pixel; - switch (context_surfaces->color_format) { - case NV062_SET_COLOR_FORMAT_LE_Y8: - bytes_per_pixel = 1; - break; - case NV062_SET_COLOR_FORMAT_LE_R5G6B5: - bytes_per_pixel = 2; - break; - case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8: - bytes_per_pixel = 4; - break; - default: - printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format); - assert(false); - break; - } - - xbaddr source_dma_len, dest_dma_len; - uint8_t *source, *dest; - - source = (uint8_t*)nv_dma_map(context_surfaces->dma_image_source, &source_dma_len); - assert(context_surfaces->source_offset < source_dma_len); - source += context_surfaces->source_offset; - - dest = (uint8_t*)nv_dma_map(context_surfaces->dma_image_dest, &dest_dma_len); - assert(context_surfaces->dest_offset < dest_dma_len); - dest += context_surfaces->dest_offset; - - printf(" - 0x%tx -> 0x%tx\n", source - MM_SYSTEM_PHYSICAL_MAP,dest - MM_SYSTEM_PHYSICAL_MAP); - - unsigned int y; - for (y = 0; yheight; y++) { - uint8_t *source_row = source - + (image_blit->in_y + y) * context_surfaces->source_pitch - + image_blit->in_x * bytes_per_pixel; - - uint8_t *dest_row = dest - + (image_blit->out_y + y) * context_surfaces->dest_pitch - + image_blit->out_x * bytes_per_pixel; - - memmove(dest_row, source_row, - image_blit->width * bytes_per_pixel); - } - } - else { - assert(false); - } - - break; - default: - EmuWarning("EmuNV2A: Unknown NV_IMAGE_BLIT Method: 0x%08X\n", method); - } - break; - } - - case NV_KELVIN_PRIMITIVE: { - switch (method) { - case NV097_SET_CONTEXT_DMA_NOTIFIES: - kelvin->dma_notifies = parameter; - break; - case NV097_SET_CONTEXT_DMA_A: - pgraph.dma_a = parameter; - break; - case NV097_SET_CONTEXT_DMA_B: - pgraph.dma_b = parameter; - break; - case NV097_SET_CONTEXT_DMA_STATE: - kelvin->dma_state = parameter; - break; - case NV097_SET_CONTEXT_DMA_COLOR: - /* try to get any straggling draws in before the surface's changed :/ */ - pgraph_update_surface(false, true, true); - - pgraph.dma_color = parameter; - break; - case NV097_SET_CONTEXT_DMA_ZETA: - pgraph.dma_zeta = parameter; - break; - case NV097_SET_CONTEXT_DMA_VERTEX_A: - pgraph.dma_vertex_a = parameter; - break; - case NV097_SET_CONTEXT_DMA_VERTEX_B: - pgraph.dma_vertex_b = parameter; - break; - case NV097_SET_CONTEXT_DMA_SEMAPHORE: - kelvin->dma_semaphore = parameter; - break; - case NV097_SET_CONTEXT_DMA_REPORT: - pgraph.dma_report = parameter; - break; - case NV097_SET_SURFACE_CLIP_HORIZONTAL: - pgraph_update_surface(false, true, true); - - pgraph.surface_shape.clip_x = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X); - pgraph.surface_shape.clip_width = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH); - break; - case NV097_SET_SURFACE_CLIP_VERTICAL: - pgraph_update_surface(false, true, true); - - pgraph.surface_shape.clip_y = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y); - pgraph.surface_shape.clip_height = - GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT); - break; - case NV097_SET_SURFACE_FORMAT: - pgraph_update_surface(false, true, true); - - pgraph.surface_shape.color_format = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR); - pgraph.surface_shape.zeta_format = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA); - pgraph.surface_type = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE); - pgraph.surface_shape.anti_aliasing = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING); - pgraph.surface_shape.log_width = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH); - pgraph.surface_shape.log_height = - GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT); - break; - case NV097_SET_SURFACE_PITCH: - pgraph_update_surface(false, true, true); - - pgraph.surface_color.pitch = - GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR); - pgraph.surface_zeta.pitch = - GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA); - break; - case NV097_SET_SURFACE_COLOR_OFFSET: - pgraph_update_surface(false, true, true); - - pgraph.surface_color.offset = parameter; - break; - case NV097_SET_SURFACE_ZETA_OFFSET: - pgraph_update_surface(false, true, true); - - pgraph.surface_zeta.offset = parameter; - break; - case NV097_SET_COMBINER_SPECULAR_FOG_CW0: - pgraph.regs[NV_PGRAPH_COMBINESPECFOG0] = parameter; - break; - case NV097_SET_COMBINER_SPECULAR_FOG_CW1: - pgraph.regs[NV_PGRAPH_COMBINESPECFOG1] = parameter; - break; - CASE_4(NV097_SET_TEXTURE_ADDRESS, 64): - slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; - pgraph.regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter; - break; - case NV097_SET_CONTROL0: { - pgraph_update_surface(false, true, true); - - bool stencil_write_enable = - parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE; - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE, - stencil_write_enable); - - uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT); - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format); - - bool z_perspective = - parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE; - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, - z_perspective); - break; - } - - case NV097_SET_FOG_MODE: { - /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */ - unsigned int mode; - switch (parameter) { - case NV097_SET_FOG_MODE_V_LINEAR: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break; - case NV097_SET_FOG_MODE_V_EXP: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break; - case NV097_SET_FOG_MODE_V_EXP2: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break; - case NV097_SET_FOG_MODE_V_EXP_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break; - case NV097_SET_FOG_MODE_V_EXP2_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break; - case NV097_SET_FOG_MODE_V_LINEAR_ABS: - mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break; - default: - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE, - mode); - break; - } - case NV097_SET_FOG_GEN_MODE: { - unsigned int mode; - switch (parameter) { - case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break; - case NV097_SET_FOG_GEN_MODE_V_RADIAL: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break; - case NV097_SET_FOG_GEN_MODE_V_PLANAR: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break; - case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break; - case NV097_SET_FOG_GEN_MODE_V_FOG_X: - mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break; - default: - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode); - break; - } - case NV097_SET_FOG_ENABLE: - /* - FIXME: There is also: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE, - parameter); - */ - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE, - parameter); - break; - case NV097_SET_FOG_COLOR: { - /* PGRAPH channels are ARGB, parameter channels are ABGR */ - uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED); - uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN); - uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE); - uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA); - SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red); - SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green); - SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue); - SET_MASK(pgraph.regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha); - break; - } - case NV097_SET_ALPHA_TEST_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter); - break; - case NV097_SET_BLEND_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter); - break; - case NV097_SET_CULL_FACE_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_CULLENABLE, - parameter); - break; - case NV097_SET_DEPTH_TEST_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE, - parameter); - break; - case NV097_SET_DITHER_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter); - break; - case NV097_SET_LIGHTING_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING, - parameter); - break; - case NV097_SET_SKIN_MODE: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN, - parameter); - break; - case NV097_SET_STENCIL_TEST_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_POINT_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_LINE_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter); - break; - case NV097_SET_POLY_OFFSET_FILL_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter); - break; - case NV097_SET_ALPHA_FUNC: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF); - break; - case NV097_SET_ALPHA_REF: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHAREF, parameter); - break; - case NV097_SET_BLEND_FUNC_SFACTOR: { - unsigned int factor; - switch (parameter) { - case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO: - factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE: - factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break; - case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; - default: - fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor); - - break; - } - - case NV097_SET_BLEND_FUNC_DFACTOR: { - unsigned int factor; - switch (parameter) { - case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO: - factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE: - factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break; - case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: - factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; - default: - fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor); - - break; - } - - case NV097_SET_BLEND_COLOR: - pgraph.regs[NV_PGRAPH_BLENDCOLOR] = parameter; - break; - - case NV097_SET_BLEND_EQUATION: { - unsigned int equation; - switch (parameter) { - case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT: - equation = 0; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT: - equation = 1; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_ADD: - equation = 2; break; - case NV097_SET_BLEND_EQUATION_V_MIN: - equation = 3; break; - case NV097_SET_BLEND_EQUATION_V_MAX: - equation = 4; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED: - equation = 5; break; - case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED: - equation = 6; break; - default: - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation); - - break; - } - - case NV097_SET_DEPTH_FUNC: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC, - parameter & 0xF); - break; - - case NV097_SET_COLOR_MASK: { - pgraph.surface_color.write_enabled_cache |= pgraph_color_write_enabled(); - - bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE; - bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; - bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; - bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE; - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha); - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red); - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green); - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue); - break; - } - case NV097_SET_DEPTH_MASK: - pgraph.surface_zeta.write_enabled_cache |= pgraph_zeta_write_enabled(); - - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_0], - NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter); - break; - case NV097_SET_STENCIL_MASK: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter); - break; - case NV097_SET_STENCIL_FUNC: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF); - break; - case NV097_SET_STENCIL_FUNC_REF: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter); - break; - case NV097_SET_STENCIL_FUNC_MASK: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_1], - NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter); - break; - case NV097_SET_STENCIL_OP_FAIL: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL, - kelvin_map_stencil_op(parameter)); - break; - case NV097_SET_STENCIL_OP_ZFAIL: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL, - kelvin_map_stencil_op(parameter)); - break; - case NV097_SET_STENCIL_OP_ZPASS: - SET_MASK(pgraph.regs[NV_PGRAPH_CONTROL_2], - NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS, - kelvin_map_stencil_op(parameter)); - break; - - case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR: - pgraph.regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter; - break; - case NV097_SET_POLYGON_OFFSET_BIAS: - pgraph.regs[NV_PGRAPH_ZOFFSETBIAS] = parameter; - break; - case NV097_SET_FRONT_POLYGON_MODE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACEMODE, - kelvin_map_polygon_mode(parameter)); - break; - case NV097_SET_BACK_POLYGON_MODE: - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_BACKFACEMODE, - kelvin_map_polygon_mode(parameter)); - break; - case NV097_SET_CLIP_MIN: - pgraph.regs[NV_PGRAPH_ZCLIPMIN] = parameter; - break; - case NV097_SET_CLIP_MAX: - pgraph.regs[NV_PGRAPH_ZCLIPMAX] = parameter; - break; - case NV097_SET_CULL_FACE: { - unsigned int face; - switch (parameter) { - case NV097_SET_CULL_FACE_V_FRONT: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break; - case NV097_SET_CULL_FACE_V_BACK: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break; - case NV097_SET_CULL_FACE_V_FRONT_AND_BACK: - face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break; - default: - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_CULLCTRL, - face); - break; - } - case NV097_SET_FRONT_FACE: { - bool ccw; - switch (parameter) { - case NV097_SET_FRONT_FACE_V_CW: - ccw = false; break; - case NV097_SET_FRONT_FACE_V_CCW: - ccw = true; break; - default: - fprintf(stderr, "Unknown front face: 0x%x\n", parameter); - assert(false); - break; - } - SET_MASK(pgraph.regs[NV_PGRAPH_SETUPRASTER], - NV_PGRAPH_SETUPRASTER_FRONTFACE, - ccw ? 1 : 0); - break; - } - case NV097_SET_NORMALIZATION_ENABLE: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_C], - NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, - parameter); - break; - - case NV097_SET_LIGHT_ENABLE_MASK: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], - NV_PGRAPH_CSV0_D_LIGHTS, - parameter); - break; - - CASE_4(NV097_SET_TEXGEN_S, 16) : { - slot = (method - NV097_SET_TEXGEN_S) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S - : NV_PGRAPH_CSV1_A_T0_S; - SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 0)); - break; - } - CASE_4(NV097_SET_TEXGEN_T, 16) : { - slot = (method - NV097_SET_TEXGEN_T) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T - : NV_PGRAPH_CSV1_A_T0_T; - SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 1)); - break; - } - CASE_4(NV097_SET_TEXGEN_R, 16) : { - slot = (method - NV097_SET_TEXGEN_R) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R - : NV_PGRAPH_CSV1_A_T0_R; - SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 2)); - break; - } - CASE_4(NV097_SET_TEXGEN_Q, 16) : { - slot = (method - NV097_SET_TEXGEN_Q) / 16; - unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A - : NV_PGRAPH_CSV1_B; - unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q - : NV_PGRAPH_CSV1_A_T0_Q; - SET_MASK(pgraph.regs[reg], mask, kelvin_map_texgen(parameter, 3)); - break; - } - CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) : - slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; - pgraph.texture_matrix_enable[slot] = parameter; - break; - - case NV097_SET_TEXGEN_VIEW_MODEL: - SET_MASK(pgraph.regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter); - break; - case NV097_SET_SEMAPHORE_OFFSET: - kelvin->semaphore_offset = parameter; - break; - case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: { - - pgraph_update_surface(false, true, true); - - //qemu_mutex_unlock(&d->pgraph.lock); - //qemu_mutex_lock_iothread(); - - xbaddr semaphore_dma_len; - uint8_t *semaphore_data = (uint8_t*)nv_dma_map(kelvin->dma_semaphore, - &semaphore_dma_len); - assert(kelvin->semaphore_offset < semaphore_dma_len); - semaphore_data += kelvin->semaphore_offset; - - stl_le_p((uint32_t*)semaphore_data, parameter); - - //qemu_mutex_lock(&d->pgraph.lock); - //qemu_mutex_unlock_iothread(); - - break; - } - default: - if (method >= NV097_SET_COMBINER_ALPHA_ICW && method <= NV097_SET_COMBINER_ALPHA_ICW + 28) { - slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; - pgraph.regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_PROJECTION_MATRIX && method <= NV097_SET_PROJECTION_MATRIX + 0x3c) { - slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; - // pg->projection_matrix[slot] = *(float*)¶meter; - unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; - pgraph.vsh_constants[row][slot % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_MODEL_VIEW_MATRIX && method <= NV097_SET_MODEL_VIEW_MATRIX + 0xfc) { - slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; - pgraph.vsh_constants[row][entry % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_INVERSE_MODEL_VIEW_MATRIX && method <= NV097_SET_INVERSE_MODEL_VIEW_MATRIX + 0xfc) { - slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; - unsigned int matnum = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; - pgraph.vsh_constants[row][entry % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_COMPOSITE_MATRIX && method <= NV097_SET_COMPOSITE_MATRIX + 0x3c) { - slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; - unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; - pgraph.vsh_constants[row][slot % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_TEXTURE_MATRIX && method <= NV097_SET_TEXTURE_MATRIX + 0xfc) { - slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; - pgraph.vsh_constants[row][entry % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_FOG_PARAMS && method <= NV097_SET_FOG_PARAMS + 8) { - slot = (method - NV097_SET_FOG_PARAMS) / 4; - if (slot < 2) { - pgraph.regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; - } - else { - /* FIXME: No idea where slot = 2 is */ - } - - pgraph.ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; - pgraph.ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; - break; - } - - /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ - if (method >= NV097_SET_TEXGEN_PLANE_S && method <= NV097_SET_TEXGEN_PLANE_S + 0xfc) { - slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; - unsigned int tex = slot / 16; - unsigned int entry = slot % 16; - unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; - pgraph.vsh_constants[row][entry % 4] = parameter; - pgraph.vsh_constants_dirty[row] = true; - break; - } - - if (method >= NV097_SET_FOG_PLANE && method <= NV097_SET_FOG_PLANE + 12) { - slot = (method - NV097_SET_FOG_PLANE) / 4; - pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; - pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; - break; - } - - if (method >= NV097_SET_SCENE_AMBIENT_COLOR && method <= NV097_SET_SCENE_AMBIENT_COLOR + 8) { - slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; - // ?? - pgraph.ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; - pgraph.ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; - break; - } - - if (method >= NV097_SET_VIEWPORT_OFFSET && method <= NV097_SET_VIEWPORT_OFFSET + 12) { - slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; - pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; - pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; - break; - } - - if (method >= NV097_SET_EYE_POSITION && method <= NV097_SET_EYE_POSITION + 12) { - slot = (method - NV097_SET_EYE_POSITION) / 4; - pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; - pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; - break; - } - - if (method >= NV097_SET_COMBINER_FACTOR0 && method <= NV097_SET_COMBINER_FACTOR0 + 28) { - slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; - pgraph.regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_FACTOR1 && method <= NV097_SET_COMBINER_FACTOR1 + 28) { - slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; - pgraph.regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_ALPHA_OCW && method <= NV097_SET_COMBINER_ALPHA_OCW + 28) { - slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; - pgraph.regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_COMBINER_COLOR_ICW && method <= NV097_SET_COMBINER_COLOR_ICW + 28) { - slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; - pgraph.regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; - break; - } - - if (method >= NV097_SET_VIEWPORT_SCALE && method <= NV097_SET_VIEWPORT_SCALE + 12) { - slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; - pgraph.vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; - pgraph.vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; - break; - } - - EmuWarning("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n", method); - } - break; - } - - default: - EmuWarning("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", object->graphics_class, method); - break; - } -} - -static void* pfifo_puller_thread() -{ - CxbxSetThreadName("Cxbx NV2A FIFO"); - - Cache1State *state = &pfifo.cache1; - - while (true) { - // Scope the lock so that it automatically unlocks at tne end of this block - { - std::unique_lock lk(state->mutex); - - while (state->cache.empty() || !state->pull_enabled) { - state->cache_cond.wait(lk); - } - - // Copy cache to working_cache - while (!state->cache.empty()) { - state->working_cache.push(state->cache.front()); - state->cache.pop(); - } - } - - while (!state->working_cache.empty()) { - CacheEntry* command = state->working_cache.front(); - state->working_cache.pop(); - - if (command->method == 0) { - // qemu_mutex_lock_iothread(); - RAMHTEntry entry = ramht_lookup(command->parameter); - assert(entry.valid); - - assert(entry.channel_id == state->channel_id); - // qemu_mutex_unlock_iothread(); - - switch (entry.engine) { - case ENGINE_GRAPHICS: - pgraph_context_switch(entry.channel_id); - pgraph_wait_fifo_access(); - pgraph_method(command->subchannel, 0, entry.instance); - break; - default: - assert(false); - break; - } - - /* the engine is bound to the subchannel */ - std::lock_guard lk(pfifo.cache1.mutex); - state->bound_engines[command->subchannel] = entry.engine; - state->last_engine = entry.engine; - } else if (command->method >= 0x100) { - /* method passed to engine */ - - uint32_t parameter = command->parameter; - - /* methods that take objects. - * TODO: Check this range is correct for the nv2a */ - if (command->method >= 0x180 && command->method < 0x200) { - //qemu_mutex_lock_iothread(); - RAMHTEntry entry = ramht_lookup(parameter); - assert(entry.valid); - assert(entry.channel_id == state->channel_id); - parameter = entry.instance; - //qemu_mutex_unlock_iothread(); - } - - // qemu_mutex_lock(&state->cache_lock); - enum FIFOEngine engine = state->bound_engines[command->subchannel]; - // qemu_mutex_unlock(&state->cache_lock); - - switch (engine) { - case ENGINE_GRAPHICS: - pgraph_wait_fifo_access(); - pgraph_method(command->subchannel, command->method, parameter); - break; - default: - assert(false); - break; - } - - // qemu_mutex_lock(&state->cache_lock); - state->last_engine = state->bound_engines[command->subchannel]; - // qemu_mutex_unlock(&state->cache_lock); - } - - free(command); - } - } - - return NULL; -} - -DEVICE_READ32(PMC) -{ - DEVICE_READ32_SWITCH() { - case NV_PMC_BOOT_0: // chipset and stepping: NV2A, A02, Rev 0 - result = 0x02A000A2; - break; - case NV_PMC_BOOT_1: // Selects big/little endian mode for the card - result = 0; // When read, returns 0 if in little-endian mode, 0x01000001 if in big-endian mode. - break; - case NV_PMC_INTR_0: // Shows which functional units have pending IRQ - result = pmc.pending_interrupts; - break; - case NV_PMC_INTR_EN_0: // Selects which functional units can cause IRQs - result = pmc.enabled_interrupts; - break; - default: - result = 0; - //DEVICE_READ32_REG(pmc); // Was : DEBUG_READ32_UNHANDLED(PMC); - break; - } - - DEVICE_READ32_END(PMC); -} - -DEVICE_WRITE32(PMC) -{ - switch(addr) { - case NV_PMC_INTR_0: - pmc.pending_interrupts &= ~value; - update_irq(); - break; - case NV_PMC_INTR_EN_0: - pmc.enabled_interrupts = value; - update_irq(); - break; - - default: - //DEVICE_WRITE32_REG(pmc); // Was : DEBUG_WRITE32_UNHANDLED(PMC); - break; - } - - DEVICE_WRITE32_END(PMC); -} - - -DEVICE_READ32(PBUS) -{ - DEVICE_READ32_SWITCH() { - case NV_PBUS_PCI_NV_0: - result = 0x10de; // PCI_VENDOR_ID_NVIDIA (?where to return PCI_DEVICE_ID_NVIDIA_NV2A = 0x01b7) - - break; - case NV_PBUS_PCI_NV_1: - result = 1; // NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED - break; - case NV_PBUS_PCI_NV_2: - result = (0x02 << 24) | 161; // PCI_CLASS_DISPLAY_3D (0x02) Rev 161 (0xA1) - break; - - default: - DEBUG_READ32_UNHANDLED(PBUS); // TODO : DEVICE_READ32_REG(pbus); - break; - } - - DEVICE_READ32_END(PBUS); -} - -DEVICE_WRITE32(PBUS) -{ - switch(addr) { - case NV_PBUS_PCI_NV_1: - // TODO : Handle write on NV_PBUS_PCI_NV_1 with 1 (NV_PBUS_PCI_NV_1_IO_SPACE_ENABLED) + 4 (NV_PBUS_PCI_NV_1_BUS_MASTER_ENABLED) - break; - default: - DEBUG_WRITE32_UNHANDLED(PBUS); // TODO : DEVICE_WRITE32_REG(pbus); - break; - } - - DEVICE_WRITE32_END(PBUS); -} - - -DEVICE_READ32(PFIFO) -{ - DEVICE_READ32_SWITCH() { - case NV_PFIFO_RAMHT: - result = 0x03000100; // = NV_PFIFO_RAMHT_SIZE_4K | NV_PFIFO_RAMHT_BASE_ADDRESS(NumberOfPaddingBytes >> 12) | NV_PFIFO_RAMHT_SEARCH_128 - break; - case NV_PFIFO_RAMFC: - result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ? - break; - case NV_PFIFO_INTR_0: - result = pfifo.pending_interrupts; - break; - case NV_PFIFO_INTR_EN_0: - result = pfifo.enabled_interrupts; - break; - case NV_PFIFO_RUNOUT_STATUS: - result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */ - break; - case NV_PFIFO_CACHE1_PUSH0: - result = pfifo.cache1.push_enabled; - break; - case NV_PFIFO_CACHE1_PUSH1: - SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, pfifo.cache1.channel_id); - SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, pfifo.cache1.mode); - break; - case NV_PFIFO_CACHE1_STATUS: { - std::lock_guard lk(pfifo.cache1.mutex); - - if (pfifo.cache1.cache.empty()) { - result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */ - } - - } break; - case NV_PFIFO_CACHE1_DMA_PUSH: - SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS, - pfifo.cache1.dma_push_enabled); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_STATUS, - pfifo.cache1.dma_push_suspended); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_BUFFER, 1); /* buffer emoty */ - break; - case NV_PFIFO_CACHE1_DMA_STATE: - SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, - pfifo.cache1.method_nonincreasing); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD, - pfifo.cache1.method >> 2); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, - pfifo.cache1.subchannel); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, - pfifo.cache1.method_count); - SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - pfifo.cache1.error); - break; - case NV_PFIFO_CACHE1_DMA_INSTANCE: - SET_MASK(result, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK, - pfifo.cache1.dma_instance >> 4); - break; - case NV_PFIFO_CACHE1_DMA_PUT: - result = user.channel_control[pfifo.cache1.channel_id].dma_put; - break; - case NV_PFIFO_CACHE1_DMA_GET: - result = user.channel_control[pfifo.cache1.channel_id].dma_get; - break; - case NV_PFIFO_CACHE1_DMA_SUBROUTINE: - result = pfifo.cache1.subroutine_return - | pfifo.cache1.subroutine_active; - break; - case NV_PFIFO_CACHE1_PULL0: { - std::lock_guard lk(pfifo.cache1.mutex); - result = pfifo.cache1.pull_enabled; - } break; - case NV_PFIFO_CACHE1_ENGINE: { - std::lock_guard lk(pfifo.cache1.mutex); - for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) { - result |= pfifo.cache1.bound_engines[i] << (i * 2); - } - - } break; - case NV_PFIFO_CACHE1_DMA_DCOUNT: - result = pfifo.cache1.dcount; - break; - case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW: - result = pfifo.cache1.get_jmp_shadow; - break; - case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW: - result = pfifo.cache1.rsvd_shadow; - break; - case NV_PFIFO_CACHE1_DMA_DATA_SHADOW: - result = pfifo.cache1.data_shadow; - break; - default: - DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO); - break; - } - - DEVICE_READ32_END(PFIFO); -} - -DEVICE_WRITE32(PFIFO) -{ - switch(addr) { - case NV_PFIFO_INTR_0: - pfifo.pending_interrupts &= ~value; - update_irq(); - break; - case NV_PFIFO_INTR_EN_0: - pfifo.enabled_interrupts = value; - update_irq(); - break; - case NV_PFIFO_CACHE1_PUSH0: - pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS; - break; - case NV_PFIFO_CACHE1_PUSH1: - pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID); - pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE); - assert(pfifo.cache1.channel_id < NV2A_NUM_CHANNELS); - break; - case NV_PFIFO_CACHE1_DMA_PUSH: - pfifo.cache1.dma_push_enabled = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS); - if (pfifo.cache1.dma_push_suspended && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) { - pfifo.cache1.dma_push_suspended = false; - pfifo_run_pusher(); - } - pfifo.cache1.dma_push_suspended = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS); - break; - case NV_PFIFO_CACHE1_DMA_STATE: - pfifo.cache1.method_nonincreasing = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); - pfifo.cache1.method = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; - pfifo.cache1.subchannel = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); - pfifo.cache1.method_count = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); - pfifo.cache1.error = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR); - break; - case NV_PFIFO_CACHE1_DMA_INSTANCE: - pfifo.cache1.dma_instance = GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; - break; - case NV_PFIFO_CACHE1_DMA_PUT: - user.channel_control[pfifo.cache1.channel_id].dma_put = value; - break; - case NV_PFIFO_CACHE1_DMA_GET: - user.channel_control[pfifo.cache1.channel_id].dma_get = value; - break; - case NV_PFIFO_CACHE1_DMA_SUBROUTINE: - pfifo.cache1.subroutine_return = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET); - pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); - break; - case NV_PFIFO_CACHE1_PULL0: { - std::lock_guard lk(pfifo.cache1.mutex); - - if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS) - && !pfifo.cache1.pull_enabled) { - pfifo.cache1.pull_enabled = true; - - /* the puller thread should wake up */ - pfifo.cache1.cache_cond.notify_all(); - } else if (!(value & NV_PFIFO_CACHE1_PULL0_ACCESS) - && pfifo.cache1.pull_enabled) { - pfifo.cache1.pull_enabled = false; - } - } break; - case NV_PFIFO_CACHE1_ENGINE: { - std::lock_guard lk(pfifo.cache1.mutex); - - for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) { - pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3); - } - - } break; - case NV_PFIFO_CACHE1_DMA_DCOUNT: - pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE); - break; - case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW: - pfifo.cache1.get_jmp_shadow = (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET); - break; - case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW: - pfifo.cache1.rsvd_shadow = value; - break; - case NV_PFIFO_CACHE1_DMA_DATA_SHADOW: - pfifo.cache1.data_shadow = value; - break; - default: - DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO); - break; - } - - DEVICE_WRITE32_END(PFIFO); -} - -DEVICE_READ32(PRMA) -{ - DEVICE_READ32_SWITCH() { - default: - DEBUG_READ32_UNHANDLED(PRMA); // TODO : DEVICE_READ32_REG(prma); - break; - } - - DEVICE_READ32_END(PRMA); -} - -DEVICE_WRITE32(PRMA) -{ - switch(addr) { - default: - DEBUG_WRITE32_UNHANDLED(PRMA); // TODO : DEVICE_WRITE32_REG(prma); - break; - } - - DEVICE_WRITE32_END(PRMA); -} - - -DEVICE_READ32(PVIDEO) -{ - DEVICE_READ32_SWITCH() { - - case NV_PVIDEO_STOP: - result = 0; - break; - default: - DEVICE_READ32_REG(pvideo); - break; - } - - DEVICE_READ32_END(PVIDEO); -} - -DEVICE_WRITE32(PVIDEO) -{ - switch (addr) { - case NV_PVIDEO_BUFFER: - pvideo.regs[addr] = value; - // TODO: vga.enable_overlay = true; - // pvideo_vga_invalidate(d); - break; - case NV_PVIDEO_STOP: - pvideo.regs[NV_PVIDEO_BUFFER] = 0; - // TODO: vga.enable_overlay = false; - //pvideo_vga_invalidate(d); - break; - default: - DEVICE_WRITE32_REG(pvideo); - break; - } - - DEVICE_WRITE32_END(PVIDEO); -} - -DEVICE_READ32(PCOUNTER) -{ - DEVICE_READ32_SWITCH() { - default: - DEBUG_READ32_UNHANDLED(PCOUNTER); // TODO : DEVICE_READ32_REG(pcounter); - break; - } - - DEVICE_READ32_END(PCOUNTER); -} - -DEVICE_WRITE32(PCOUNTER) -{ - switch (addr) { - default: - DEBUG_WRITE32_UNHANDLED(PCOUNTER); // TODO : DEVICE_WRITE32_REG(pcounter); - break; - } - - DEVICE_WRITE32_END(PCOUNTER); -} - -#include "EmuNV2A_PMC.cpp" #include "EmuNV2A_PBUS.cpp" +#include "EmuNV2A_PCRTC.cpp" +#include "EmuNV2A_PFB.cpp" +#include "EmuNV2A_PGRAPH.cpp" #include "EmuNV2A_PFIFO.cpp" -#include "EmuNV2A_PRMA.cpp" -#include "EmuNV2A_PVIDEO.cpp" -#include "EmuNV2A_PCOUNTER.cpp" +#include "EmuNV2A_PMC.cpp" +#include "EmuNV2A_PRAMDAC.cpp" +#include "EmuNV2A_PRMCIO.cpp" +#include "EmuNV2A_PRMVIO.cpp" #include "EmuNV2A_PTIMER.cpp" +#include "EmuNV2A_PVIDEO.cpp" +#include "EmuNV2A_USER.cpp" + +#include "EmuNV2A_PRMA.cpp" +#include "EmuNV2A_PCOUNTER.cpp" #include "EmuNV2A_PVPE.cpp" #include "EmuNV2A_PTV.cpp" #include "EmuNV2A_PRMFB.cpp" -#include "EmuNV2A_PRMVIO.cpp" -#include "EmuNV2A_PFB.cpp" #include "EmuNV2A_PSTRAPS.cpp" -#include "EmuNV2A_PGRAPH.cpp" -#include "EmuNV2A_PCRTC.cpp" -#include "EmuNV2A_PRMCIO.cpp" -#include "EmuNV2A_PRAMDAC.cpp" #include "EmuNV2A_PRMDIO.cpp" #include "EmuNV2A_PRAMIN.cpp" -#include "EmuNV2A_USER.cpp" - -typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr -typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t) - -typedef value_t(*read_func)(/*void *opaque, */hwaddr addr); //, unsigned int size); -typedef void(*write_func)(/*void *opaque, */hwaddr addr, value_t val); //, unsigned int size); - -typedef struct { - read_func read; - write_func write; -} MemoryRegionOps; - -typedef struct NV2ABlockInfo { - const char* name; - hwaddr offset; - uint64_t size; - MemoryRegionOps ops; -} NV2ABlockInfo; const NV2ABlockInfo regions[] = { // blocktable @@ -2193,9 +261,9 @@ const NV2ABlockInfo regions[] = { // blocktable ENTRY(0x700000, 0x100000, PRAMIN, EmuNV2A_PRAMIN_Read32, EmuNV2A_PRAMIN_Write32) /* PFIFO MMIO and DMA submission area */ ENTRY(0x800000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // Size was 0x800000 - /* User area mirror - TODO : Confirm */ - ENTRY(0xC00000, 0x400000, USER, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER - + /* UREMAP User area mirror - TODO : Confirm */ + ENTRY(0xC00000, 0x400000, UREMAP, EmuNV2A_USER_Read32, EmuNV2A_USER_Write32) // NOTE : Mirror of USER + // Terminating entry ENTRY(0xFFFFFF, 0x000000, END, nullptr, nullptr) #undef ENTRY }; @@ -2217,6 +285,9 @@ const NV2ABlockInfo* EmuNV2A_Block(xbaddr addr) return nullptr; } +NV2AState g_nv2a_state; +NV2AState *d = &g_nv2a_state; + uint32_t EmuNV2A_Read(xbaddr addr, int size) { const NV2ABlockInfo* block = EmuNV2A_Block(addr); @@ -2224,11 +295,11 @@ uint32_t EmuNV2A_Read(xbaddr addr, int size) if (block != nullptr) { switch (size) { case sizeof(uint8_t): - return block->ops.read(addr - block->offset) & 0xFF; + return block->ops.read(d, addr - block->offset) & 0xFF; case sizeof(uint16_t) : - return block->ops.read(addr - block->offset) & 0xFFFF; + return block->ops.read(d, addr - block->offset) & 0xFFFF; case sizeof(uint32_t) : - return block->ops.read(addr - block->offset); + return block->ops.read(d, addr - block->offset); default: EmuWarning("EmuNV2A_Read: Invalid read size: %d", size); return 0; @@ -2252,25 +323,25 @@ void EmuNV2A_Write(xbaddr addr, uint32_t value, int size) case sizeof(uint8_t) : shift = (addr & 3) * 8; aligned_addr = addr & ~3; - aligned_value = block->ops.read(aligned_addr - block->offset); + aligned_value = block->ops.read(d, aligned_addr - block->offset); mask = 0xFF << shift; // TODO : Must the second byte be written to the next DWORD? - block->ops.write(aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); + block->ops.write(d, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); return; case sizeof(uint16_t) : assert((addr & 1) == 0); shift = (addr & 2) * 16; aligned_addr = addr & ~3; - aligned_value = block->ops.read(addr - block->offset); + aligned_value = block->ops.read(d, addr - block->offset); mask = 0xFFFF << shift; // TODO : Must the second byte be written to the next DWORD? - block->ops.write(aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); + block->ops.write(d, aligned_addr - block->offset, (aligned_value & ~mask) | (value << shift)); return; case sizeof(uint32_t) : - block->ops.write(addr - block->offset, value); + block->ops.write(d, addr - block->offset, value); return; default: EmuWarning("EmuNV2A_Read: Invalid read size: %d", size); @@ -2555,7 +626,7 @@ void InitOpenGLContext() // we simulate VBLANK by calling the interrupt at 60Hz std::thread vblank_thread; extern std::chrono::time_point> GetNextVBlankTime(); -static void nv2a_vblank_thread() +static void nv2a_vblank_thread(NV2AState *d) { CxbxSetThreadName("Cxbx NV2A VBlank"); @@ -2564,14 +635,14 @@ static void nv2a_vblank_thread() while (true) { // Handle VBlank if (std::chrono::steady_clock::now() > nextVBlankTime) { - pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; - update_irq(); + d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; + update_irq(d); nextVBlankTime = GetNextVBlankTime(); } } } -void CxbxReserveNV2AMemory() +void CxbxReserveNV2AMemory(NV2AState *d) { // Reserve NV2A memory : void *memory = (void *)VirtualAllocEx( @@ -2589,13 +660,14 @@ void CxbxReserveNV2AMemory() GetCurrentThreadId(), NV2A_SIZE / ONE_MB, NV2A_ADDR, NV2A_ADDR + NV2A_SIZE - 1); // Allocate PRAMIN Region - pramin.memory = (intptr_t)VirtualAllocEx( + d->pramin.ramin_size = NV_PRAMIN_SIZE; + d->pramin.ramin_ptr = (uint8_t*)VirtualAllocEx( GetCurrentProcess(), (void*)(NV2A_ADDR + NV_PRAMIN_ADDR), - NV_PRAMIN_SIZE, + d->pramin.ramin_size, MEM_COMMIT, // No MEM_RESERVE | PAGE_READWRITE); - if (pramin.memory == NULL) { + if (d->pramin.ramin_ptr == NULL) { EmuWarning("Couldn't allocate NV2A PRAMIN memory"); return; } @@ -2606,19 +678,19 @@ void CxbxReserveNV2AMemory() void EmuNV2A_Init() { - CxbxReserveNV2AMemory(); + CxbxReserveNV2AMemory(d); - pcrtc.start = 0; + d->pcrtc.start = 0; - pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */ - pramdac.core_clock_freq = 189000000; - pramdac.memory_clock_coeff = 0; - pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */ + d->pramdac.core_clock_coeff = 0x00011c01; /* 189MHz...? */ + d->pramdac.core_clock_freq = 189000000; + d->pramdac.memory_clock_coeff = 0; + d->pramdac.video_clock_coeff = 0x0003C20D; /* 25182Khz...? */ - pfifo.puller_thread = std::thread(pfifo_puller_thread); + d->pfifo.puller_thread = std::thread(pfifo_puller_thread, d); // Only spawn VBlank thread when LLE is enabled if (bLLE_GPU) { - vblank_thread = std::thread(nv2a_vblank_thread); + vblank_thread = std::thread(nv2a_vblank_thread, d); } } diff --git a/src/devices/video/EmuNV2A.h b/src/devices/video/EmuNV2A.h index 10801acf7..3e17f503a 100644 --- a/src/devices/video/EmuNV2A.h +++ b/src/devices/video/EmuNV2A.h @@ -37,20 +37,6 @@ // Valid after PCI init : #define NV20_REG_BASE_KERNEL 0xFD000000 -#define NV2A_ADDR 0xFD000000 -#define NV2A_SIZE 0x01000000 - -#define NV_PMC_SIZE 0x001000 -#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE -#define NV_PVIDEO_SIZE 0x001000 -#define NV_PTIMER_SIZE 0x001000 -#define NV_PFB_SIZE 0x001000 -#define NV_PGRAPH_SIZE 0x002000 -#define NV_PCRTC_SIZE 0x001000 -#define NV_PRAMDAC_SIZE 0x001000 -#define NV_PRAMIN_ADDR 0x00700000 -#define NV_PRAMIN_SIZE 0x100000 - typedef volatile DWORD *PPUSH; typedef struct { diff --git a/src/devices/video/EmuNV2A_PCRTC.cpp b/src/devices/video/EmuNV2A_PCRTC.cpp index fdf01a1c8..c61eb608e 100644 --- a/src/devices/video/EmuNV2A_PCRTC.cpp +++ b/src/devices/video/EmuNV2A_PCRTC.cpp @@ -3,13 +3,13 @@ DEVICE_READ32(PCRTC) DEVICE_READ32_SWITCH() { case NV_PCRTC_INTR_0: - result = pcrtc.pending_interrupts; + result = d->pcrtc.pending_interrupts; break; case NV_PCRTC_INTR_EN_0: - result = pcrtc.enabled_interrupts; + result = d->pcrtc.enabled_interrupts; break; case NV_PCRTC_START: - result = pcrtc.start; + result = d->pcrtc.start; break; default: result = 0; @@ -25,17 +25,17 @@ DEVICE_WRITE32(PCRTC) switch (addr) { case NV_PCRTC_INTR_0: - pcrtc.pending_interrupts &= ~value; - update_irq(); + d->pcrtc.pending_interrupts &= ~value; + update_irq(d); break; case NV_PCRTC_INTR_EN_0: - pcrtc.enabled_interrupts = value; - update_irq(); + d->pcrtc.enabled_interrupts = value; + update_irq(d); break; case NV_PCRTC_START: value &= 0x07FFFFFF; // assert(val < memory_region_size(d->vram)); - pcrtc.start = value; + d->pcrtc.start = value; break; default: diff --git a/src/devices/video/EmuNV2A_PFIFO.cpp b/src/devices/video/EmuNV2A_PFIFO.cpp index 8302ab61e..6d8a3aef1 100644 --- a/src/devices/video/EmuNV2A_PFIFO.cpp +++ b/src/devices/video/EmuNV2A_PFIFO.cpp @@ -1,3 +1,15 @@ +typedef struct RAMHTEntry { + uint32_t handle; + xbaddr instance; + enum FIFOEngine engine; + unsigned int channel_id : 5; + bool valid; +} RAMHTEntry; + +static void pfifo_run_pusher(NV2AState *d); // forward declaration +int pfifo_puller_thread(void *opaque); +static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle); // forward declaration + DEVICE_READ32(PFIFO) { DEVICE_READ32_SWITCH() { @@ -8,84 +20,84 @@ DEVICE_READ32(PFIFO) result = 0x00890110; // = ? | NV_PFIFO_RAMFC_SIZE_2K | ? break; case NV_PFIFO_INTR_0: - result = pfifo.pending_interrupts; + result = d->pfifo.pending_interrupts; break; case NV_PFIFO_INTR_EN_0: - result = pfifo.enabled_interrupts; + result = d->pfifo.enabled_interrupts; break; case NV_PFIFO_RUNOUT_STATUS: result = NV_PFIFO_RUNOUT_STATUS_LOW_MARK; /* low mark empty */ break; case NV_PFIFO_CACHE1_PUSH0: - result = pfifo.cache1.push_enabled; + result = d->pfifo.cache1.push_enabled; break; case NV_PFIFO_CACHE1_PUSH1: - SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, pfifo.cache1.channel_id); - SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, pfifo.cache1.mode); + SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_CHID, d->pfifo.cache1.channel_id); + SET_MASK(result, NV_PFIFO_CACHE1_PUSH1_MODE, d->pfifo.cache1.mode); break; case NV_PFIFO_CACHE1_STATUS: { - std::lock_guard lk(pfifo.cache1.mutex); + std::lock_guard lk(d->pfifo.cache1.mutex); - if (pfifo.cache1.cache.empty()) { + if (d->pfifo.cache1.cache.empty()) { result |= NV_PFIFO_CACHE1_STATUS_LOW_MARK; /* low mark empty */ } } break; case NV_PFIFO_CACHE1_DMA_PUSH: SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS, - pfifo.cache1.dma_push_enabled); + d->pfifo.cache1.dma_push_enabled); SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_STATUS, - pfifo.cache1.dma_push_suspended); + d->pfifo.cache1.dma_push_suspended); SET_MASK(result, NV_PFIFO_CACHE1_DMA_PUSH_BUFFER, 1); /* buffer emoty */ break; case NV_PFIFO_CACHE1_DMA_STATE: SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE, - pfifo.cache1.method_nonincreasing); + d->pfifo.cache1.method_nonincreasing); SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD, - pfifo.cache1.method >> 2); + d->pfifo.cache1.method >> 2); SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL, - pfifo.cache1.subchannel); + d->pfifo.cache1.subchannel); SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT, - pfifo.cache1.method_count); + d->pfifo.cache1.method_count); SET_MASK(result, NV_PFIFO_CACHE1_DMA_STATE_ERROR, - pfifo.cache1.error); + d->pfifo.cache1.error); break; case NV_PFIFO_CACHE1_DMA_INSTANCE: SET_MASK(result, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK, - pfifo.cache1.dma_instance >> 4); + d->pfifo.cache1.dma_instance >> 4); break; case NV_PFIFO_CACHE1_DMA_PUT: - result = user.channel_control[pfifo.cache1.channel_id].dma_put; + result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_put; break; case NV_PFIFO_CACHE1_DMA_GET: - result = user.channel_control[pfifo.cache1.channel_id].dma_get; + result = d->user.channel_control[d->pfifo.cache1.channel_id].dma_get; break; case NV_PFIFO_CACHE1_DMA_SUBROUTINE: - result = pfifo.cache1.subroutine_return - | pfifo.cache1.subroutine_active; + result = d->pfifo.cache1.subroutine_return + | d->pfifo.cache1.subroutine_active; break; case NV_PFIFO_CACHE1_PULL0: { - std::lock_guard lk(pfifo.cache1.mutex); - result = pfifo.cache1.pull_enabled; + std::lock_guard lk(d->pfifo.cache1.mutex); + result = d->pfifo.cache1.pull_enabled; } break; case NV_PFIFO_CACHE1_ENGINE: { - std::lock_guard lk(pfifo.cache1.mutex); + std::lock_guard lk(d->pfifo.cache1.mutex); for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) { - result |= pfifo.cache1.bound_engines[i] << (i * 2); + result |= d->pfifo.cache1.bound_engines[i] << (i * 2); } } break; case NV_PFIFO_CACHE1_DMA_DCOUNT: - result = pfifo.cache1.dcount; + result = d->pfifo.cache1.dcount; break; case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW: - result = pfifo.cache1.get_jmp_shadow; + result = d->pfifo.cache1.get_jmp_shadow; break; case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW: - result = pfifo.cache1.rsvd_shadow; + result = d->pfifo.cache1.rsvd_shadow; break; case NV_PFIFO_CACHE1_DMA_DATA_SHADOW: - result = pfifo.cache1.data_shadow; + result = d->pfifo.cache1.data_shadow; break; default: DEVICE_READ32_REG(pfifo); // Was : DEBUG_READ32_UNHANDLED(PFIFO); @@ -99,82 +111,82 @@ DEVICE_WRITE32(PFIFO) { switch(addr) { case NV_PFIFO_INTR_0: - pfifo.pending_interrupts &= ~value; - update_irq(); + d->pfifo.pending_interrupts &= ~value; + update_irq(d); break; case NV_PFIFO_INTR_EN_0: - pfifo.enabled_interrupts = value; - update_irq(); + d->pfifo.enabled_interrupts = value; + update_irq(d); break; case NV_PFIFO_CACHE1_PUSH0: - pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS; + d->pfifo.cache1.push_enabled = value & NV_PFIFO_CACHE1_PUSH0_ACCESS; break; case NV_PFIFO_CACHE1_PUSH1: - pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID); - pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE); - assert(pfifo.cache1.channel_id < NV2A_NUM_CHANNELS); + d->pfifo.cache1.channel_id = GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_CHID); + d->pfifo.cache1.mode = (FifoMode)GET_MASK(value, NV_PFIFO_CACHE1_PUSH1_MODE); + assert(d->pfifo.cache1.channel_id < NV2A_NUM_CHANNELS); break; case NV_PFIFO_CACHE1_DMA_PUSH: - pfifo.cache1.dma_push_enabled = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS); - if (pfifo.cache1.dma_push_suspended && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) { - pfifo.cache1.dma_push_suspended = false; - pfifo_run_pusher(); + d->pfifo.cache1.dma_push_enabled = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_ACCESS); + if (d->pfifo.cache1.dma_push_suspended && !GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS)) { + d->pfifo.cache1.dma_push_suspended = false; + pfifo_run_pusher(d); } - pfifo.cache1.dma_push_suspended = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS); + d->pfifo.cache1.dma_push_suspended = GET_MASK(value, NV_PFIFO_CACHE1_DMA_PUSH_STATUS); break; case NV_PFIFO_CACHE1_DMA_STATE: - pfifo.cache1.method_nonincreasing = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); - pfifo.cache1.method = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; - pfifo.cache1.subchannel = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); - pfifo.cache1.method_count = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); - pfifo.cache1.error = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR); + d->pfifo.cache1.method_nonincreasing = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_TYPE); + d->pfifo.cache1.method = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD) << 2; + d->pfifo.cache1.subchannel = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_SUBCHANNEL); + d->pfifo.cache1.method_count = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_METHOD_COUNT); + d->pfifo.cache1.error = GET_MASK(value, NV_PFIFO_CACHE1_DMA_STATE_ERROR); break; case NV_PFIFO_CACHE1_DMA_INSTANCE: - pfifo.cache1.dma_instance = GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; + d->pfifo.cache1.dma_instance = GET_MASK(value, NV_PFIFO_CACHE1_DMA_INSTANCE_ADDRESS_MASK) << 4; break; case NV_PFIFO_CACHE1_DMA_PUT: - user.channel_control[pfifo.cache1.channel_id].dma_put = value; + d->user.channel_control[d->pfifo.cache1.channel_id].dma_put = value; break; case NV_PFIFO_CACHE1_DMA_GET: - user.channel_control[pfifo.cache1.channel_id].dma_get = value; + d->user.channel_control[d->pfifo.cache1.channel_id].dma_get = value; break; case NV_PFIFO_CACHE1_DMA_SUBROUTINE: - pfifo.cache1.subroutine_return = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET); - pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); + d->pfifo.cache1.subroutine_return = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_RETURN_OFFSET); + d->pfifo.cache1.subroutine_active = (value & NV_PFIFO_CACHE1_DMA_SUBROUTINE_STATE); break; case NV_PFIFO_CACHE1_PULL0: { - std::lock_guard lk(pfifo.cache1.mutex); + std::lock_guard lk(d->pfifo.cache1.mutex); if ((value & NV_PFIFO_CACHE1_PULL0_ACCESS) - && !pfifo.cache1.pull_enabled) { - pfifo.cache1.pull_enabled = true; + && !d->pfifo.cache1.pull_enabled) { + d->pfifo.cache1.pull_enabled = true; /* the puller thread should wake up */ - pfifo.cache1.cache_cond.notify_all(); + d->pfifo.cache1.cache_cond.notify_all(); } else if (!(value & NV_PFIFO_CACHE1_PULL0_ACCESS) - && pfifo.cache1.pull_enabled) { - pfifo.cache1.pull_enabled = false; + && d->pfifo.cache1.pull_enabled) { + d->pfifo.cache1.pull_enabled = false; } } break; case NV_PFIFO_CACHE1_ENGINE: { - std::lock_guard lk(pfifo.cache1.mutex); + std::lock_guard lk(d->pfifo.cache1.mutex); for (int i = 0; i < NV2A_NUM_SUBCHANNELS; i++) { - pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3); + d->pfifo.cache1.bound_engines[i] = (FIFOEngine)((value >> (i * 2)) & 3); } } break; case NV_PFIFO_CACHE1_DMA_DCOUNT: - pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE); + d->pfifo.cache1.dcount = (value & NV_PFIFO_CACHE1_DMA_DCOUNT_VALUE); break; case NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW: - pfifo.cache1.get_jmp_shadow = (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET); + d->pfifo.cache1.get_jmp_shadow = (value & NV_PFIFO_CACHE1_DMA_GET_JMP_SHADOW_OFFSET); break; case NV_PFIFO_CACHE1_DMA_RSVD_SHADOW: - pfifo.cache1.rsvd_shadow = value; + d->pfifo.cache1.rsvd_shadow = value; break; case NV_PFIFO_CACHE1_DMA_DATA_SHADOW: - pfifo.cache1.data_shadow = value; + d->pfifo.cache1.data_shadow = value; break; default: DEVICE_WRITE32_REG(pfifo); // Was : DEBUG_WRITE32_UNHANDLED(PFIFO); @@ -186,7 +198,7 @@ DEVICE_WRITE32(PFIFO) /* pusher should be fine to run from a mimo handler * whenever's it's convenient */ -static void pfifo_run_pusher() { +static void pfifo_run_pusher(NV2AState *d) { uint8_t channel_id; ChannelControl *control; Cache1State *state; @@ -196,16 +208,16 @@ static void pfifo_run_pusher() { uint32_t word; /* TODO: How is cache1 selected? */ - state = &pfifo.cache1; + state = &d->pfifo.cache1; channel_id = state->channel_id; - control = &user.channel_control[channel_id]; + control = &d->user.channel_control[channel_id]; if (!state->push_enabled) return; /* only handling DMA for now... */ /* Channel running DMA */ - uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE]; + uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE]; assert(channel_modes & (1 << channel_id)); assert(state->mode == FIFO_DMA); @@ -215,7 +227,7 @@ static void pfifo_run_pusher() { /* We're running so there should be no pending errors... */ assert(state->error == NV_PFIFO_CACHE1_DMA_STATE_ERROR_NONE); - dma = (uint8_t*)nv_dma_map(state->dma_instance, &dma_len); + dma = (uint8_t*)nv_dma_map(d, state->dma_instance, &dma_len); printf("DMA pusher: max 0x%08X, 0x%08X - 0x%08X\n", dma_len, control->dma_get, control->dma_put); @@ -321,14 +333,17 @@ static void pfifo_run_pusher() { state->dma_push_suspended = true; - pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER; - update_irq(); + d->pfifo.pending_interrupts |= NV_PFIFO_INTR_0_DMA_PUSHER; + update_irq(d); } } -static void* pfifo_puller_thread() +int pfifo_puller_thread(void *opaque) { - Cache1State *state = &pfifo.cache1; + CxbxSetThreadName("Cxbx NV2A FIFO"); + + NV2AState *d = (NV2AState *)opaque; + Cache1State *state = &d->pfifo.cache1; while (true) { // Scope the lock so that it automatically unlocks at tne end of this block @@ -352,7 +367,7 @@ static void* pfifo_puller_thread() if (command->method == 0) { // qemu_mutex_lock_iothread(); - RAMHTEntry entry = ramht_lookup(command->parameter); + RAMHTEntry entry = ramht_lookup(d, command->parameter); assert(entry.valid); assert(entry.channel_id == state->channel_id); @@ -360,9 +375,9 @@ static void* pfifo_puller_thread() switch (entry.engine) { case ENGINE_GRAPHICS: - pgraph_context_switch(entry.channel_id); - pgraph_wait_fifo_access(); - pgraph_method(command->subchannel, 0, entry.instance); + pgraph_context_switch(d, entry.channel_id); + pgraph_wait_fifo_access(d); + pgraph_method(d, command->subchannel, 0, entry.instance); break; default: assert(false); @@ -370,7 +385,7 @@ static void* pfifo_puller_thread() } /* the engine is bound to the subchannel */ - std::lock_guard lk(pfifo.cache1.mutex); + std::lock_guard lk(state->mutex); state->bound_engines[command->subchannel] = entry.engine; state->last_engine = entry.engine; } else if (command->method >= 0x100) { @@ -382,7 +397,7 @@ static void* pfifo_puller_thread() * TODO: Check this range is correct for the nv2a */ if (command->method >= 0x180 && command->method < 0x200) { //qemu_mutex_lock_iothread(); - RAMHTEntry entry = ramht_lookup(parameter); + RAMHTEntry entry = ramht_lookup(d, parameter); assert(entry.valid); assert(entry.channel_id == state->channel_id); parameter = entry.instance; @@ -395,8 +410,8 @@ static void* pfifo_puller_thread() switch (engine) { case ENGINE_GRAPHICS: - pgraph_wait_fifo_access(); - pgraph_method(command->subchannel, command->method, parameter); + pgraph_wait_fifo_access(d); + pgraph_method(d, command->subchannel, command->method, parameter); break; default: assert(false); @@ -412,12 +427,12 @@ static void* pfifo_puller_thread() } } - return NULL; + return 0; } -static uint32_t ramht_hash(uint32_t handle) +static uint32_t ramht_hash(NV2AState *d, uint32_t handle) { - unsigned int ramht_size = 1 << (GET_MASK(pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); + unsigned int ramht_size = 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); /* XXX: Think this is different to what nouveau calculates... */ unsigned int bits = ffs(ramht_size) - 2; @@ -427,24 +442,23 @@ static uint32_t ramht_hash(uint32_t handle) hash ^= (handle & ((1 << bits) - 1)); handle >>= bits; } - hash ^= pfifo.cache1.channel_id << (bits - 4); + hash ^= d->pfifo.cache1.channel_id << (bits - 4); return hash; } - -static RAMHTEntry ramht_lookup(uint32_t handle) +static RAMHTEntry ramht_lookup(NV2AState *d, uint32_t handle) { - unsigned int ramht_size = 1 << (GET_MASK(pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); + unsigned int ramht_size = 1 << (GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_SIZE_MASK) + 12); - uint32_t hash = ramht_hash(handle); + uint32_t hash = ramht_hash(d, handle); assert(hash * 8 < ramht_size); uint32_t ramht_address = - GET_MASK(pfifo.regs[NV_PFIFO_RAMHT], + GET_MASK(d->pfifo.regs[NV_PFIFO_RAMHT], NV_PFIFO_RAMHT_BASE_ADDRESS_MASK) << 12; - uint8_t *entry_ptr = (uint8_t*)(pramin.memory + ramht_address + hash * 8); + uint8_t *entry_ptr = d->pramin.ramin_ptr + ramht_address + hash * 8; uint32_t entry_handle = ldl_le_p((uint32_t*)entry_ptr); uint32_t entry_context = ldl_le_p((uint32_t*)(entry_ptr + 4)); diff --git a/src/devices/video/EmuNV2A_PGRAPH.cpp b/src/devices/video/EmuNV2A_PGRAPH.cpp index d75c4fe9f..ef430f06d 100644 --- a/src/devices/video/EmuNV2A_PGRAPH.cpp +++ b/src/devices/video/EmuNV2A_PGRAPH.cpp @@ -1,39 +1,82 @@ +static void pgraph_context_switch(NV2AState *d, unsigned int channel_id); +static void pgraph_set_context_user(NV2AState *d, uint32_t val); +//static void pgraph_wait_fifo_access(NV2AState *d); +static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); +//static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg, unsigned int attr); +//static void pgraph_finish_inline_buffer_vertex(PGRAPHState *pg); +//static void pgraph_shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); +//static void pgraph_bind_shaders(PGRAPHState *pg); +//static bool pgraph_framebuffer_dirty(PGRAPHState *pg); +static bool pgraph_color_write_enabled(PGRAPHState *pg); +static bool pgraph_zeta_write_enabled(PGRAPHState *pg); +//static void pgraph_set_surface_dirty(PGRAPHState *pg, bool color, bool zeta); +//static void pgraph_update_surface_part(NV2AState *d, bool upload, bool color); +static void pgraph_update_surface(NV2AState *d, bool upload, bool color_write, bool zeta_write); +//static void pgraph_bind_textures(NV2AState *d); +//static void pgraph_apply_anti_aliasing_factor(PGRAPHState *pg, unsigned int *width, unsigned int *height); +//static void pgraph_get_surface_dimensions(PGRAPHState *pg, unsigned int *width, unsigned int *height); +//static void pgraph_update_memory_buffer(NV2AState *d, hwaddr addr, hwaddr size, bool f); +//static void pgraph_bind_vertex_attributes(NV2AState *d, unsigned int num_elements, bool inline_data, unsigned int inline_stride); +//static unsigned int pgraph_bind_inline_array(NV2AState *d); +static void load_graphics_object(NV2AState *d, hwaddr instance_address, GraphicsObject *obj); +static GraphicsObject* lookup_graphics_object(PGRAPHState *s, hwaddr instance_address); +//static float convert_f16_to_float(uint16_t f16); +//static float convert_f24_to_float(uint32_t f24); +//static uint8_t cliptobyte(int x); +//static void convert_yuy2_to_rgb(const uint8_t *line, unsigned int ix, uint8_t *r, uint8_t *g, uint8_t* b); +//static uint8_t* convert_texture_data(const TextureShape s, const uint8_t *data, const uint8_t *palette_data, unsigned int width, unsigned int height, unsigned int depth, unsigned int row_pitch, unsigned int slice_pitch); +//static void upload_gl_texture(GLenum gl_target, const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +//static TextureBinding* generate_texture(const TextureShape s, const uint8_t *texture_data, const uint8_t *palette_data); +//static guint texture_key_hash(gconstpointer key); +//static gboolean texture_key_equal(gconstpointer a, gconstpointer b); +//static gpointer texture_key_retrieve(gpointer key, gpointer user_data, GError **error); +//static void texture_key_destroy(gpointer data); +//static void texture_binding_destroy(gpointer data); +//static guint shader_hash(gconstpointer key); +//static gboolean shader_equal(gconstpointer a, gconstpointer b); +static unsigned int kelvin_map_stencil_op(uint32_t parameter); +static unsigned int kelvin_map_polygon_mode(uint32_t parameter); +static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel); +static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter); +//static uint64_t fnv_hash(const uint8_t *data, size_t len); +//static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples); + DEVICE_READ32(PGRAPH) { - std::lock_guard lk(pgraph.mutex); + std::lock_guard lk(d->pgraph.mutex); DEVICE_READ32_SWITCH() { case NV_PGRAPH_INTR: - result = pgraph.pending_interrupts; + result = d->pgraph.pending_interrupts; break; case NV_PGRAPH_INTR_EN: - result = pgraph.enabled_interrupts; + result = d->pgraph.enabled_interrupts; break; case NV_PGRAPH_NSOURCE: - result = pgraph.notify_source; + result = d->pgraph.notify_source; break; case NV_PGRAPH_CTX_USER: - SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D, pgraph.context[pgraph.channel_id].channel_3d); + SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D, d->pgraph.context[d->pgraph.channel_id].channel_3d); SET_MASK(result, NV_PGRAPH_CTX_USER_CHANNEL_3D_VALID, 1); - SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH, pgraph.context[pgraph.channel_id].subchannel << 13); - SET_MASK(result, NV_PGRAPH_CTX_USER_CHID, pgraph.channel_id); + SET_MASK(result, NV_PGRAPH_CTX_USER_SUBCH, d->pgraph.context[d->pgraph.channel_id].subchannel << 13); + SET_MASK(result, NV_PGRAPH_CTX_USER_CHID, d->pgraph.channel_id); break; case NV_PGRAPH_TRAPPED_ADDR: - SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_CHID, pgraph.trapped_channel_id); - SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_SUBCH, pgraph.trapped_subchannel); - SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_MTHD, pgraph.trapped_method); + SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_CHID, d->pgraph.trapped_channel_id); + SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_SUBCH, d->pgraph.trapped_subchannel); + SET_MASK(result, NV_PGRAPH_TRAPPED_ADDR_MTHD, d->pgraph.trapped_method); break; case NV_PGRAPH_TRAPPED_DATA_LOW: - result = pgraph.trapped_data[0]; + result = d->pgraph.trapped_data[0]; break; case NV_PGRAPH_FIFO: - SET_MASK(result, NV_PGRAPH_FIFO_ACCESS, pgraph.fifo_access); + SET_MASK(result, NV_PGRAPH_FIFO_ACCESS, d->pgraph.fifo_access); break; case NV_PGRAPH_CHANNEL_CTX_TABLE: - result = pgraph.context_table >> 4; + result = d->pgraph.context_table >> 4; break; case NV_PGRAPH_CHANNEL_CTX_POINTER: - result = pgraph.context_address >> 4; + result = d->pgraph.context_address >> 4; break; default: DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH); @@ -42,66 +85,66 @@ DEVICE_READ32(PGRAPH) DEVICE_READ32_END(PGRAPH); } -static void pgraph_set_context_user(uint32_t value) +static void pgraph_set_context_user(NV2AState *d, uint32_t value) { - pgraph.channel_id = (value & NV_PGRAPH_CTX_USER_CHID) >> 24; - pgraph.context[pgraph.channel_id].channel_3d = GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D); - pgraph.context[pgraph.channel_id].subchannel = GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH); + d->pgraph.channel_id = (value & NV_PGRAPH_CTX_USER_CHID) >> 24; + d->pgraph.context[d->pgraph.channel_id].channel_3d = GET_MASK(value, NV_PGRAPH_CTX_USER_CHANNEL_3D); + d->pgraph.context[d->pgraph.channel_id].subchannel = GET_MASK(value, NV_PGRAPH_CTX_USER_SUBCH); } DEVICE_WRITE32(PGRAPH) { - std::lock_guard lk(pgraph.mutex); + std::lock_guard lk(d->pgraph.mutex); switch (addr) { case NV_PGRAPH_INTR: - pgraph.pending_interrupts &= ~value; - pgraph.interrupt_cond.notify_all(); + d->pgraph.pending_interrupts &= ~value; + d->pgraph.interrupt_cond.notify_all(); break; case NV_PGRAPH_INTR_EN: - pgraph.enabled_interrupts = value; + d->pgraph.enabled_interrupts = value; break; case NV_PGRAPH_CTX_CONTROL: - pgraph.channel_valid = (value & NV_PGRAPH_CTX_CONTROL_CHID); + d->pgraph.channel_valid = (value & NV_PGRAPH_CTX_CONTROL_CHID); break; case NV_PGRAPH_CTX_USER: - pgraph_set_context_user(value); + pgraph_set_context_user(d, value); break; case NV_PGRAPH_INCREMENT: if (value & NV_PGRAPH_INCREMENT_READ_3D) { - SET_MASK(pgraph.regs[NV_PGRAPH_SURFACE], + SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D, - (GET_MASK(pgraph.regs[NV_PGRAPH_SURFACE], + (GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D) + 1) - % GET_MASK(pgraph.regs[NV_PGRAPH_SURFACE], + % GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D)); - pgraph.flip_3d.notify_all(); + d->pgraph.flip_3d.notify_all(); } break; case NV_PGRAPH_FIFO: - pgraph.fifo_access = GET_MASK(value, NV_PGRAPH_FIFO_ACCESS); - pgraph.fifo_access_cond.notify_all(); + d->pgraph.fifo_access = GET_MASK(value, NV_PGRAPH_FIFO_ACCESS); + d->pgraph.fifo_access_cond.notify_all(); break; case NV_PGRAPH_CHANNEL_CTX_TABLE: - pgraph.context_table = (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4; + d->pgraph.context_table = (value & NV_PGRAPH_CHANNEL_CTX_TABLE_INST) << 4; break; case NV_PGRAPH_CHANNEL_CTX_POINTER: - pgraph.context_address = + d->pgraph.context_address = (value & NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; break; case NV_PGRAPH_CHANNEL_CTX_TRIGGER: if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { printf("PGRAPH: read channel %d context from %0x08X\n", - pgraph.channel_id, pgraph.context_address); + d->pgraph.channel_id, d->pgraph.context_address); - uint8_t *context_ptr = (uint8_t*)(pramin.memory + pgraph.context_address); + uint8_t *context_ptr = d->pramin.ramin_ptr + d->pgraph.context_address; uint32_t context_user = ldl_le_p((uint32_t*)context_ptr); printf(" - CTX_USER = 0x%x\n", context_user); - pgraph_set_context_user(context_user); + pgraph_set_context_user(d, context_user); } if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) { /* do stuff ... */ @@ -115,3 +158,1051 @@ DEVICE_WRITE32(PGRAPH) DEVICE_WRITE32_END(PGRAPH); } + +static void pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method, uint32_t parameter) +{ + std::lock_guard lk(d->pgraph.mutex); + +// int i; + GraphicsSubchannel *subchannel_data; + GraphicsObject *object; + + unsigned int slot; + + PGRAPHState *pg = &d->pgraph; + + assert(pg->channel_valid); + subchannel_data = &pg->subchannel_data[subchannel]; + object = &subchannel_data->object; + + ContextSurfaces2DState *context_surfaces_2d = &object->data.context_surfaces_2d; + ImageBlitState *image_blit = &object->data.image_blit; + KelvinState *kelvin = &object->data.kelvin; + + pgraph_method_log(subchannel, object->graphics_class, method, parameter); + + if (method == NV_SET_OBJECT) { + subchannel_data->object_instance = parameter; + + //qemu_mutex_lock_iothread(); + load_graphics_object(d, parameter, object); + //qemu_mutex_unlock_iothread(); + return; + } + + switch (object->graphics_class) { + case NV_CONTEXT_SURFACES_2D: { + switch (method) { + case NV062_SET_CONTEXT_DMA_IMAGE_SOURCE: + context_surfaces_2d->dma_image_source = parameter; + break; + case NV062_SET_CONTEXT_DMA_IMAGE_DESTIN: + context_surfaces_2d->dma_image_dest = parameter; + break; + case NV062_SET_COLOR_FORMAT: + context_surfaces_2d->color_format = parameter; + break; + case NV062_SET_PITCH: + context_surfaces_2d->source_pitch = parameter & 0xFFFF; + context_surfaces_2d->dest_pitch = parameter >> 16; + break; + case NV062_SET_OFFSET_SOURCE: + context_surfaces_2d->source_offset = parameter & 0x07FFFFFF; + break; + case NV062_SET_OFFSET_DESTIN: + context_surfaces_2d->dest_offset = parameter & 0x07FFFFFF; + break; + default: + EmuWarning("EmuNV2A: Unknown NV_CONTEXT_SURFACES_2D Method: 0x%08X\n", method); + } + + break; + } + + case NV_IMAGE_BLIT: { + switch (method) { + case NV09F_SET_CONTEXT_SURFACES: + image_blit->context_surfaces = parameter; + break; + case NV09F_SET_OPERATION: + image_blit->operation = parameter; + break; + case NV09F_CONTROL_POINT_IN: + image_blit->in_x = parameter & 0xFFFF; + image_blit->in_y = parameter >> 16; + break; + case NV09F_CONTROL_POINT_OUT: + image_blit->out_x = parameter & 0xFFFF; + image_blit->out_y = parameter >> 16; + break; + case NV09F_SIZE: + image_blit->width = parameter & 0xFFFF; + image_blit->height = parameter >> 16; + + /* I guess this kicks it off? */ + if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) { + + printf("NV09F_SET_OPERATION_SRCCOPY\n"); + + GraphicsObject *context_surfaces_obj = lookup_graphics_object(pg, image_blit->context_surfaces); + assert(context_surfaces_obj); + assert(context_surfaces_obj->graphics_class == NV_CONTEXT_SURFACES_2D); + + ContextSurfaces2DState *context_surfaces = &context_surfaces_obj->data.context_surfaces_2d; + + unsigned int bytes_per_pixel; + switch (context_surfaces->color_format) { + case NV062_SET_COLOR_FORMAT_LE_Y8: + bytes_per_pixel = 1; + break; + case NV062_SET_COLOR_FORMAT_LE_R5G6B5: + bytes_per_pixel = 2; + break; + case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8: + bytes_per_pixel = 4; + break; + default: + printf("Unknown blit surface format: 0x%x\n", context_surfaces->color_format); + assert(false); + break; + } + + xbaddr source_dma_len, dest_dma_len; + uint8_t *source, *dest; + + source = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_source, &source_dma_len); + assert(context_surfaces->source_offset < source_dma_len); + source += context_surfaces->source_offset; + + dest = (uint8_t*)nv_dma_map(d, context_surfaces->dma_image_dest, &dest_dma_len); + assert(context_surfaces->dest_offset < dest_dma_len); + dest += context_surfaces->dest_offset; + + printf(" - 0x%tx -> 0x%tx\n", source - MM_SYSTEM_PHYSICAL_MAP,dest - MM_SYSTEM_PHYSICAL_MAP); + + unsigned int y; + for (y = 0; yheight; y++) { + uint8_t *source_row = source + + (image_blit->in_y + y) * context_surfaces->source_pitch + + image_blit->in_x * bytes_per_pixel; + + uint8_t *dest_row = dest + + (image_blit->out_y + y) * context_surfaces->dest_pitch + + image_blit->out_x * bytes_per_pixel; + + memmove(dest_row, source_row, + image_blit->width * bytes_per_pixel); + } + } + else { + assert(false); + } + + break; + default: + EmuWarning("EmuNV2A: Unknown NV_IMAGE_BLIT Method: 0x%08X\n", method); + } + break; + } + + case NV_KELVIN_PRIMITIVE: { + switch (method) { + case NV097_SET_CONTEXT_DMA_NOTIFIES: + kelvin->dma_notifies = parameter; + break; + case NV097_SET_CONTEXT_DMA_A: + pg->dma_a = parameter; + break; + case NV097_SET_CONTEXT_DMA_B: + pg->dma_b = parameter; + break; + case NV097_SET_CONTEXT_DMA_STATE: + kelvin->dma_state = parameter; + break; + case NV097_SET_CONTEXT_DMA_COLOR: + /* try to get any straggling draws in before the surface's changed :/ */ + pgraph_update_surface(d, false, true, true); + + pg->dma_color = parameter; + break; + case NV097_SET_CONTEXT_DMA_ZETA: + pg->dma_zeta = parameter; + break; + case NV097_SET_CONTEXT_DMA_VERTEX_A: + pg->dma_vertex_a = parameter; + break; + case NV097_SET_CONTEXT_DMA_VERTEX_B: + pg->dma_vertex_b = parameter; + break; + case NV097_SET_CONTEXT_DMA_SEMAPHORE: + kelvin->dma_semaphore = parameter; + break; + case NV097_SET_CONTEXT_DMA_REPORT: + pg->dma_report = parameter; + break; + case NV097_SET_SURFACE_CLIP_HORIZONTAL: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.clip_x = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_X); + pg->surface_shape.clip_width = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_HORIZONTAL_WIDTH); + break; + case NV097_SET_SURFACE_CLIP_VERTICAL: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.clip_y = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_Y); + pg->surface_shape.clip_height = + GET_MASK(parameter, NV097_SET_SURFACE_CLIP_VERTICAL_HEIGHT); + break; + case NV097_SET_SURFACE_FORMAT: + pgraph_update_surface(d, false, true, true); + + pg->surface_shape.color_format = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_COLOR); + pg->surface_shape.zeta_format = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ZETA); + pg->surface_type = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_TYPE); + pg->surface_shape.anti_aliasing = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_ANTI_ALIASING); + pg->surface_shape.log_width = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_WIDTH); + pg->surface_shape.log_height = + GET_MASK(parameter, NV097_SET_SURFACE_FORMAT_HEIGHT); + break; + case NV097_SET_SURFACE_PITCH: + pgraph_update_surface(d, false, true, true); + + pg->surface_color.pitch = + GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR); + pg->surface_zeta.pitch = + GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA); + break; + case NV097_SET_SURFACE_COLOR_OFFSET: + pgraph_update_surface(d, false, true, true); + + pg->surface_color.offset = parameter; + break; + case NV097_SET_SURFACE_ZETA_OFFSET: + pgraph_update_surface(d, false, true, true); + + pg->surface_zeta.offset = parameter; + break; + case NV097_SET_COMBINER_SPECULAR_FOG_CW0: + pg->regs[NV_PGRAPH_COMBINESPECFOG0] = parameter; + break; + case NV097_SET_COMBINER_SPECULAR_FOG_CW1: + pg->regs[NV_PGRAPH_COMBINESPECFOG1] = parameter; + break; + CASE_4(NV097_SET_TEXTURE_ADDRESS, 64): + slot = (method - NV097_SET_TEXTURE_ADDRESS) / 64; + pg->regs[NV_PGRAPH_TEXADDRESS0 + slot * 4] = parameter; + break; + case NV097_SET_CONTROL0: { + pgraph_update_surface(d, false, true, true); + + bool stencil_write_enable = + parameter & NV097_SET_CONTROL0_STENCIL_WRITE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE, + stencil_write_enable); + + uint32_t z_format = GET_MASK(parameter, NV097_SET_CONTROL0_Z_FORMAT); + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_Z_FORMAT, z_format); + + bool z_perspective = + parameter & NV097_SET_CONTROL0_Z_PERSPECTIVE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_Z_PERSPECTIVE_ENABLE, + z_perspective); + break; + } + + case NV097_SET_FOG_MODE: { + /* FIXME: There is also NV_PGRAPH_CSV0_D_FOG_MODE */ + unsigned int mode; + switch (parameter) { + case NV097_SET_FOG_MODE_V_LINEAR: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR; break; + case NV097_SET_FOG_MODE_V_EXP: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP; break; + case NV097_SET_FOG_MODE_V_EXP2: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2; break; + case NV097_SET_FOG_MODE_V_EXP_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP_ABS; break; + case NV097_SET_FOG_MODE_V_EXP2_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_EXP2_ABS; break; + case NV097_SET_FOG_MODE_V_LINEAR_ABS: + mode = NV_PGRAPH_CONTROL_3_FOG_MODE_LINEAR_ABS; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOG_MODE, + mode); + break; + } + case NV097_SET_FOG_GEN_MODE: { + unsigned int mode; + switch (parameter) { + case NV097_SET_FOG_GEN_MODE_V_SPEC_ALPHA: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_SPEC_ALPHA; break; + case NV097_SET_FOG_GEN_MODE_V_RADIAL: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_RADIAL; break; + case NV097_SET_FOG_GEN_MODE_V_PLANAR: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_PLANAR; break; + case NV097_SET_FOG_GEN_MODE_V_ABS_PLANAR: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_ABS_PLANAR; break; + case NV097_SET_FOG_GEN_MODE_V_FOG_X: + mode = NV_PGRAPH_CSV0_D_FOGGENMODE_FOG_X; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGGENMODE, mode); + break; + } + case NV097_SET_FOG_ENABLE: + /* + FIXME: There is also: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_FOGENABLE, + parameter); + */ + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_3], NV_PGRAPH_CONTROL_3_FOGENABLE, + parameter); + break; + case NV097_SET_FOG_COLOR: { + /* PGRAPH channels are ARGB, parameter channels are ABGR */ + uint8_t red = GET_MASK(parameter, NV097_SET_FOG_COLOR_RED); + uint8_t green = GET_MASK(parameter, NV097_SET_FOG_COLOR_GREEN); + uint8_t blue = GET_MASK(parameter, NV097_SET_FOG_COLOR_BLUE); + uint8_t alpha = GET_MASK(parameter, NV097_SET_FOG_COLOR_ALPHA); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_RED, red); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_GREEN, green); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_BLUE, blue); + SET_MASK(pg->regs[NV_PGRAPH_FOGCOLOR], NV_PGRAPH_FOGCOLOR_ALPHA, alpha); + break; + } + case NV097_SET_ALPHA_TEST_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHATESTENABLE, parameter); + break; + case NV097_SET_BLEND_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EN, parameter); + break; + case NV097_SET_CULL_FACE_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_CULLENABLE, + parameter); + break; + case NV097_SET_DEPTH_TEST_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZENABLE, + parameter); + break; + case NV097_SET_DITHER_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_DITHERENABLE, parameter); + break; + case NV097_SET_LIGHTING_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], NV_PGRAPH_CSV0_C_LIGHTING, + parameter); + break; + case NV097_SET_SKIN_MODE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_SKIN, + parameter); + break; + case NV097_SET_STENCIL_TEST_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_TEST_ENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_POINT_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETPOINTENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_LINE_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETLINEENABLE, parameter); + break; + case NV097_SET_POLY_OFFSET_FILL_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_POFFSETFILLENABLE, parameter); + break; + case NV097_SET_ALPHA_FUNC: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAFUNC, parameter & 0xF); + break; + case NV097_SET_ALPHA_REF: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHAREF, parameter); + break; + case NV097_SET_BLEND_FUNC_SFACTOR: { + unsigned int factor; + switch (parameter) { + case NV097_SET_BLEND_FUNC_SFACTOR_V_ZERO: + factor = NV_PGRAPH_BLEND_SFACTOR_ZERO; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_DST_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_DST_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_SRC_ALPHA_SATURATE: + factor = NV_PGRAPH_BLEND_SFACTOR_SRC_ALPHA_SATURATE; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_CONSTANT_ALPHA; break; + case NV097_SET_BLEND_FUNC_SFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_SFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; + default: + fprintf(stderr, "Unknown blend source factor: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_SFACTOR, factor); + + break; + } + + case NV097_SET_BLEND_FUNC_DFACTOR: { + unsigned int factor; + switch (parameter) { + case NV097_SET_BLEND_FUNC_DFACTOR_V_ZERO: + factor = NV_PGRAPH_BLEND_DFACTOR_ZERO; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_SRC_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_SRC_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_DST_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_DST_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_DST_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_SRC_ALPHA_SATURATE: + factor = NV_PGRAPH_BLEND_DFACTOR_SRC_ALPHA_SATURATE; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_COLOR: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_COLOR; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_CONSTANT_ALPHA; break; + case NV097_SET_BLEND_FUNC_DFACTOR_V_ONE_MINUS_CONSTANT_ALPHA: + factor = NV_PGRAPH_BLEND_DFACTOR_ONE_MINUS_CONSTANT_ALPHA; break; + default: + fprintf(stderr, "Unknown blend destination factor: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_DFACTOR, factor); + + break; + } + + case NV097_SET_BLEND_COLOR: + pg->regs[NV_PGRAPH_BLENDCOLOR] = parameter; + break; + + case NV097_SET_BLEND_EQUATION: { + unsigned int equation; + switch (parameter) { + case NV097_SET_BLEND_EQUATION_V_FUNC_SUBTRACT: + equation = 0; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT: + equation = 1; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_ADD: + equation = 2; break; + case NV097_SET_BLEND_EQUATION_V_MIN: + equation = 3; break; + case NV097_SET_BLEND_EQUATION_V_MAX: + equation = 4; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_REVERSE_SUBTRACT_SIGNED: + equation = 5; break; + case NV097_SET_BLEND_EQUATION_V_FUNC_ADD_SIGNED: + equation = 6; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_BLEND], NV_PGRAPH_BLEND_EQN, equation); + + break; + } + + case NV097_SET_DEPTH_FUNC: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], NV_PGRAPH_CONTROL_0_ZFUNC, + parameter & 0xF); + break; + + case NV097_SET_COLOR_MASK: { + pg->surface_color.write_enabled_cache |= pgraph_color_write_enabled(pg); + + bool alpha = parameter & NV097_SET_COLOR_MASK_ALPHA_WRITE_ENABLE; + bool red = parameter & NV097_SET_COLOR_MASK_RED_WRITE_ENABLE; + bool green = parameter & NV097_SET_COLOR_MASK_GREEN_WRITE_ENABLE; + bool blue = parameter & NV097_SET_COLOR_MASK_BLUE_WRITE_ENABLE; + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE, alpha); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE, red); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE, green); + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE, blue); + break; + } + case NV097_SET_DEPTH_MASK: + pg->surface_zeta.write_enabled_cache |= pgraph_zeta_write_enabled(pg); + + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_0], + NV_PGRAPH_CONTROL_0_ZWRITEENABLE, parameter); + break; + case NV097_SET_STENCIL_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_WRITE, parameter); + break; + case NV097_SET_STENCIL_FUNC: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_FUNC, parameter & 0xF); + break; + case NV097_SET_STENCIL_FUNC_REF: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_REF, parameter); + break; + case NV097_SET_STENCIL_FUNC_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_1], + NV_PGRAPH_CONTROL_1_STENCIL_MASK_READ, parameter); + break; + case NV097_SET_STENCIL_OP_FAIL: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_FAIL, + kelvin_map_stencil_op(parameter)); + break; + case NV097_SET_STENCIL_OP_ZFAIL: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZFAIL, + kelvin_map_stencil_op(parameter)); + break; + case NV097_SET_STENCIL_OP_ZPASS: + SET_MASK(pg->regs[NV_PGRAPH_CONTROL_2], + NV_PGRAPH_CONTROL_2_STENCIL_OP_ZPASS, + kelvin_map_stencil_op(parameter)); + break; + + case NV097_SET_POLYGON_OFFSET_SCALE_FACTOR: + pg->regs[NV_PGRAPH_ZOFFSETFACTOR] = parameter; + break; + case NV097_SET_POLYGON_OFFSET_BIAS: + pg->regs[NV_PGRAPH_ZOFFSETBIAS] = parameter; + break; + case NV097_SET_FRONT_POLYGON_MODE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACEMODE, + kelvin_map_polygon_mode(parameter)); + break; + case NV097_SET_BACK_POLYGON_MODE: + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_BACKFACEMODE, + kelvin_map_polygon_mode(parameter)); + break; + case NV097_SET_CLIP_MIN: + pg->regs[NV_PGRAPH_ZCLIPMIN] = parameter; + break; + case NV097_SET_CLIP_MAX: + pg->regs[NV_PGRAPH_ZCLIPMAX] = parameter; + break; + case NV097_SET_CULL_FACE: { + unsigned int face; + switch (parameter) { + case NV097_SET_CULL_FACE_V_FRONT: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT; break; + case NV097_SET_CULL_FACE_V_BACK: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_BACK; break; + case NV097_SET_CULL_FACE_V_FRONT_AND_BACK: + face = NV_PGRAPH_SETUPRASTER_CULLCTRL_FRONT_AND_BACK; break; + default: + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_CULLCTRL, + face); + break; + } + case NV097_SET_FRONT_FACE: { + bool ccw; + switch (parameter) { + case NV097_SET_FRONT_FACE_V_CW: + ccw = false; break; + case NV097_SET_FRONT_FACE_V_CCW: + ccw = true; break; + default: + fprintf(stderr, "Unknown front face: 0x%x\n", parameter); + assert(false); + break; + } + SET_MASK(pg->regs[NV_PGRAPH_SETUPRASTER], + NV_PGRAPH_SETUPRASTER_FRONTFACE, + ccw ? 1 : 0); + break; + } + case NV097_SET_NORMALIZATION_ENABLE: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_C], + NV_PGRAPH_CSV0_C_NORMALIZATION_ENABLE, + parameter); + break; + + case NV097_SET_LIGHT_ENABLE_MASK: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], + NV_PGRAPH_CSV0_D_LIGHTS, + parameter); + break; + + CASE_4(NV097_SET_TEXGEN_S, 16) : { + slot = (method - NV097_SET_TEXGEN_S) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_S + : NV_PGRAPH_CSV1_A_T0_S; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 0)); + break; + } + CASE_4(NV097_SET_TEXGEN_T, 16) : { + slot = (method - NV097_SET_TEXGEN_T) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_T + : NV_PGRAPH_CSV1_A_T0_T; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 1)); + break; + } + CASE_4(NV097_SET_TEXGEN_R, 16) : { + slot = (method - NV097_SET_TEXGEN_R) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_R + : NV_PGRAPH_CSV1_A_T0_R; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 2)); + break; + } + CASE_4(NV097_SET_TEXGEN_Q, 16) : { + slot = (method - NV097_SET_TEXGEN_Q) / 16; + unsigned int reg = (slot < 2) ? NV_PGRAPH_CSV1_A + : NV_PGRAPH_CSV1_B; + unsigned int mask = (slot % 2) ? NV_PGRAPH_CSV1_A_T1_Q + : NV_PGRAPH_CSV1_A_T0_Q; + SET_MASK(pg->regs[reg], mask, kelvin_map_texgen(parameter, 3)); + break; + } + CASE_4(NV097_SET_TEXTURE_MATRIX_ENABLE, 4) : + slot = (method - NV097_SET_TEXTURE_MATRIX_ENABLE) / 4; + pg->texture_matrix_enable[slot] = parameter; + break; + + case NV097_SET_TEXGEN_VIEW_MODEL: + SET_MASK(pg->regs[NV_PGRAPH_CSV0_D], NV_PGRAPH_CSV0_D_TEXGEN_REF, parameter); + break; + case NV097_SET_SEMAPHORE_OFFSET: + kelvin->semaphore_offset = parameter; + break; + case NV097_BACK_END_WRITE_SEMAPHORE_RELEASE: { + + pgraph_update_surface(d, false, true, true); + + //qemu_mutex_unlock(&d->pg->lock); + //qemu_mutex_lock_iothread(); + + xbaddr semaphore_dma_len; + uint8_t *semaphore_data = (uint8_t*)nv_dma_map(d, kelvin->dma_semaphore, + &semaphore_dma_len); + assert(kelvin->semaphore_offset < semaphore_dma_len); + semaphore_data += kelvin->semaphore_offset; + + stl_le_p((uint32_t*)semaphore_data, parameter); + + //qemu_mutex_lock(&d->pg->lock); + //qemu_mutex_unlock_iothread(); + + break; + } + default: + if (method >= NV097_SET_COMBINER_ALPHA_ICW && method <= NV097_SET_COMBINER_ALPHA_ICW + 28) { + slot = (method - NV097_SET_COMBINER_ALPHA_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAI0 + slot * 4] = parameter; + break; + } + + if (method >= NV097_SET_PROJECTION_MATRIX && method <= NV097_SET_PROJECTION_MATRIX + 0x3c) { + slot = (method - NV097_SET_PROJECTION_MATRIX) / 4; + // pg->projection_matrix[slot] = *(float*)¶meter; + unsigned int row = NV_IGRAPH_XF_XFCTX_PMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_MODEL_VIEW_MATRIX && method <= NV097_SET_MODEL_VIEW_MATRIX + 0xfc) { + slot = (method - NV097_SET_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_MMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_INVERSE_MODEL_VIEW_MATRIX && method <= NV097_SET_INVERSE_MODEL_VIEW_MATRIX + 0xfc) { + slot = (method - NV097_SET_INVERSE_MODEL_VIEW_MATRIX) / 4; + unsigned int matnum = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_IMMAT0 + matnum * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_COMPOSITE_MATRIX && method <= NV097_SET_COMPOSITE_MATRIX + 0x3c) { + slot = (method - NV097_SET_COMPOSITE_MATRIX) / 4; + unsigned int row = NV_IGRAPH_XF_XFCTX_CMAT0 + slot / 4; + pg->vsh_constants[row][slot % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_TEXTURE_MATRIX && method <= NV097_SET_TEXTURE_MATRIX + 0xfc) { + slot = (method - NV097_SET_TEXTURE_MATRIX) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_T0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_FOG_PARAMS && method <= NV097_SET_FOG_PARAMS + 8) { + slot = (method - NV097_SET_FOG_PARAMS) / 4; + if (slot < 2) { + pg->regs[NV_PGRAPH_FOGPARAM0 + slot * 4] = parameter; + } + else { + /* FIXME: No idea where slot = 2 is */ + } + + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FOG_K][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FOG_K] = true; + break; + } + + /* Handles NV097_SET_TEXGEN_PLANE_S,T,R,Q */ + if (method >= NV097_SET_TEXGEN_PLANE_S && method <= NV097_SET_TEXGEN_PLANE_S + 0xfc) { + slot = (method - NV097_SET_TEXGEN_PLANE_S) / 4; + unsigned int tex = slot / 16; + unsigned int entry = slot % 16; + unsigned int row = NV_IGRAPH_XF_XFCTX_TG0MAT + tex * 8 + entry / 4; + pg->vsh_constants[row][entry % 4] = parameter; + pg->vsh_constants_dirty[row] = true; + break; + } + + if (method >= NV097_SET_FOG_PLANE && method <= NV097_SET_FOG_PLANE + 12) { + slot = (method - NV097_SET_FOG_PLANE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_FOG][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_FOG] = true; + break; + } + + if (method >= NV097_SET_SCENE_AMBIENT_COLOR && method <= NV097_SET_SCENE_AMBIENT_COLOR + 8) { + slot = (method - NV097_SET_SCENE_AMBIENT_COLOR) / 4; + // ?? + pg->ltctxa[NV_IGRAPH_XF_LTCTXA_FR_AMB][slot] = parameter; + pg->ltctxa_dirty[NV_IGRAPH_XF_LTCTXA_FR_AMB] = true; + break; + } + + if (method >= NV097_SET_VIEWPORT_OFFSET && method <= NV097_SET_VIEWPORT_OFFSET + 12) { + slot = (method - NV097_SET_VIEWPORT_OFFSET) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPOFF][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPOFF] = true; + break; + } + + if (method >= NV097_SET_EYE_POSITION && method <= NV097_SET_EYE_POSITION + 12) { + slot = (method - NV097_SET_EYE_POSITION) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_EYEP][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_EYEP] = true; + break; + } + + if (method >= NV097_SET_COMBINER_FACTOR0 && method <= NV097_SET_COMBINER_FACTOR0 + 28) { + slot = (method - NV097_SET_COMBINER_FACTOR0) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR0 + slot * 4] = parameter; + break; + } + + if (method >= NV097_SET_COMBINER_FACTOR1 && method <= NV097_SET_COMBINER_FACTOR1 + 28) { + slot = (method - NV097_SET_COMBINER_FACTOR1) / 4; + pg->regs[NV_PGRAPH_COMBINEFACTOR1 + slot * 4] = parameter; + break; + } + + if (method >= NV097_SET_COMBINER_ALPHA_OCW && method <= NV097_SET_COMBINER_ALPHA_OCW + 28) { + slot = (method - NV097_SET_COMBINER_ALPHA_OCW) / 4; + pg->regs[NV_PGRAPH_COMBINEALPHAO0 + slot * 4] = parameter; + break; + } + + if (method >= NV097_SET_COMBINER_COLOR_ICW && method <= NV097_SET_COMBINER_COLOR_ICW + 28) { + slot = (method - NV097_SET_COMBINER_COLOR_ICW) / 4; + pg->regs[NV_PGRAPH_COMBINECOLORI0 + slot * 4] = parameter; + break; + } + + if (method >= NV097_SET_VIEWPORT_SCALE && method <= NV097_SET_VIEWPORT_SCALE + 12) { + slot = (method - NV097_SET_VIEWPORT_SCALE) / 4; + pg->vsh_constants[NV_IGRAPH_XF_XFCTX_VPSCL][slot] = parameter; + pg->vsh_constants_dirty[NV_IGRAPH_XF_XFCTX_VPSCL] = true; + break; + } + + EmuWarning("EmuNV2A: Unknown NV_KELVIN_PRIMITIVE Method: 0x%08X\n", method); + } + break; + } + + default: + EmuWarning("EmuNV2A: Unknown Graphics Class/Method 0x%08X/0x%08X\n", object->graphics_class, method); + break; + } +} + +static void pgraph_context_switch(NV2AState *d, unsigned int channel_id) +{ + bool valid = false; + + // Scope the lock so that it gets unlocked at end of this block + { + std::lock_guard lk(d->pgraph.mutex); + + valid = d->pgraph.channel_valid && d->pgraph.channel_id == channel_id; + if (!valid) { + d->pgraph.trapped_channel_id = channel_id; + } + } + + if (!valid) { + printf("puller needs to switch to ch %d\n", channel_id); + + //qemu_mutex_lock_iothread(); + d->pgraph.pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; + update_irq(d); + + std::unique_lock lk(d->pgraph.mutex); + //qemu_mutex_unlock_iothread(); + + while (d->pgraph.pending_interrupts & NV_PGRAPH_INTR_CONTEXT_SWITCH) { + d->pgraph.interrupt_cond.wait(lk); + } + } +} + +static void pgraph_wait_fifo_access(NV2AState *d) { + std::unique_lock lk(d->pgraph.mutex); + + while (!d->pgraph.fifo_access) { + d->pgraph.fifo_access_cond.wait(lk); + } +} + +static void pgraph_method_log(unsigned int subchannel, unsigned int graphics_class, unsigned int method, uint32_t parameter) { + static unsigned int last = 0; + static unsigned int count = 0; + + if (last == 0x1800 && method != last) { + printf("d->pgraph method (%d) 0x%08X * %d", subchannel, last, count); + } + if (method != 0x1800) { + const char* method_name = NULL; + unsigned int nmethod = 0; + switch (graphics_class) { + case NV_KELVIN_PRIMITIVE: + nmethod = method | (0x5c << 16); + break; + case NV_CONTEXT_SURFACES_2D: + nmethod = method | (0x6d << 16); + break; + default: + break; + } + /* + if (nmethod != 0 && nmethod < ARRAY_SIZE(nv2a_method_names)) { + method_name = nv2a_method_names[nmethod]; + } + if (method_name) { + printf("d->pgraph method (%d): %s (0x%x)\n", + subchannel, method_name, parameter); + } + else { + */ + printf("d->pgraph method (%d): 0x%x -> 0x%04x (0x%x)\n", + subchannel, graphics_class, method, parameter); + //} + + } + if (method == last) { count++; } + else { count = 0; } + last = method; +} + +static bool pgraph_color_write_enabled(PGRAPHState *pg) +{ + return pg->regs[NV_PGRAPH_CONTROL_0] & ( + NV_PGRAPH_CONTROL_0_ALPHA_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_RED_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_GREEN_WRITE_ENABLE + | NV_PGRAPH_CONTROL_0_BLUE_WRITE_ENABLE); +} + +static bool pgraph_zeta_write_enabled(PGRAPHState *pg) +{ + return pg->regs[NV_PGRAPH_CONTROL_0] & ( + NV_PGRAPH_CONTROL_0_ZWRITEENABLE + | NV_PGRAPH_CONTROL_0_STENCIL_WRITE_ENABLE); +} + +static void pgraph_update_surface(NV2AState *d, bool upload, + bool color_write, bool zeta_write) +{ + printf("TODO: pgraph_update_surface\n"); +} + +static void load_graphics_object(NV2AState *d, xbaddr instance_address, GraphicsObject *obj) +{ + uint8_t *obj_ptr; + uint32_t switch1, switch2, switch3; + + assert(instance_address < d->pramin.ramin_size); + obj_ptr = d->pramin.ramin_ptr + instance_address; + + switch1 = ldl_le_p((uint32_t*)obj_ptr); + switch2 = ldl_le_p((uint32_t*)(obj_ptr + 4)); + switch3 = ldl_le_p((uint32_t*)(obj_ptr + 8)); + + obj->graphics_class = switch1 & NV_PGRAPH_CTX_SWITCH1_GRCLASS; + + /* init graphics object */ + switch (obj->graphics_class) { + case NV_KELVIN_PRIMITIVE: + // kelvin->vertex_attributes[NV2A_VERTEX_ATTR_DIFFUSE].inline_value = 0xFFFFFFF; + break; + default: + break; + } +} + +static GraphicsObject* lookup_graphics_object(PGRAPHState *s,xbaddr instance_address) +{ + int i; + for (i = 0; isubchannel_data[i].object_instance == instance_address) { + return &s->subchannel_data[i].object; + } + } + return NULL; +} + +static unsigned int kelvin_map_stencil_op(uint32_t parameter) +{ + unsigned int op; + switch (parameter) { + case NV097_SET_STENCIL_OP_V_KEEP: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_KEEP; break; + case NV097_SET_STENCIL_OP_V_ZERO: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_ZERO; break; + case NV097_SET_STENCIL_OP_V_REPLACE: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_REPLACE; break; + case NV097_SET_STENCIL_OP_V_INCRSAT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCRSAT; break; + case NV097_SET_STENCIL_OP_V_DECRSAT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECRSAT; break; + case NV097_SET_STENCIL_OP_V_INVERT: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INVERT; break; + case NV097_SET_STENCIL_OP_V_INCR: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_INCR; break; + case NV097_SET_STENCIL_OP_V_DECR: + op = NV_PGRAPH_CONTROL_2_STENCIL_OP_V_DECR; break; + default: + assert(false); + break; + } + return op; +} + +static unsigned int kelvin_map_polygon_mode(uint32_t parameter) +{ + unsigned int mode; + switch (parameter) { + case NV097_SET_FRONT_POLYGON_MODE_V_POINT: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_POINT; break; + case NV097_SET_FRONT_POLYGON_MODE_V_LINE: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_LINE; break; + case NV097_SET_FRONT_POLYGON_MODE_V_FILL: + mode = NV_PGRAPH_SETUPRASTER_FRONTFACEMODE_FILL; break; + default: + assert(false); + break; + } + return mode; +} + +static unsigned int kelvin_map_texgen(uint32_t parameter, unsigned int channel) +{ + assert(channel < 4); + unsigned int texgen; + switch (parameter) { + case NV097_SET_TEXGEN_S_DISABLE: + texgen = NV_PGRAPH_CSV1_A_T0_S_DISABLE; break; + case NV097_SET_TEXGEN_S_EYE_LINEAR: + texgen = NV_PGRAPH_CSV1_A_T0_S_EYE_LINEAR; break; + case NV097_SET_TEXGEN_S_OBJECT_LINEAR: + texgen = NV_PGRAPH_CSV1_A_T0_S_OBJECT_LINEAR; break; + case NV097_SET_TEXGEN_S_SPHERE_MAP: + assert(channel < 2); + texgen = NV_PGRAPH_CSV1_A_T0_S_SPHERE_MAP; break; + case NV097_SET_TEXGEN_S_REFLECTION_MAP: + assert(channel < 3); + texgen = NV_PGRAPH_CSV1_A_T0_S_REFLECTION_MAP; break; + case NV097_SET_TEXGEN_S_NORMAL_MAP: + assert(channel < 3); + texgen = NV_PGRAPH_CSV1_A_T0_S_NORMAL_MAP; break; + default: + assert(false); + break; + } + return texgen; +} + diff --git a/src/devices/video/EmuNV2A_PMC.cpp b/src/devices/video/EmuNV2A_PMC.cpp index 95050ba32..a5dfaaf80 100644 --- a/src/devices/video/EmuNV2A_PMC.cpp +++ b/src/devices/video/EmuNV2A_PMC.cpp @@ -8,14 +8,14 @@ DEVICE_READ32(PMC) result = 0; // When read, returns 0 if in little-endian mode, 0x01000001 if in big-endian mode. break; case NV_PMC_INTR_0: // Shows which functional units have pending IRQ - result = pmc.pending_interrupts; + result = d->pmc.pending_interrupts; break; case NV_PMC_INTR_EN_0: // Selects which functional units can cause IRQs - result = pmc.enabled_interrupts; + result = d->pmc.enabled_interrupts; break; default: result = 0; - //DEVICE_READ32_REG(pmc); // Was : DEBUG_READ32_UNHANDLED(PMC); + //DEVICE_READ32_REG(PMC); // Was : DEBUG_READ32_UNHANDLED(PMC); break; } @@ -27,12 +27,12 @@ DEVICE_WRITE32(PMC) switch(addr) { case NV_PMC_INTR_0: /* the bits of the interrupts to clear are wrtten */ - pmc.pending_interrupts &= ~value; - update_irq(); + d->pmc.pending_interrupts &= ~value; + update_irq(d); break; case NV_PMC_INTR_EN_0: - pmc.enabled_interrupts = value; - update_irq(); + d->pmc.enabled_interrupts = value; + update_irq(d); break; default: diff --git a/src/devices/video/EmuNV2A_PRAMDAC.cpp b/src/devices/video/EmuNV2A_PRAMDAC.cpp index be91e6242..34ec5c60b 100644 --- a/src/devices/video/EmuNV2A_PRAMDAC.cpp +++ b/src/devices/video/EmuNV2A_PRAMDAC.cpp @@ -3,13 +3,13 @@ DEVICE_READ32(PRAMDAC) DEVICE_READ32_SWITCH() { case NV_PRAMDAC_NVPLL_COEFF: - result = pramdac.core_clock_coeff; + result = d->pramdac.core_clock_coeff; break; case NV_PRAMDAC_MPLL_COEFF: - result = pramdac.memory_clock_coeff; + result = d->pramdac.memory_clock_coeff; break; case NV_PRAMDAC_VPLL_COEFF: - result = pramdac.video_clock_coeff; + result = d->pramdac.video_clock_coeff; break; case NV_PRAMDAC_PLL_TEST_COUNTER: /* emulated PLLs locked instantly? */ @@ -36,26 +36,26 @@ DEVICE_WRITE32(PRAMDAC) uint32_t m, n, p; case NV_PRAMDAC_NVPLL_COEFF: - pramdac.core_clock_coeff = value; + d->pramdac.core_clock_coeff = value; m = value & NV_PRAMDAC_NVPLL_COEFF_MDIV; n = (value & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8; p = (value & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16; if (m == 0) { - pramdac.core_clock_freq = 0; + d->pramdac.core_clock_freq = 0; } else { - pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n) + d->pramdac.core_clock_freq = (NV2A_CRYSTAL_FREQ * n) / (1 << p) / m; } break; case NV_PRAMDAC_MPLL_COEFF: - pramdac.memory_clock_coeff = value; + d->pramdac.memory_clock_coeff = value; break; case NV_PRAMDAC_VPLL_COEFF: - pramdac.video_clock_coeff = value; + d->pramdac.video_clock_coeff = value; break; default: diff --git a/src/devices/video/EmuNV2A_PRMCIO.cpp b/src/devices/video/EmuNV2A_PRMCIO.cpp index 0505b4359..6283cf19b 100644 --- a/src/devices/video/EmuNV2A_PRMCIO.cpp +++ b/src/devices/video/EmuNV2A_PRMCIO.cpp @@ -4,13 +4,13 @@ DEVICE_READ32(PRMCIO) DEVICE_READ32_SWITCH() { case VGA_CRT_IM: case VGA_CRT_IC: - result = prmcio.cr_index; + result = d->prmcio.cr_index; break; case VGA_CRT_DM: case VGA_CRT_DC: - result = prmcio.cr[prmcio.cr_index]; + result = d->prmcio.cr[d->prmcio.cr_index]; - printf("vga: read CR%x = 0x%02x\n", prmcio.cr_index, result); + printf("vga: read CR%x = 0x%02x\n", d->prmcio.cr_index, result); break; default: DEBUG_READ32_UNHANDLED(PRMCIO); @@ -39,18 +39,18 @@ DEVICE_WRITE32(PRMCIO) // vga_ioport_write : case VGA_CRT_IM: case VGA_CRT_IC: - prmcio.cr_index = value; + d->prmcio.cr_index = value; break; case VGA_CRT_DM: case VGA_CRT_DC: - printf("vga: write CR%x = 0x%02x\n", prmcio.cr_index, value); + printf("vga: write CR%x = 0x%02x\n", d->prmcio.cr_index, value); /* handle CR0-7 protection */ - if ((prmcio.cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && - prmcio.cr_index <= VGA_CRTC_OVERFLOW) { + if ((d->prmcio.cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && + d->prmcio.cr_index <= VGA_CRTC_OVERFLOW) { /* can always write bit 4 of CR7 */ - if (prmcio.cr_index == VGA_CRTC_OVERFLOW) { - prmcio.cr[VGA_CRTC_OVERFLOW] = (prmcio.cr[VGA_CRTC_OVERFLOW] & ~0x10) | + if (d->prmcio.cr_index == VGA_CRTC_OVERFLOW) { + d->prmcio.cr[VGA_CRTC_OVERFLOW] = (d->prmcio.cr[VGA_CRTC_OVERFLOW] & ~0x10) | (value & 0x10); EmuWarning("TODO: vbe_update_vgaregs"); //vbe_update_vgaregs(); @@ -58,11 +58,11 @@ DEVICE_WRITE32(PRMCIO) return; } - prmcio.cr[prmcio.cr_index] = value; + d->prmcio.cr[d->prmcio.cr_index] = value; EmuWarning("TODO: vbe_update_vgaregs"); //vbe_update_vgaregs(); - switch (prmcio.cr_index) { + switch (d->prmcio.cr_index) { case VGA_CRTC_H_TOTAL: case VGA_CRTC_H_SYNC_START: case VGA_CRTC_H_SYNC_END: diff --git a/src/devices/video/EmuNV2A_PTIMER.cpp b/src/devices/video/EmuNV2A_PTIMER.cpp index 46f1590f6..16baa64d0 100644 --- a/src/devices/video/EmuNV2A_PTIMER.cpp +++ b/src/devices/video/EmuNV2A_PTIMER.cpp @@ -18,34 +18,34 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) } /* PIMTER - time measurement and time-based alarms */ -static uint32_t ptimer_get_clock() +static uint32_t ptimer_get_clock(NV2AState * d) { // Get time in nanoseconds long int time = static_cast(std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count()); - return muldiv64(time, pramdac.core_clock_freq * ptimer.numerator, CLOCKS_PER_SEC * ptimer.denominator); + return muldiv64(time, d->pramdac.core_clock_freq * d->ptimer.numerator, CLOCKS_PER_SEC * d->ptimer.denominator); } DEVICE_READ32(PTIMER) { DEVICE_READ32_SWITCH() { case NV_PTIMER_INTR_0: - result = ptimer.pending_interrupts; + result = d->ptimer.pending_interrupts; break; case NV_PTIMER_INTR_EN_0: - result = ptimer.enabled_interrupts; + result = d->ptimer.enabled_interrupts; break; case NV_PTIMER_NUMERATOR: - result = ptimer.numerator; + result = d->ptimer.numerator; break; case NV_PTIMER_DENOMINATOR: - result = ptimer.denominator; + result = d->ptimer.denominator; break; case NV_PTIMER_TIME_0: - result = (ptimer_get_clock() & 0x7ffffff) << 5; + result = (ptimer_get_clock(d) & 0x7ffffff) << 5; break; case NV_PTIMER_TIME_1: - result = (ptimer_get_clock() >> 27) & 0x1fffffff; + result = (ptimer_get_clock(d) >> 27) & 0x1fffffff; break; default: result = 0; @@ -62,21 +62,21 @@ DEVICE_WRITE32(PTIMER) switch (addr) { case NV_PTIMER_INTR_0: - ptimer.pending_interrupts &= ~value; - update_irq(); + d->ptimer.pending_interrupts &= ~value; + update_irq(d); break; case NV_PTIMER_INTR_EN_0: - ptimer.enabled_interrupts = value; - update_irq(); + d->ptimer.enabled_interrupts = value; + update_irq(d); break; case NV_PTIMER_DENOMINATOR: - ptimer.denominator = value; + d->ptimer.denominator = value; break; case NV_PTIMER_NUMERATOR: - ptimer.numerator = value; + d->ptimer.numerator = value; break; case NV_PTIMER_ALARM_0: - ptimer.alarm_time = value; + d->ptimer.alarm_time = value; break; default: //DEVICE_WRITE32_REG(ptimer); // Was : DEBUG_WRITE32_UNHANDLED(PTIMER); diff --git a/src/devices/video/EmuNV2A_PVIDEO.cpp b/src/devices/video/EmuNV2A_PVIDEO.cpp index 595877a9d..8121a8907 100644 --- a/src/devices/video/EmuNV2A_PVIDEO.cpp +++ b/src/devices/video/EmuNV2A_PVIDEO.cpp @@ -29,12 +29,12 @@ DEVICE_WRITE32(PVIDEO) { switch (addr) { case NV_PVIDEO_BUFFER: - pvideo.regs[addr] = value; + d->pvideo.regs[addr] = value; // TODO: vga.enable_overlay = true; // pvideo_vga_invalidate(d); break; case NV_PVIDEO_STOP: - pvideo.regs[NV_PVIDEO_BUFFER] = 0; + d->pvideo.regs[NV_PVIDEO_BUFFER] = 0; // TODO: vga.enable_overlay = false; //pvideo_vga_invalidate(d); break; diff --git a/src/devices/video/EmuNV2A_USER.cpp b/src/devices/video/EmuNV2A_USER.cpp index c3435866c..731a73141 100644 --- a/src/devices/video/EmuNV2A_USER.cpp +++ b/src/devices/video/EmuNV2A_USER.cpp @@ -3,9 +3,9 @@ DEVICE_READ32(USER) unsigned int channel_id = addr >> 16; assert(channel_id < NV2A_NUM_CHANNELS); - ChannelControl *control = &user.channel_control[channel_id]; + ChannelControl *control = &d->user.channel_control[channel_id]; - uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE]; + uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE]; /* PIO Mode */ if (!channel_modes & (1 << channel_id)) { @@ -37,17 +37,17 @@ DEVICE_WRITE32(USER) unsigned int channel_id = addr >> 16; assert(channel_id < NV2A_NUM_CHANNELS); - ChannelControl *control = &user.channel_control[channel_id]; + ChannelControl *control = &d->user.channel_control[channel_id]; - uint32_t channel_modes = pfifo.regs[NV_PFIFO_MODE]; + uint32_t channel_modes = d->pfifo.regs[NV_PFIFO_MODE]; if (channel_modes & (1 << channel_id)) { /* DMA Mode */ switch (addr & 0xFFFF) { case NV_USER_DMA_PUT: control->dma_put = value; - if (pfifo.cache1.push_enabled) { - pfifo_run_pusher(); + if (d->pfifo.cache1.push_enabled) { + pfifo_run_pusher(d); } break; case NV_USER_DMA_GET: diff --git a/src/devices/video/nv2a.h b/src/devices/video/nv2a.h index 360bdf2c4..adacc9cf1 100644 --- a/src/devices/video/nv2a.h +++ b/src/devices/video/nv2a.h @@ -34,11 +34,504 @@ // ****************************************************************** #pragma once +#include "Cxbx.h" // For xbaddr #include "devices\PCIDevice.h" // For PCIDevice +#include +#include +#include +#include + +#include + +#include "nv2a_int.h" + #define NV2A_ADDR 0xFD000000 #define NV2A_SIZE 0x01000000 +#define NV_PMC_SIZE 0x001000 +#define _NV_PFIFO_SIZE 0x002000 // Underscore prefix to prevent clash with NV_PFIFO_SIZE +#define NV_PVIDEO_SIZE 0x001000 +#define NV_PTIMER_SIZE 0x001000 +#define NV_PFB_SIZE 0x001000 +#define NV_PGRAPH_SIZE 0x002000 +#define NV_PCRTC_SIZE 0x001000 +#define NV_PRAMDAC_SIZE 0x001000 +#define NV_PRAMIN_ADDR 0x00700000 +#define NV_PRAMIN_SIZE 0x100000 + +typedef xbaddr hwaddr; // Compatibility; Cxbx uses xbaddr, xqemu and OpenXbox use hwaddr +typedef uint32_t value_t; // Compatibility; Cxbx values are uint32_t (xqemu and OpenXbox use uint64_t) + + +#define USE_TEXTURE_CACHE + +// Public Domain ffs Implementation +// See: http://snipplr.com/view/22147/stringsh-implementation/ +constexpr int ffs(int v) +{ + unsigned int x = v; + int c = 1; + + /* + * adapted from from + * http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightBinSearch + * + * a modified binary search algorithm to count 0 bits from + * the right (lsb). This algorithm should work regardless + * of the size of ints on the platform. + * + */ + + /* a couple special cases */ + if (x == 0) return 0; + if (x & 1) return 1; /* probably pretty common */ + + c = 1; + while ((x & 0xffff) == 0) { + x >>= 16; + c += 16; + } + if ((x & 0xff) == 0) { + x >>= 8; + c += 8; + } + if ((x & 0x0f) == 0) { + x >>= 4; + c += 4; + } + if ((x & 0x03) == 0) { + x >>= 2; + c += 2; + } + + c -= (x & 1); + c += 1; /* ffs() requires indexing bits from 1 */ + /* ie., the lsb is bit 1, not bit 0 */ + return c; +} + +inline int GET_MASK(int v, int mask) { + return (((v) & (mask)) >> (ffs(mask) - 1)); +}; + +inline int SET_MASK(int v, int mask, int val) { + const unsigned int __val = (val); + const unsigned int __mask = (mask); + + (v) &= ~(__mask); + return (v) |= ((__val) << (ffs(__mask)-1)) & (__mask); +} + +#define CASE_4(v, step) \ + case (v): \ + case (v)+(step) : \ + case (v)+(step) * 2: \ + case (v)+(step) * 3 + + +#define NV2A_DEVICE(obj) \ + OBJECT_CHECK(NV2AState, (obj), "nv2a") + +//void reg_log_read(int block, hwaddr addr, uint64_t val); +//void reg_log_write(int block, hwaddr addr, uint64_t val); + +enum FifoMode { + FIFO_PIO = 0, + FIFO_DMA = 1, +}; + +enum FIFOEngine { + ENGINE_SOFTWARE = 0, + ENGINE_GRAPHICS = 1, + ENGINE_DVD = 2, +}; + +typedef struct DMAObject { + unsigned int dma_class; + unsigned int dma_target; + xbaddr address; + xbaddr limit; +} DMAObject; + +typedef struct VertexAttribute { + bool dma_select; + xbaddr offset; + + /* inline arrays are packed in order? + * Need to pass the offset to converted attributes */ + unsigned int inline_array_offset; + + float inline_value[4]; + + unsigned int format; + unsigned int size; /* size of the data type */ + unsigned int count; /* number of components */ + uint32_t stride; + + bool needs_conversion; + uint8_t *converted_buffer; + unsigned int converted_elements; + unsigned int converted_size; + unsigned int converted_count; + + float *inline_buffer; + + GLint gl_count; + GLenum gl_type; + GLboolean gl_normalize; + + GLuint gl_converted_buffer; + GLuint gl_inline_buffer; +} VertexAttribute; + +typedef struct Surface { + bool draw_dirty; + bool buffer_dirty; + bool write_enabled_cache; + unsigned int pitch; + + xbaddr offset; +} Surface; + +typedef struct SurfaceShape { + unsigned int z_format; + unsigned int color_format; + unsigned int zeta_format; + unsigned int log_width, log_height; + unsigned int clip_x, clip_y; + unsigned int clip_width, clip_height; + unsigned int anti_aliasing; +} SurfaceShape; + +typedef struct TextureShape { + bool cubemap; + unsigned int dimensionality; + unsigned int color_format; + unsigned int levels; + unsigned int width, height, depth; + + unsigned int min_mipmap_level, max_mipmap_level; + unsigned int pitch; +} TextureShape; + +typedef struct TextureKey { + TextureShape state; + uint64_t data_hash; + uint8_t* texture_data; + uint8_t* palette_data; +} TextureKey; + +typedef struct TextureBinding { + GLenum gl_target; + GLuint gl_texture; + unsigned int refcnt; +} TextureBinding; + +typedef struct KelvinState { + xbaddr dma_notifies; + xbaddr dma_state; + xbaddr dma_semaphore; + unsigned int semaphore_offset; +} KelvinState; + +typedef struct ContextSurfaces2DState { + xbaddr dma_image_source; + xbaddr dma_image_dest; + unsigned int color_format; + unsigned int source_pitch, dest_pitch; + xbaddr source_offset, dest_offset; + +} ContextSurfaces2DState; + +typedef struct ImageBlitState { + xbaddr context_surfaces; + unsigned int operation; + unsigned int in_x, in_y; + unsigned int out_x, out_y; + unsigned int width, height; + +} ImageBlitState; + +typedef struct GraphicsObject { + uint8_t graphics_class; + union { + ContextSurfaces2DState context_surfaces_2d; + + ImageBlitState image_blit; + + KelvinState kelvin; + } data; +} GraphicsObject; + +typedef struct GraphicsSubchannel { + xbaddr object_instance; + GraphicsObject object; + uint32_t object_cache[5]; +} GraphicsSubchannel; + +typedef struct GraphicsContext { + bool channel_3d; + unsigned int subchannel; +} GraphicsContext; + + +typedef struct PGRAPHState { + std::mutex mutex; + + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + std::condition_variable interrupt_cond; + + xbaddr context_table; + xbaddr context_address; + + + unsigned int trapped_method; + unsigned int trapped_subchannel; + unsigned int trapped_channel_id; + uint32_t trapped_data[2]; + uint32_t notify_source; + + bool fifo_access; + std::condition_variable fifo_access_cond; + std::condition_variable flip_3d; + + unsigned int channel_id; + bool channel_valid; + GraphicsContext context[NV2A_NUM_CHANNELS]; + + xbaddr dma_color, dma_zeta; + Surface surface_color, surface_zeta; + unsigned int surface_type; + SurfaceShape surface_shape; + SurfaceShape last_surface_shape; + + xbaddr dma_a, dma_b; + //GLruCache *texture_cache; + bool texture_dirty[NV2A_MAX_TEXTURES]; + TextureBinding *texture_binding[NV2A_MAX_TEXTURES]; + + //GHashTable *shader_cache; + //ShaderBinding *shader_binding; + + bool texture_matrix_enable[NV2A_MAX_TEXTURES]; + + /* FIXME: Move to NV_PGRAPH_BUMPMAT... */ + float bump_env_matrix[NV2A_MAX_TEXTURES - 1][4]; /* 3 allowed stages with 2x2 matrix each */ + + // wglContext *gl_context; + GLuint gl_framebuffer; + GLuint gl_color_buffer, gl_zeta_buffer; + GraphicsSubchannel subchannel_data[NV2A_NUM_SUBCHANNELS]; + + xbaddr dma_report; + xbaddr report_offset; + bool zpass_pixel_count_enable; + unsigned int zpass_pixel_count_result; + unsigned int gl_zpass_pixel_count_query_count; + GLuint* gl_zpass_pixel_count_queries; + + xbaddr dma_vertex_a, dma_vertex_b; + + unsigned int primitive_mode; + + bool enable_vertex_program_write; + + //uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE]; + + uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4]; + bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS]; + + /* lighting constant arrays */ + uint32_t ltctxa[NV2A_LTCTXA_COUNT][4]; + bool ltctxa_dirty[NV2A_LTCTXA_COUNT]; + uint32_t ltctxb[NV2A_LTCTXB_COUNT][4]; + bool ltctxb_dirty[NV2A_LTCTXB_COUNT]; + uint32_t ltc1[NV2A_LTC1_COUNT][4]; + bool ltc1_dirty[NV2A_LTC1_COUNT]; + + // should figure out where these are in lighting context + float light_infinite_half_vector[NV2A_MAX_LIGHTS][3]; + float light_infinite_direction[NV2A_MAX_LIGHTS][3]; + float light_local_position[NV2A_MAX_LIGHTS][3]; + float light_local_attenuation[NV2A_MAX_LIGHTS][3]; + + //VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES]; + + unsigned int inline_array_length; + uint32_t inline_array[NV2A_MAX_BATCH_LENGTH]; + GLuint gl_inline_array_buffer; + + unsigned int inline_elements_length; + uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH]; + + unsigned int inline_buffer_length; + + unsigned int draw_arrays_length; + unsigned int draw_arrays_max_count; + + /* FIXME: Unknown size, possibly endless, 1000 will do for now */ + GLint gl_draw_arrays_start[1000]; + GLsizei gl_draw_arrays_count[1000]; + + GLuint gl_element_buffer; + GLuint gl_memory_buffer; + GLuint gl_vertex_array; + + uint32_t regs[NV_PGRAPH_SIZE]; // TODO : union +} PGRAPHState; + + +typedef struct CacheEntry { + //QSIMPLEQ_ENTRY(CacheEntry) entry; + unsigned int method : 14; + unsigned int subchannel : 3; + bool nonincreasing; + uint32_t parameter; +} CacheEntry; + +typedef struct Cache1State { + unsigned int channel_id; + FifoMode mode; + + /* Pusher state */ + bool push_enabled; + bool dma_push_enabled; + bool dma_push_suspended; + xbaddr dma_instance; + + bool method_nonincreasing; + unsigned int method : 14; + unsigned int subchannel : 3; + unsigned int method_count : 24; + uint32_t dcount; + + bool subroutine_active; + xbaddr subroutine_return; + xbaddr get_jmp_shadow; + uint32_t rsvd_shadow; + uint32_t data_shadow; + uint32_t error; + + bool pull_enabled; + enum FIFOEngine bound_engines[NV2A_NUM_SUBCHANNELS]; + enum FIFOEngine last_engine; + + /* The actual command queue */ + std::mutex mutex; + std::condition_variable cache_cond; + std::queue cache; + std::queue working_cache; +} Cache1State; + +typedef struct ChannelControl { + xbaddr dma_put; + xbaddr dma_get; + uint32_t ref; +} ChannelControl; + +typedef struct NV2AState { + // PCIDevice dev; + // qemu_irq irq; + bool exiting; + + // VGACommonState vga; + // GraphicHwOps hw_ops; + // QEMUTimer *vblank_timer; + + // MemoryRegion *vram; + // MemoryRegion vram_pci; + uint8_t *vram_ptr; + size_t vram_size; + // MemoryRegion ramin; + struct { + uint8_t *ramin_ptr; + size_t ramin_size; + uint32_t regs[NV_PRAMIN_SIZE]; // TODO : union + } pramin; + + // MemoryRegion mmio; + // MemoryRegion block_mmio[NV_NUM_BLOCKS]; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + uint32_t regs[NV_PMC_SIZE]; // TODO : union + } pmc; + + struct { + std::thread puller_thread; + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + Cache1State cache1; + uint32_t regs[_NV_PFIFO_SIZE]; // TODO : union + } pfifo; + + struct { + uint32_t regs[NV_PVIDEO_SIZE]; // TODO : union + } pvideo; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + uint32_t numerator; + uint32_t denominator; + uint32_t alarm_time; + uint32_t regs[NV_PTIMER_SIZE]; // TODO : union + } ptimer; + + struct { + uint32_t regs[NV_PFB_SIZE]; // TODO : union + } pfb; + + struct PGRAPHState pgraph; + + struct { + uint32_t pending_interrupts; + uint32_t enabled_interrupts; + hwaddr start; + uint32_t regs[NV_PCRTC_SIZE]; // TODO : union + } pcrtc; + + struct { + uint32_t core_clock_coeff; + uint64_t core_clock_freq; + uint32_t memory_clock_coeff; + uint32_t video_clock_coeff; + uint32_t regs[NV_PRAMDAC_SIZE]; // TODO : union + } pramdac; + + struct { + ChannelControl channel_control[NV2A_NUM_CHANNELS]; + } user; + + // PRMCIO (Actually the VGA controller) + struct { + uint8_t cr_index; + uint8_t cr[256]; /* CRT registers */ + } prmcio; + + std::mutex io_lock; + //SDL_Window *sdl_window; + +} NV2AState; + +typedef value_t(*read_func)(NV2AState *d, hwaddr addr); //, unsigned int size); +typedef void(*write_func)(NV2AState *d, hwaddr addr, value_t val); //, unsigned int size); + +typedef struct { + read_func read; + write_func write; +} MemoryRegionOps; + +typedef struct NV2ABlockInfo { + const char* name; + hwaddr offset; + uint64_t size; + MemoryRegionOps ops; +} NV2ABlockInfo; + class NV2ADevice : public PCIDevice { public: // PCI Device functions