From be23a674f3cc65619bb5ec0d6447a03e01d3b1a3 Mon Sep 17 00:00:00 2001
From: Matt Borgerson <contact@mborgerson.com>
Date: Mon, 31 Jan 2022 22:23:15 -0700
Subject: [PATCH] nv2a: Start using tracing infrastructure for logging

---
 build.sh                  |   2 +-
 hw/xbox/nv2a/debug.h      |   1 -
 hw/xbox/nv2a/nv2a.c       | 102 ++++--------
 hw/xbox/nv2a/nv2a_int.h   |  28 +++-
 hw/xbox/nv2a/pbus.c       |   4 +-
 hw/xbox/nv2a/pcrtc.c      |   4 +-
 hw/xbox/nv2a/pfb.c        |   4 +-
 hw/xbox/nv2a/pfifo.c      |   4 +-
 hw/xbox/nv2a/pgraph.c     | 341 +++++++++++++++++++-------------------
 hw/xbox/nv2a/pmc.c        |   4 +-
 hw/xbox/nv2a/pramdac.c    |   5 +-
 hw/xbox/nv2a/prmcio.c     |   4 +-
 hw/xbox/nv2a/prmdio.c     |   4 +-
 hw/xbox/nv2a/prmvio.c     |   4 +-
 hw/xbox/nv2a/ptimer.c     |   4 +-
 hw/xbox/nv2a/pvideo.c     |   4 +-
 hw/xbox/nv2a/stubs.c      |   4 +-
 hw/xbox/nv2a/trace-events |  17 ++
 hw/xbox/nv2a/trace.h      |   1 +
 hw/xbox/nv2a/user.c       |   4 +-
 meson.build               |   1 +
 21 files changed, 266 insertions(+), 280 deletions(-)
 create mode 100644 hw/xbox/nv2a/trace-events
 create mode 100644 hw/xbox/nv2a/trace.h

diff --git a/build.sh b/build.sh
index 4739ef97b3..a15a032920 100755
--- a/build.sh
+++ b/build.sh
@@ -165,7 +165,7 @@ done
 target="qemu-system-i386"
 if test ! -z "$debug"; then
     build_cflags='-O0 -g -DXEMU_DEBUG_BUILD=1'
-    opts="--enable-debug"
+    opts="--enable-debug --enable-trace-backends=log"
 else
     opts="--enable-lto"
 fi
diff --git a/hw/xbox/nv2a/debug.h b/hw/xbox/nv2a/debug.h
index 73c8bd3f4f..7eb574c017 100644
--- a/hw/xbox/nv2a/debug.h
+++ b/hw/xbox/nv2a/debug.h
@@ -36,7 +36,6 @@
 # define NV2A_DPRINTF(format, ...)       do { } while (0)
 #endif
 
-// #define DEBUG_NV2A_REG
 // #define DEBUG_NV2A_GL
 #ifdef DEBUG_NV2A_GL
 
diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c
index 445b792501..7bd3960753 100644
--- a/hw/xbox/nv2a/nv2a.c
+++ b/hw/xbox/nv2a/nv2a.c
@@ -21,9 +21,6 @@
 
 #include "hw/xbox/nv2a/nv2a_int.h"
 
-#define DBG_IRQ 0
-#define DBG_DMA 0
-
 void nv2a_update_irq(NV2AState *d)
 {
     /* PFIFO */
@@ -48,7 +45,7 @@ void nv2a_update_irq(NV2AState *d)
     }
 
     if (d->pmc.pending_interrupts && d->pmc.enabled_interrupts) {
-        NV2A_XPRINTF(DBG_IRQ, "raise irq\n");
+        trace_nv2a_irq(d->pmc.pending_interrupts);
         pci_irq_assert(PCI_DEVICE(d));
     } else {
         pci_irq_deassert(PCI_DEVICE(d));
@@ -77,10 +74,8 @@ void *nv_dma_map(NV2AState *d, hwaddr dma_obj_address, hwaddr *len)
     DMAObject dma = nv_dma_load(d, dma_obj_address);
 
     /* TODO: Handle targets and classes properly */
-    NV2A_XPRINTF(DBG_DMA,
-                 "dma_map %" HWADDR_PRIx " - %x, %x, %" HWADDR_PRIx " %" HWADDR_PRIx "\n",
-                 dma_obj_address,
-                 dma.dma_class, dma.dma_target, dma.address, dma.limit);
+    trace_nv2a_dma_map(dma_obj_address, dma.dma_class, dma.dma_target,
+                       dma.address, dma.limit);
     dma.address &= 0x07FFFFFF;
 
     assert(dma.address < memory_region_size(d->vram));
@@ -89,75 +84,36 @@ void *nv_dma_map(NV2AState *d, hwaddr dma_obj_address, hwaddr *len)
     return d->vram_ptr + dma.address;
 }
 
-const struct NV2ABlockInfo blocktable[] = {
-    #define ENTRY(NAME, OFFSET, SIZE, RDFUNC, WRFUNC)  \
-    [NV_##NAME] = {                                    \
-        .name   = #NAME,                               \
-        .offset = OFFSET,                              \
-        .size   = SIZE,                                \
-        .ops    = { .read = RDFUNC, .write = WRFUNC }, \
+const NV2ABlockInfo blocktable[NV_NUM_BLOCKS] = {
+    #define ENTRY(NAME, LNAME, OFFSET, SIZE) [NV_##NAME] = {            \
+        .name   = #NAME,                                                \
+        .offset = OFFSET,                                               \
+        .size   = SIZE,                                                 \
+        .ops    = { .read = LNAME ## _read, .write = LNAME ## _write }, \
     }
-    ENTRY(PMC,      0x000000, 0x001000, pmc_read,      pmc_write),
-    ENTRY(PBUS,     0x001000, 0x001000, pbus_read,     pbus_write),
-    ENTRY(PFIFO,    0x002000, 0x002000, pfifo_read,    pfifo_write),
-    ENTRY(PRMA,     0x007000, 0x001000, prma_read,     prma_write),
-    ENTRY(PVIDEO,   0x008000, 0x001000, pvideo_read,   pvideo_write),
-    ENTRY(PTIMER,   0x009000, 0x001000, ptimer_read,   ptimer_write),
-    ENTRY(PCOUNTER, 0x00a000, 0x001000, pcounter_read, pcounter_write),
-    ENTRY(PVPE,     0x00b000, 0x001000, pvpe_read,     pvpe_write),
-    ENTRY(PTV,      0x00d000, 0x001000, ptv_read,      ptv_write),
-    ENTRY(PRMFB,    0x0a0000, 0x020000, prmfb_read,    prmfb_write),
-    ENTRY(PRMVIO,   0x0c0000, 0x001000, prmvio_read,   prmvio_write),
-    ENTRY(PFB,      0x100000, 0x001000, pfb_read,      pfb_write),
-    ENTRY(PSTRAPS,  0x101000, 0x001000, pstraps_read,  pstraps_write),
-    ENTRY(PGRAPH,   0x400000, 0x002000, pgraph_read,   pgraph_write),
-    ENTRY(PCRTC,    0x600000, 0x001000, pcrtc_read,    pcrtc_write),
-    ENTRY(PRMCIO,   0x601000, 0x001000, prmcio_read,   prmcio_write),
-    ENTRY(PRAMDAC,  0x680000, 0x001000, pramdac_read,  pramdac_write),
-    ENTRY(PRMDIO,   0x681000, 0x001000, prmdio_read,   prmdio_write),
-    // ENTRY(PRAMIN,   0x700000, 0x100000, pramin_read,   pramin_write),
-    ENTRY(USER,     0x800000, 0x800000, user_read,     user_write),
+    ENTRY(PMC,      pmc,      0x000000, 0x001000),
+    ENTRY(PBUS,     pbus,     0x001000, 0x001000),
+    ENTRY(PFIFO,    pfifo,    0x002000, 0x002000),
+    ENTRY(PRMA,     prma,     0x007000, 0x001000),
+    ENTRY(PVIDEO,   pvideo,   0x008000, 0x001000),
+    ENTRY(PTIMER,   ptimer,   0x009000, 0x001000),
+    ENTRY(PCOUNTER, pcounter, 0x00a000, 0x001000),
+    ENTRY(PVPE,     pvpe,     0x00b000, 0x001000),
+    ENTRY(PTV,      ptv,      0x00d000, 0x001000),
+    ENTRY(PRMFB,    prmfb,    0x0a0000, 0x020000),
+    ENTRY(PRMVIO,   prmvio,   0x0c0000, 0x001000),
+    ENTRY(PFB,      pfb,      0x100000, 0x001000),
+    ENTRY(PSTRAPS,  pstraps,  0x101000, 0x001000),
+    ENTRY(PGRAPH,   pgraph,   0x400000, 0x002000),
+    ENTRY(PCRTC,    pcrtc,    0x600000, 0x001000),
+    ENTRY(PRMCIO,   prmcio,   0x601000, 0x001000),
+    ENTRY(PRAMDAC,  pramdac,  0x680000, 0x001000),
+    ENTRY(PRMDIO,   prmdio,   0x681000, 0x001000),
+    // ENTRY(PRAMIN,   pramin,   0x700000, 0x100000),
+    ENTRY(USER,     user,     0x800000, 0x800000),
 };
 #undef ENTRY
 
-#ifdef DEBUG_NV2A_REG
-static const char *nv2a_reg_names[] = {};
-
-void nv2a_reg_log_read(int block, hwaddr addr, uint64_t val)
-{
-    if (blocktable[block].name) {
-        hwaddr naddr = blocktable[block].offset + addr;
-        if (naddr < ARRAY_SIZE(nv2a_reg_names) && nv2a_reg_names[naddr]) {
-            NV2A_DPRINTF("%s: read [%s] -> 0x%" PRIx64 "\n",
-                    blocktable[block].name, nv2a_reg_names[naddr], val);
-        } else {
-            NV2A_DPRINTF("%s: read [%" HWADDR_PRIx "] -> 0x%" PRIx64 "\n",
-                         blocktable[block].name, addr, val);
-        }
-    } else {
-        NV2A_DPRINTF("(%d?): read [%" HWADDR_PRIx "] -> 0x%" PRIx64 "\n",
-                     block, addr, val);
-    }
-}
-
-void nv2a_reg_log_write(int block, hwaddr addr, uint64_t val)
-{
-    if (blocktable[block].name) {
-        hwaddr naddr = blocktable[block].offset + addr;
-        if (naddr < ARRAY_SIZE(nv2a_reg_names) && nv2a_reg_names[naddr]) {
-            NV2A_DPRINTF("%s: [%s] = 0x%" PRIx64 "\n",
-                    blocktable[block].name, nv2a_reg_names[naddr], val);
-        } else {
-            NV2A_DPRINTF("%s: [%" HWADDR_PRIx "] = 0x%" PRIx64 "\n",
-                         blocktable[block].name, addr, val);
-        }
-    } else {
-        NV2A_DPRINTF("(%d?): [%" HWADDR_PRIx "] = 0x%" PRIx64 "\n",
-                     block, addr, val);
-    }
-}
-#endif
-
 static int nv2a_get_bpp(VGACommonState *s)
 {
     NV2AState *d = container_of(s, NV2AState, vga);
diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h
index 935011ea30..69dd2ca720 100644
--- a/hw/xbox/nv2a/nv2a_int.h
+++ b/hw/xbox/nv2a/nv2a_int.h
@@ -41,6 +41,7 @@
 #include "hw/pci/pci.h"
 #include "cpu.h"
 
+#include "trace.h"
 #include "swizzle.h"
 #include "lru.h"
 #include "gl/gloffscreen.h"
@@ -488,19 +489,32 @@ typedef struct NV2ABlockInfo {
     uint64_t size;
     MemoryRegionOps ops;
 } NV2ABlockInfo;
+extern const NV2ABlockInfo blocktable[NV_NUM_BLOCKS];
 
 extern GloContext *g_nv2a_context_render;
 extern GloContext *g_nv2a_context_display;
 
 void nv2a_update_irq(NV2AState *d);
 
-#ifdef DEBUG_NV2A_REG
-void nv2a_reg_log_read(int block, hwaddr addr, uint64_t val);
-void nv2a_reg_log_write(int block, hwaddr addr, uint64_t val);
-#else
-#define nv2a_reg_log_read(block, addr, val) do {} while (0)
-#define nv2a_reg_log_write(block, addr, val) do {} while (0)
-#endif
+static inline
+void nv2a_reg_log_read(int block, hwaddr addr, unsigned int size, uint64_t val)
+{
+    const char *block_name = "UNK";
+    if (block < ARRAY_SIZE(blocktable) && blocktable[block].name) {
+        block_name = blocktable[block].name;
+    }
+    trace_nv2a_reg_read(block_name, addr, size, val);
+}
+
+static inline
+void nv2a_reg_log_write(int block, hwaddr addr, unsigned int size, uint64_t val)
+{
+    const char *block_name = "UNK";
+    if (block < ARRAY_SIZE(blocktable) && blocktable[block].name) {
+        block_name = blocktable[block].name;
+    }
+    trace_nv2a_reg_write(block_name, addr, size, val);
+}
 
 #define DEFINE_PROTO(n) \
     uint64_t n##_read(void *opaque, hwaddr addr, unsigned int size); \
diff --git a/hw/xbox/nv2a/pbus.c b/hw/xbox/nv2a/pbus.c
index 737d1bfb2d..3296c329fd 100644
--- a/hw/xbox/nv2a/pbus.c
+++ b/hw/xbox/nv2a/pbus.c
@@ -42,7 +42,7 @@ uint64_t pbus_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PBUS, addr, r);
+    nv2a_reg_log_read(NV_PBUS, addr, size, r);
     return r;
 }
 
@@ -51,7 +51,7 @@ void pbus_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
     NV2AState *s = opaque;
     PCIDevice *d = PCI_DEVICE(s);
 
-    nv2a_reg_log_write(NV_PBUS, addr, val);
+    nv2a_reg_log_write(NV_PBUS, addr, size, val);
 
     switch (addr) {
     case NV_PBUS_PCI_NV_1:
diff --git a/hw/xbox/nv2a/pcrtc.c b/hw/xbox/nv2a/pcrtc.c
index 340b370077..cc9f77c231 100644
--- a/hw/xbox/nv2a/pcrtc.c
+++ b/hw/xbox/nv2a/pcrtc.c
@@ -43,7 +43,7 @@ uint64_t pcrtc_read(void *opaque, hwaddr addr, unsigned int size)
             break;
     }
 
-    nv2a_reg_log_read(NV_PCRTC, addr, r);
+    nv2a_reg_log_read(NV_PCRTC, addr, size, r);
     return r;
 }
 
@@ -51,7 +51,7 @@ void pcrtc_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_PCRTC, addr, val);
+    nv2a_reg_log_write(NV_PCRTC, addr, size, val);
 
     switch (addr) {
     case NV_PCRTC_INTR_0:
diff --git a/hw/xbox/nv2a/pfb.c b/hw/xbox/nv2a/pfb.c
index 892195d06a..ef2d7528af 100644
--- a/hw/xbox/nv2a/pfb.c
+++ b/hw/xbox/nv2a/pfb.c
@@ -42,7 +42,7 @@ uint64_t pfb_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PFB, addr, r);
+    nv2a_reg_log_read(NV_PFB, addr, size, r);
     return r;
 }
 
@@ -50,7 +50,7 @@ void pfb_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_PFB, addr, val);
+    nv2a_reg_log_write(NV_PFB, addr, size, val);
 
     switch (addr) {
     default:
diff --git a/hw/xbox/nv2a/pfifo.c b/hw/xbox/nv2a/pfifo.c
index 0b54bf694b..0f73a9092c 100644
--- a/hw/xbox/nv2a/pfifo.c
+++ b/hw/xbox/nv2a/pfifo.c
@@ -58,7 +58,7 @@ uint64_t pfifo_read(void *opaque, hwaddr addr, unsigned int size)
 
     qemu_mutex_unlock(&d->pfifo.lock);
 
-    nv2a_reg_log_read(NV_PFIFO, addr, r);
+    nv2a_reg_log_read(NV_PFIFO, addr, size, r);
     return r;
 }
 
@@ -66,7 +66,7 @@ void pfifo_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_PFIFO, addr, val);
+    nv2a_reg_log_write(NV_PFIFO, addr, size, val);
 
     qemu_mutex_lock(&d->pfifo.lock);
 
diff --git a/hw/xbox/nv2a/pgraph.c b/hw/xbox/nv2a/pgraph.c
index 14c878bda5..b08f3afaa8 100644
--- a/hw/xbox/nv2a/pgraph.c
+++ b/hw/xbox/nv2a/pgraph.c
@@ -20,6 +20,7 @@
  */
 
 #include "nv2a_int.h"
+
 #include "s3tc.h"
 #include "ui/xemu-settings.h"
 #include "qemu/fast-hash.h"
@@ -511,7 +512,7 @@ uint64_t pgraph_read(void *opaque, hwaddr addr, unsigned int size)
 
     qemu_mutex_unlock(&pg->lock);
 
-    nv2a_reg_log_read(NV_PGRAPH, addr, r);
+    nv2a_reg_log_read(NV_PGRAPH, addr, size, r);
     return r;
 }
 
@@ -520,7 +521,7 @@ void pgraph_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
     NV2AState *d = (NV2AState *)opaque;
     PGRAPHState *pg = &d->pgraph;
 
-    nv2a_reg_log_write(NV_PGRAPH, addr, val);
+    nv2a_reg_log_write(NV_PGRAPH, addr, size, val);
 
     qemu_mutex_lock(&d->pfifo.lock); // FIXME: Factor out fifo lock here
     qemu_mutex_lock(&pg->lock);
@@ -1006,7 +1007,6 @@ int pgraph_method(NV2AState *d, unsigned int subchannel,
     uint32_t graphics_class = GET_MASK(pg->regs[NV_PGRAPH_CTX_SWITCH1],
                                        NV_PGRAPH_CTX_SWITCH1_GRCLASS);
 
-    // NV2A_DPRINTF("graphics_class %d 0x%x\n", subchannel, graphics_class);
     pgraph_method_log(subchannel, graphics_class, method, parameter);
 
     if (subchannel != 0) {
@@ -1016,128 +1016,140 @@ int pgraph_method(NV2AState *d, unsigned int subchannel,
 
     /* ugly switch for now */
     switch (graphics_class) {
-
-    case NV_BETA: { switch (method) {
-    case NV012_SET_OBJECT:
-        beta->object_instance = parameter;
-        break;
-
-    case NV012_SET_BETA:
-        if (parameter & 0x80000000) {
-            beta->beta = 0;
-        } else {
-            // The parameter is a signed fixed-point number with a sign bit and
-            // 31 fractional bits. Note that negative values are clamped to 0,
-            // and only 8 fractional bits are actually implemented in hardware.
-            beta->beta = parameter & 0x7f800000;
+    case NV_BETA: {
+        switch (method) {
+        case NV012_SET_OBJECT:
+            beta->object_instance = parameter;
+            break;
+        case NV012_SET_BETA:
+            if (parameter & 0x80000000) {
+                beta->beta = 0;
+            } else {
+                // The parameter is a signed fixed-point number with a sign bit
+                // and 31 fractional bits. Note that negative values are clamped
+                // to 0, and only 8 fractional bits are actually implemented in
+                // hardware.
+                beta->beta = parameter & 0x7f800000;
+            }
+            break;
+        default:
+            goto unhandled;
         }
         break;
-    } break; }
-
-    case NV_CONTEXT_PATTERN: { switch (method) {
-    case NV044_SET_MONOCHROME_COLOR0:
-        pg->regs[NV_PGRAPH_PATT_COLOR0] = parameter;
-        break;
-    } break; }
-
-    case NV_CONTEXT_SURFACES_2D: { switch (method) {
-    case NV062_SET_OBJECT:
-        context_surfaces_2d->object_instance = parameter;
-        break;
-
-    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;
-    } break; }
-
-    case NV_IMAGE_BLIT: { switch (method) {
-    case NV09F_SET_OBJECT:
-        image_blit->object_instance = parameter;
-        break;
-
-    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;
-
-        if (image_blit->width && image_blit->height) {
-            pgraph_image_blit(d);
+    }
+    case NV_CONTEXT_PATTERN: {
+        switch (method) {
+        case NV044_SET_MONOCHROME_COLOR0:
+            pg->regs[NV_PGRAPH_PATT_COLOR0] = parameter;
+            break;
+        default:
+            goto unhandled;
         }
         break;
-    } break; }
+    }
+    case NV_CONTEXT_SURFACES_2D: {
+        switch (method) {
+        case NV062_SET_OBJECT:
+            context_surfaces_2d->object_instance = parameter;
+            break;
+        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:
+            goto unhandled;
+        }
+        break;
+    }
+    case NV_IMAGE_BLIT: {
+        switch (method) {
+        case NV09F_SET_OBJECT:
+            image_blit->object_instance = parameter;
+            break;
+        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;
 
+            if (image_blit->width && image_blit->height) {
+                pgraph_image_blit(d);
+            }
+            break;
+        default:
+            goto unhandled;
+        }
+        break;
+    }
     case NV_KELVIN_PRIMITIVE: {
         MethodFunc handler =
             pgraph_kelvin_methods[METHOD_ADDR_TO_INDEX(method)].handler;
         if (handler == NULL) {
-            NV2A_GL_DPRINTF(true, "    unhandled  (0x%02x 0x%08x)",
-                            graphics_class, method);
-        } else {
-            size_t num_words_consumed = 1;
-            handler(d, pg, subchannel, method, parameter, parameters,
-                    num_words_available, &num_words_consumed, inc);
-
-            /* Squash repeated BEGIN,DRAW_ARRAYS,END */
-            #define LAM(i, mthd) ((parameters[i*2+1] & 0x31fff) == (mthd))
-            #define LAP(i, prm) (parameters[i*2+2] == (prm))
-            #define LAMP(i, mthd, prm) (LAM(i, mthd) && LAP(i, prm))
-
-            if (method == NV097_DRAW_ARRAYS && (max_lookahead_words >= 7) &&
-                pg->draw_arrays_length <
-                    (ARRAY_SIZE(pg->gl_draw_arrays_start) - 1) &&
-                LAMP(0, NV097_SET_BEGIN_END, NV097_SET_BEGIN_END_OP_END) &&
-                LAMP(1, NV097_SET_BEGIN_END, pg->primitive_mode) &&
-                LAM(2, NV097_DRAW_ARRAYS)) {
-                num_words_consumed += 4;
-                pg->draw_arrays_prevent_connect = true;
-            }
-
-            #undef LAM
-            #undef LAP
-            #undef LAMP
-
-            num_processed = num_words_consumed;
+            goto unhandled;
         }
+        size_t num_words_consumed = 1;
+        handler(d, pg, subchannel, method, parameter, parameters,
+                num_words_available, &num_words_consumed, inc);
+
+        /* Squash repeated BEGIN,DRAW_ARRAYS,END */
+        #define LAM(i, mthd) ((parameters[i*2+1] & 0x31fff) == (mthd))
+        #define LAP(i, prm) (parameters[i*2+2] == (prm))
+        #define LAMP(i, mthd, prm) (LAM(i, mthd) && LAP(i, prm))
+
+        if (method == NV097_DRAW_ARRAYS && (max_lookahead_words >= 7) &&
+            pg->draw_arrays_length <
+                (ARRAY_SIZE(pg->gl_draw_arrays_start) - 1) &&
+            LAMP(0, NV097_SET_BEGIN_END, NV097_SET_BEGIN_END_OP_END) &&
+            LAMP(1, NV097_SET_BEGIN_END, pg->primitive_mode) &&
+            LAM(2, NV097_DRAW_ARRAYS)) {
+            num_words_consumed += 4;
+            pg->draw_arrays_prevent_connect = true;
+        }
+
+        #undef LAM
+        #undef LAP
+        #undef LAMP
+
+        num_processed = num_words_consumed;
         break;
     }
-
     default:
-        NV2A_GL_DPRINTF(true, "    unhandled  (0x%02x 0x%08x)",
-                        graphics_class, method);
-        break;
-
+        goto unhandled;
     }
 
     return num_processed;
+
+unhandled:
+    trace_nv2a_pgraph_method_unhandled(subchannel, graphics_class,
+                                           method, parameter);
+    return num_processed;
 }
 
 DEF_METHOD(NV097, SET_OBJECT)
@@ -1206,25 +1218,27 @@ DEF_METHOD(NV097, SET_FLIP_MODULO)
 
 DEF_METHOD(NV097, FLIP_INCREMENT_WRITE)
 {
-    NV2A_DPRINTF("flip increment write %d -> ",
-        GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
-                      NV_PGRAPH_SURFACE_WRITE_3D));
+    uint32_t old =
+        GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D);
+
     SET_MASK(pg->regs[NV_PGRAPH_SURFACE],
              NV_PGRAPH_SURFACE_WRITE_3D,
              (GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
                       NV_PGRAPH_SURFACE_WRITE_3D)+1)
                 % GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
                            NV_PGRAPH_SURFACE_MODULO_3D) );
-    NV2A_DPRINTF("%d\n",
-        GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
-                      NV_PGRAPH_SURFACE_WRITE_3D));
 
+    uint32_t new =
+        GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D);
+
+    trace_nv2a_pgraph_flip_increment_write(old, new);
     NV2A_GL_DFRAME_TERMINATOR();
     pg->frame_time++;
 }
 
 DEF_METHOD(NV097, FLIP_STALL)
 {
+    trace_nv2a_pgraph_flip_stall();
     pgraph_update_surface(d, false, true, true);
     nv2a_profile_flip_stall();
     pg->waiting_for_flip = true;
@@ -3392,7 +3406,6 @@ DEF_METHOD(NV097, SET_TRANSFORM_CONSTANT_LOAD)
     assert(parameter < NV2A_VERTEXSHADER_CONSTANTS);
     SET_MASK(pg->regs[NV_PGRAPH_CHEOPS_OFFSET],
              NV_PGRAPH_CHEOPS_OFFSET_CONST_LD_PTR, parameter);
-    NV2A_DPRINTF("load to %d\n", parameter);
 }
 
 
@@ -3425,60 +3438,52 @@ void pgraph_context_switch(NV2AState *d, unsigned int channel_id)
 
 static void pgraph_method_log(unsigned int subchannel,
                               unsigned int graphics_class,
-                              unsigned int method, uint32_t parameter) {
-#ifdef DEBUG_NV2A
+                              unsigned int method, uint32_t parameter)
+{
+    const char *method_name = "?";
     static unsigned int last = 0;
     static unsigned int count = 0;
-    if (last == 0x1800 && method != last) {
-        NV2A_GL_DPRINTF(true, "pgraph method (%d) 0x%x * %d",
-                     subchannel, last, count);
+
+    if (last == NV097_ARRAY_ELEMENT16 && method != last) {
+        method_name = "NV097_ARRAY_ELEMENT16";
+        trace_nv2a_pgraph_method_abbrev(subchannel, graphics_class, last,
+                                        method_name, count);
+        NV2A_GL_DPRINTF(false, "pgraph method (%d) 0x%x %s * %d", subchannel,
+                        last, method_name, count);
     }
-    if (method != 0x1800) {
-        const char* method_name = NULL;
+
+    if (method != NV097_ARRAY_ELEMENT16) {
         uint32_t base = method;
-        // unsigned int nmethod = 0;
         switch (graphics_class) {
-            case NV_KELVIN_PRIMITIVE: {
-                // nmethod = method | (0x5c << 16);
-                int idx = METHOD_ADDR_TO_INDEX(method);
-                if (idx < ARRAY_SIZE(pgraph_kelvin_methods)) {
-                    method_name = pgraph_kelvin_methods[idx].name;
-                    base = pgraph_kelvin_methods[idx].base;
-                }
-                break;
+        case NV_KELVIN_PRIMITIVE: {
+            int idx = METHOD_ADDR_TO_INDEX(method);
+            if (idx < ARRAY_SIZE(pgraph_kelvin_methods) &&
+                pgraph_kelvin_methods[idx].handler) {
+                method_name = pgraph_kelvin_methods[idx].name;
+                base = pgraph_kelvin_methods[idx].base;
             }
-            case NV_CONTEXT_SURFACES_2D:
-                // nmethod = method | (0x6d << 16);
-                break;
-            case NV_CONTEXT_PATTERN:
-                // nmethod = method | (0x68 << 16);
-                break;
-            default:
-                break;
+            break;
+        }
+        default:
+            break;
         }
 
-        char buf[256];
-        char *ptr = buf;
-        char *end = ptr + sizeof(buf);
-
-        ptr += snprintf(ptr, end - ptr, "pgraph method (%d): 0x%x -> 0x%04x",
-            subchannel, graphics_class, method);
-
-        if (method_name) {
-            ptr += snprintf(ptr, end - ptr, " %s", method_name);
-            uint32_t o = method - base;
-            if (o) {
-                ptr += snprintf(ptr, end - ptr, "+0x%02x", o);
-            }
-        }
-
-        ptr += snprintf(ptr, end - ptr, " (0x%x)", parameter);
-        NV2A_GL_DPRINTF(true, "%s", buf);
+        uint32_t offset = method - base;
+        trace_nv2a_pgraph_method(subchannel, graphics_class, method,
+                                 method_name, offset, parameter);
+        NV2A_GL_DPRINTF(false,
+                        "pgraph method (%d): 0x%" PRIx32 " -> 0x%04" PRIx32
+                        " %s[%" PRId32 "] 0x%" PRIx32,
+                        subchannel, graphics_class, method, method_name, offset,
+                        parameter);
+    }
+
+    if (method == last) {
+        count++;
+    } else {
+        count = 0;
     }
-    if (method == last) { count++; }
-    else {count = 0; }
     last = method;
-#endif
 }
 
 static void pgraph_allocate_inline_buffer_vertices(PGRAPHState *pg,
@@ -4954,16 +4959,12 @@ static void pgraph_surface_access_callback(
     assert(offset < e->size);
 
     if (qatomic_read(&e->draw_dirty)) {
-        NV2A_XPRINTF(DBG_SURFACE_SYNC,
-                     "Surface accessed at %" HWADDR_PRIx "+%" HWADDR_PRIx "\n",
-                     e->vram_addr, offset);
+        trace_nv2a_pgraph_surface_cpu_access(e->vram_addr, offset);
         pgraph_wait_for_surface_download(e);
     }
 
     if (write && !qatomic_read(&e->upload_pending)) {
-        NV2A_XPRINTF(DBG_SURFACE_SYNC,
-                     "Surface write at %" HWADDR_PRIx "+%" HWADDR_PRIx "\n",
-                     e->vram_addr, offset);
+        trace_nv2a_pgraph_surface_cpu_access(e->vram_addr, offset);
         qatomic_set(&e->upload_pending, true);
     }
 }
@@ -4973,9 +4974,8 @@ static SurfaceBinding *pgraph_surface_put(NV2AState *d,
     SurfaceBinding *surface_in)
 {
     assert(pgraph_surface_get(d, addr) == NULL);
-    NV2A_XPRINTF(DBG_SURFACES,
-        "Adding surface region at [%" HWADDR_PRIx ": %" HWADDR_PRIx ")\n",
-        surface_in->vram_addr, surface_in->vram_addr + surface_in->size);
+    trace_nv2a_pgraph_surface_created(surface_in->vram_addr,
+                                      surface_in->vram_addr + surface_in->size);
 
     SurfaceBinding *surface, *next;
     uintptr_t e_end = surface_in->vram_addr + surface_in->size - 1;
@@ -5039,8 +5039,7 @@ static SurfaceBinding *pgraph_surface_get_within(NV2AState *d, hwaddr addr)
 
 static void pgraph_surface_invalidate(NV2AState *d, SurfaceBinding *surface)
 {
-    NV2A_XPRINTF(DBG_SURFACES, "Removing surface at %" HWADDR_PRIx "\n",
-        surface->vram_addr);
+    trace_nv2a_pgraph_surface_invalidated(surface->vram_addr);
 
     assert(surface != d->pgraph.color_binding);
     assert(surface != d->pgraph.zeta_binding);
diff --git a/hw/xbox/nv2a/pmc.c b/hw/xbox/nv2a/pmc.c
index 1a0191cbbf..62e96ec6ff 100644
--- a/hw/xbox/nv2a/pmc.c
+++ b/hw/xbox/nv2a/pmc.c
@@ -46,7 +46,7 @@ uint64_t pmc_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PMC, addr, r);
+    nv2a_reg_log_read(NV_PMC, addr, size, r);
     return r;
 }
 
@@ -54,7 +54,7 @@ void pmc_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_PMC, addr, val);
+    nv2a_reg_log_write(NV_PMC, addr, size, val);
 
     switch (addr) {
     case NV_PMC_INTR_0:
diff --git a/hw/xbox/nv2a/pramdac.c b/hw/xbox/nv2a/pramdac.c
index 14ed307125..1a93549f57 100644
--- a/hw/xbox/nv2a/pramdac.c
+++ b/hw/xbox/nv2a/pramdac.c
@@ -74,8 +74,7 @@ uint64_t pramdac_read(void *opaque, hwaddr addr, unsigned int size)
     /* Surprisingly, QEMU doesn't handle unaligned access for you properly */
     r >>= 32 - 8 * size - 8 * (addr & 3);
 
-    NV2A_DPRINTF("PRAMDAC: read %d [0x%" HWADDR_PRIx "] -> %" PRIx64 "\n", size,
-                 addr, r);
+    nv2a_reg_log_read(NV_PRAMDAC, addr, size, r);
     return r;
 }
 
@@ -84,7 +83,7 @@ void pramdac_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
     NV2AState *d = (NV2AState *)opaque;
     uint32_t m, n, p;
 
-    nv2a_reg_log_write(NV_PRAMDAC, addr, val);
+    nv2a_reg_log_write(NV_PRAMDAC, addr, size, val);
 
     switch (addr) {
     case NV_PRAMDAC_NVPLL_COEFF:
diff --git a/hw/xbox/nv2a/prmcio.c b/hw/xbox/nv2a/prmcio.c
index fe396a00b3..d75e14a518 100644
--- a/hw/xbox/nv2a/prmcio.c
+++ b/hw/xbox/nv2a/prmcio.c
@@ -27,7 +27,7 @@ uint64_t prmcio_read(void *opaque, hwaddr addr, unsigned int size)
     NV2AState *d = opaque;
     uint64_t r = vga_ioport_read(&d->vga, addr);
 
-    nv2a_reg_log_read(NV_PRMCIO, addr, r);
+    nv2a_reg_log_read(NV_PRMCIO, addr, size, r);
     return r;
 }
 
@@ -35,7 +35,7 @@ void prmcio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = opaque;
 
-    nv2a_reg_log_write(NV_PRMCIO, addr, val);
+    nv2a_reg_log_write(NV_PRMCIO, addr, size, val);
 
     switch (addr) {
     case VGA_ATT_W:
diff --git a/hw/xbox/nv2a/prmdio.c b/hw/xbox/nv2a/prmdio.c
index ff04adf661..a80c23ef94 100644
--- a/hw/xbox/nv2a/prmdio.c
+++ b/hw/xbox/nv2a/prmdio.c
@@ -32,7 +32,7 @@ uint64_t prmdio_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PRMDIO, addr, r);
+    nv2a_reg_log_read(NV_PRMDIO, addr, size, r);
     return r;
 }
 
@@ -40,7 +40,7 @@ void prmdio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_PRMDIO, addr, val);
+    nv2a_reg_log_write(NV_PRMDIO, addr, size, val);
 
     switch (addr) {
     case NV_USER_DAC_WRITE_MODE_ADDRESS:
diff --git a/hw/xbox/nv2a/prmvio.c b/hw/xbox/nv2a/prmvio.c
index bdf075e069..3908d894ff 100644
--- a/hw/xbox/nv2a/prmvio.c
+++ b/hw/xbox/nv2a/prmvio.c
@@ -27,7 +27,7 @@ uint64_t prmvio_read(void *opaque, hwaddr addr, unsigned int size)
     NV2AState *d = opaque;
     uint64_t r = vga_ioport_read(&d->vga, addr);
 
-    nv2a_reg_log_read(NV_PRMVIO, addr, r);
+    nv2a_reg_log_read(NV_PRMVIO, addr, size, r);
     return r;
 }
 
@@ -35,6 +35,6 @@ void prmvio_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = opaque;
 
-    nv2a_reg_log_write(NV_PRMVIO, addr, val);
+    nv2a_reg_log_write(NV_PRMVIO, addr, size, val);
     vga_ioport_write(&d->vga, addr, val);
 }
diff --git a/hw/xbox/nv2a/ptimer.c b/hw/xbox/nv2a/ptimer.c
index c056a9edb4..e18f708695 100644
--- a/hw/xbox/nv2a/ptimer.c
+++ b/hw/xbox/nv2a/ptimer.c
@@ -59,7 +59,7 @@ uint64_t ptimer_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PTIMER, addr, r);
+    nv2a_reg_log_read(NV_PTIMER, addr, size, r);
     return r;
 }
 
@@ -67,7 +67,7 @@ void ptimer_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = opaque;
 
-    nv2a_reg_log_write(NV_PTIMER, addr, val);
+    nv2a_reg_log_write(NV_PTIMER, addr, size, val);
 
     switch (addr) {
     case NV_PTIMER_INTR_0:
diff --git a/hw/xbox/nv2a/pvideo.c b/hw/xbox/nv2a/pvideo.c
index 4c2aaae742..8ca1ac24ab 100644
--- a/hw/xbox/nv2a/pvideo.c
+++ b/hw/xbox/nv2a/pvideo.c
@@ -45,7 +45,7 @@ uint64_t pvideo_read(void *opaque, hwaddr addr, unsigned int size)
         break;
     }
 
-    nv2a_reg_log_read(NV_PVIDEO, addr, r);
+    nv2a_reg_log_read(NV_PVIDEO, addr, size, r);
     return r;
 }
 
@@ -53,7 +53,7 @@ void pvideo_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = opaque;
 
-    nv2a_reg_log_write(NV_PVIDEO, addr, val);
+    nv2a_reg_log_write(NV_PVIDEO, addr, size, val);
 
     switch (addr) {
     case NV_PVIDEO_BUFFER:
diff --git a/hw/xbox/nv2a/stubs.c b/hw/xbox/nv2a/stubs.c
index 3638a06108..2f52c6ecdc 100644
--- a/hw/xbox/nv2a/stubs.c
+++ b/hw/xbox/nv2a/stubs.c
@@ -26,7 +26,7 @@
                            hwaddr addr, \
                            unsigned int size) \
     { \
-        nv2a_reg_log_read(region_id, addr, 0); \
+        nv2a_reg_log_read(region_id, addr, size, 0); \
         return 0; \
     } \
     void name ## _write(void *opaque, \
@@ -34,7 +34,7 @@
                         uint64_t val, \
                         unsigned int size) \
     { \
-        nv2a_reg_log_write(region_id, addr, val); \
+        nv2a_reg_log_write(region_id, addr, size, val); \
     } \
 
 DEFINE_STUB(prma, NV_PRMA)
diff --git a/hw/xbox/nv2a/trace-events b/hw/xbox/nv2a/trace-events
new file mode 100644
index 0000000000..3ef0859636
--- /dev/null
+++ b/hw/xbox/nv2a/trace-events
@@ -0,0 +1,17 @@
+# See docs/devel/tracing.rst for syntax documentation.
+
+# nv2a.c
+nv2a_reg_read(const char *block, uint32_t addr, unsigned int size, uint64_t val) "%s addr 0x%"PRIx32" size %d val 0x%"PRIx64
+nv2a_reg_write(const char *block, uint32_t addr, unsigned int size, uint64_t val) "%s addr 0x%"PRIx32" size %d val 0x%"PRIx64
+nv2a_irq(uint32_t pending) "%08"PRIx32
+nv2a_dma_map(uint32_t obj_address, uint32_t dma_class, uint32_t dma_target, uint32_t dma_addr, uint32_t dma_limit) "obj 0x%08"PRIx32" class 0x%08"PRIx32" target 0x%08"PRIx32" addr 0x%08"PRIx32" limit 0x%08"PRIx32
+
+# pgraph.c
+nv2a_pgraph_method(uint32_t subchannel, uint32_t graphics_class, uint32_t method, const char *name, uint32_t offset, uint32_t parameter) "%d: 0x%"PRIx32" -> 0x%04"PRIx32" %s[%"PRId32"] 0x%"PRIx32
+nv2a_pgraph_method_abbrev(uint32_t subchannel, uint32_t graphics_class, uint32_t method, const char *name, unsigned int count) "%d: 0x%"PRIx32" -> 0x%04"PRIx32" %s * %d"
+nv2a_pgraph_method_unhandled(uint32_t subchannel, uint32_t graphics_class, uint32_t method, uint32_t parameter) "%d: 0x%"PRIx32" -> 0x%04"PRIx32" 0x%"PRIx32
+nv2a_pgraph_surface_cpu_access(uint32_t addr, uint32_t offset) "0x%08"PRIx32"+0x%"PRIx32
+nv2a_pgraph_surface_created(uint32_t addr, uint32_t end_addr) "[0x%08"PRIx32", 0x%08"PRIx32")"
+nv2a_pgraph_surface_invalidated(uint32_t addr) "0x%08"PRIx32
+nv2a_pgraph_flip_stall(void) ""
+nv2a_pgraph_flip_increment_write(uint32_t write3d_old, uint32_t write3d_new) "0x%"PRIx32" -> 0x%"PRIx32
diff --git a/hw/xbox/nv2a/trace.h b/hw/xbox/nv2a/trace.h
new file mode 100644
index 0000000000..fd655cbfaf
--- /dev/null
+++ b/hw/xbox/nv2a/trace.h
@@ -0,0 +1 @@
+#include "trace/trace-hw_xbox_nv2a.h"
diff --git a/hw/xbox/nv2a/user.c b/hw/xbox/nv2a/user.c
index b0429de495..28e917d4d6 100644
--- a/hw/xbox/nv2a/user.c
+++ b/hw/xbox/nv2a/user.c
@@ -66,7 +66,7 @@ uint64_t user_read(void *opaque, hwaddr addr, unsigned int size)
 
     qemu_mutex_unlock(&d->pfifo.lock);
 
-    nv2a_reg_log_read(NV_USER, addr, r);
+    nv2a_reg_log_read(NV_USER, addr, size, r);
     return r;
 }
 
@@ -74,7 +74,7 @@ void user_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
 {
     NV2AState *d = (NV2AState *)opaque;
 
-    nv2a_reg_log_write(NV_USER, addr, val);
+    nv2a_reg_log_write(NV_USER, addr, size, val);
 
     unsigned int channel_id = addr >> 16;
     assert(channel_id < NV2A_NUM_CHANNELS);
diff --git a/meson.build b/meson.build
index 88e1e34401..2376d49cfc 100644
--- a/meson.build
+++ b/meson.build
@@ -2186,6 +2186,7 @@ if have_system
     'softmmu',
     'ui',
     'hw/remote',
+    'hw/xbox/nv2a',
   ]
 endif
 if have_system or have_user