Big refactoring, reintroducing state-arguments, moving lots of code from old to OpenXbox-comparable locations, all the while keeping it all compiling

This commit is contained in:
PatrickvL 2018-02-01 16:48:21 +01:00
parent 998e68b681
commit 1bf01df4b7
12 changed files with 1850 additions and 2194 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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:

View File

@ -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<std::mutex> lk(pfifo.cache1.mutex);
std::lock_guard<std::mutex> 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<std::mutex> lk(pfifo.cache1.mutex);
result = pfifo.cache1.pull_enabled;
std::lock_guard<std::mutex> lk(d->pfifo.cache1.mutex);
result = d->pfifo.cache1.pull_enabled;
} break;
case NV_PFIFO_CACHE1_ENGINE: {
std::lock_guard<std::mutex> lk(pfifo.cache1.mutex);
std::lock_guard<std::mutex> 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<std::mutex> lk(pfifo.cache1.mutex);
std::lock_guard<std::mutex> 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<std::mutex> lk(pfifo.cache1.mutex);
std::lock_guard<std::mutex> 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<std::mutex> lk(pfifo.cache1.mutex);
std::lock_guard<std::mutex> 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));

File diff suppressed because it is too large Load Diff

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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<long int>(std::chrono::duration_cast<std::chrono::nanoseconds>(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);

View File

@ -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;

View File

@ -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:

View File

@ -34,11 +34,504 @@
// ******************************************************************
#pragma once
#include "Cxbx.h" // For xbaddr
#include "devices\PCIDevice.h" // For PCIDevice
#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <GL/glew.h>
#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<CacheEntry*> cache;
std::queue<CacheEntry*> 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