mirror of https://github.com/xemu-project/xemu.git
nv2a: Start using tracing infrastructure for logging
This commit is contained in:
parent
a13f3f48a2
commit
be23a674f3
2
build.sh
2
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
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
# define NV2A_DPRINTF(format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
// #define DEBUG_NV2A_REG
|
||||
// #define DEBUG_NV2A_GL
|
||||
#ifdef DEBUG_NV2A_GL
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); \
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
#include "trace/trace-hw_xbox_nv2a.h"
|
|
@ -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);
|
||||
|
|
|
@ -2186,6 +2186,7 @@ if have_system
|
|||
'softmmu',
|
||||
'ui',
|
||||
'hw/remote',
|
||||
'hw/xbox/nv2a',
|
||||
]
|
||||
endif
|
||||
if have_system or have_user
|
||||
|
|
Loading…
Reference in New Issue