// Data cache fetch stage. cen64_flatten static inline void rsp_df_stage(struct rsp *rsp) { struct rsp_dfwb_latch *dfwb_latch = &rsp->pipeline.dfwb_latch; struct rsp_exdf_latch *exdf_latch = &rsp->pipeline.exdf_latch; const struct rsp_mem_request *request = &exdf_latch->request; uint32_t addr; dfwb_latch->common = exdf_latch->common; dfwb_latch->result = exdf_latch->result; if (request->type == RSP_MEM_REQUEST_NONE) return; addr = request->addr & 0xFFF; // Vector unit DMEM access. if (request->type != RSP_MEM_REQUEST_INT_MEM) { uint16_t *regp = rsp->cp2.regs[request->packet.p_vect.dest].e; unsigned element = request->packet.p_vect.element; rsp_vect_t reg, dqm; reg = rsp_vect_load_unshuffled_operand(regp); dqm = rsp_vect_load_unshuffled_operand(exdf_latch-> request.packet.p_vect.vdqm.e); // Make sure the vector data doesn't get // written into the scalar part of the RF. dfwb_latch->result.dest = 0; exdf_latch->request.packet.p_vect.vldst_func( rsp, addr, element, regp, reg, dqm); } // Scalar unit DMEM access. else { uint32_t rdqm = request->packet.p_int.rdqm; uint32_t wdqm = request->packet.p_int.wdqm; uint32_t data = request->packet.p_int.data; unsigned rshift = request->packet.p_int.rshift; uint32_t word; memcpy(&word, rsp->mem + addr, sizeof(word)); word = byteswap_32(word); dfwb_latch->result.result = rdqm & (((int32_t) word) >> rshift); word = byteswap_32((word & ~wdqm) | (data & wdqm)); memcpy(rsp->mem + addr, &word, sizeof(word)); } }
// Reads a word from cartridge ROM. int read_cart_rom(void *opaque, uint32_t address, uint32_t *word) { struct pi_controller *pi = (struct pi_controller *) opaque; unsigned offset = (address - ROM_CART_BASE_ADDRESS) & ~0x3; // TODO: Need to figure out correct behaviour. // Should this even happen to begin with? if (pi->rom == NULL || offset > pi->rom_size - sizeof(*word)) { *word = 0; return 0; } memcpy(word, pi->rom + offset, sizeof(*word)); *word = byteswap_32(*word); return 0; }
// DMA into the RSP's memory space. void rsp_dma_read(struct rsp *rsp) { uint32_t length = (rsp->regs[RSP_CP0_REGISTER_DMA_READ_LENGTH] & 0xFFF) + 1; uint32_t skip = rsp->regs[RSP_CP0_REGISTER_DMA_READ_LENGTH] >> 20 & 0xFFF; unsigned count = rsp->regs[RSP_CP0_REGISTER_DMA_READ_LENGTH] >> 12 & 0xFF; unsigned j, i = 0; // Force alignment. length = (length + 0x7) & ~0x7; rsp->regs[RSP_CP0_REGISTER_DMA_CACHE] &= ~0x3; rsp->regs[RSP_CP0_REGISTER_DMA_DRAM] &= ~0x7; // Check length. if (((rsp->regs[RSP_CP0_REGISTER_DMA_CACHE] & 0xFFF) + length) > 0x1000) length = 0x1000 - (rsp->regs[RSP_CP0_REGISTER_DMA_CACHE] & 0xFFF); do { uint32_t source = rsp->regs[RSP_CP0_REGISTER_DMA_DRAM] & 0x7FFFFC; uint32_t dest = rsp->regs[RSP_CP0_REGISTER_DMA_CACHE] & 0x1FFC; j = 0; do { uint32_t source_addr = (source + j) & 0x7FFFFC; uint32_t dest_addr = (dest + j) & 0x1FFC; uint32_t word; bus_read_word(rsp, source_addr, &word); // Update opcode cache. if (dest_addr & 0x1000) { rsp->opcode_cache[(dest_addr - 0x1000) >> 2] = *rsp_decode_instruction(word); } else { word = byteswap_32(word); } memcpy(rsp->mem + dest_addr, &word, sizeof(word)); j += 4; } while (j < length); rsp->regs[RSP_CP0_REGISTER_DMA_DRAM] += length + skip; rsp->regs[RSP_CP0_REGISTER_DMA_CACHE] += length; } while(++i <= count);
// Performs an (instantaneous) DMA. void ai_dma(struct ai_controller *ai) { struct bus_controller *bus; // Shove things into the audio context, slide the window. memcpy(&bus, ai, sizeof(bus)); // Need to raise an interrupt when the DMA engine // is doing the last 8-byte data bus transfer... if (likely(ai->fifo[ai->fifo_ri].length > 0)) { unsigned freq = (double) NTSC_DAC_FREQ / (ai->regs[AI_DACRATE_REG] + 1); unsigned samples = ai->fifo[ai->fifo_ri].length / 4;// - 1; // Shovel things into the audio context. uint8_t buf[0x40000]; unsigned i; ALuint buffer; ALint val; alGetSourcei(ai->ctx.source, AL_BUFFERS_PROCESSED, &val); if (val) { for (i = 0; i < ai->fifo[ai->fifo_ri].length / 4; i++) { uint32_t word; // Byteswap audio buffer to native format. // TODO: Can we specify endian-ness somehow? memcpy(&word, bus->ri->ram + (ai->fifo[ ai->fifo_ri].address + sizeof(word) * i), sizeof(word)); word = byteswap_32(word); memcpy(buf + sizeof(word) * i, &word, sizeof(word)); } alSourceUnqueueBuffers(ai->ctx.source, 1, &buffer); alBufferData(buffer, AL_FORMAT_STEREO16, buf, ai->fifo[ai->fifo_ri].length, 44100 /* ai->ctx.frequency */); alSourceQueueBuffers(ai->ctx.source, 1, &buffer); alGetSourcei(ai->ctx.source, AL_SOURCE_STATE, &val); if(val != AL_PLAYING) alSourcePlay(ai->ctx.source); if (alGetError() != AL_NO_ERROR) { fprintf(stderr, "OpenAL is angry!\n"); exit(255); } } ai->counter = (62500000.0 / freq) * samples; //printf("Wait %lu cycles to raise interrupt.\n", ai->counter); } // If the length was zero, just interrupt now? else { signal_rcp_interrupt(bus->vr4300, MI_INTR_AI); ai->fifo_ri = (ai->fifo_ri == 1) ? 0 : 1; ai->regs[AI_STATUS_REG] &= ~0x80000001; ai->counter = 0xFFFFFFFFU; ai->fifo_count--; } if (ai->fifo_count > 0) ai->regs[AI_STATUS_REG] |= 0x40000000; }