__attribute__((hot, flatten)) static inline void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { struct ppu_state *ppu = &state->ppu; uint32_t reg = offset & 0x7; if(reg == 4) { ppu->oam[ppu->oam_addr++] = value; ppu->open_bus = value; return; } else if(reg == 1) { ppu->reg_mask = value; ppu->open_bus = value; return; } else if(reg == 0) { ppu->reg_ctrl = value; ppu->temp_addr = (ppu->temp_addr & 0xf3ff) | ((value & 0x03) << 10); ppu->open_bus = value; return; } else if(reg == 3) { ppu->oam_addr = value; ppu->open_bus = value; return; } else if(reg == 5) { if(ppu->write_latch == 0) { ppu->fine_x = value & 0x07; ppu->temp_addr = (ppu->temp_addr & ~0x001f) | (value >> 3); ppu->write_latch = 1; } else { ppu->temp_addr = (ppu->temp_addr & ~0x73e0) | ((value & 0x07) << 12) | ((value & 0xf8) << 2); ppu->write_latch = 0; } ppu->open_bus = value; return; } else if(reg == 6) { if(ppu->write_latch == 0) { ppu->temp_addr = (ppu->temp_addr & 0x00ff) | ((value & 0x3f) << 8); ppu->write_latch = 1; } else { ppu->temp_addr = (ppu->temp_addr & 0xff00) | value; ppu->vram_addr = ppu->temp_addr; ppu->write_latch = 0; } ppu->open_bus = value; return; } else if(reg == 7) { uint32_t addr = ppu->vram_addr; if(addr < 0x2000) { state->mapper_function.chr_write(state, addr, value); } else if(addr < 0x3f00) { state->mapper_function.ciram_write(state, addr, value); } else if(addr < 0x4000) { uint32_t pal_addr = addr & 0x1f; if((pal_addr & 3) == 0) { pal_addr &= ~0x10; } ppu->palette[pal_addr] = value; } ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; ppu->open_bus = value; return; } } __attribute__((hot, flatten)) static inline uint8_t ppu_read(struct nes_state *state, uint32_t offset) { struct ppu_state *ppu = &state->ppu; uint8_t result = ppu->open_bus; uint32_t reg = offset & 0x7; if(reg == 2) { result = ppu->reg_status; ppu->reg_status &= ~0x80; ppu->write_latch = 0; ppu->open_bus = result; return result; } else if(reg == 4) { result = ppu->oam[ppu->oam_addr]; ppu->open_bus = result; return result; } else if(reg == 7) { uint32_t addr = ppu->vram_addr; if(addr < 0x2000) { result = ppu->vram_read_buffer; ppu->vram_read_buffer = state->mapper_function.chr_read(state, addr); } else if(addr < 0x3f00) { result = ppu->vram_read_buffer; ppu->vram_read_buffer = state->mapper_function.ciram_read(state, addr); } else if(addr < 0x4000) { uint32_t pal_addr = addr & 0x1f; if((pal_addr & 0x13) == 0x10) { pal_addr &= ~0x10; } result = ppu->palette[pal_addr]; } ppu->vram_addr += (ppu->reg_ctrl & 0x04) ? 32 : 1; ppu->open_bus = result; return result; } return result; } static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); __attribute__((hot)) static inline void ppu_dma_4014(struct nes_state *state, uint8_t page) { uint32_t base = page << 8; // Add 1 or 2 idle cycles depending on current CPU cycle uint8_t idle_cycles = (state->cpu.cycles & 1) ? 1 : 2; for(uint8_t i = 0; i < idle_cycles; i++) { state->cpu.cycles++; ppu_tick(state); apu_tick(state); } for(uint32_t i = 0; i < 256; i++) { uint32_t addr = base + i; state->cpu.cycles++; ppu_tick(state); apu_tick(state); uint8_t value = memory_read_dma(state, addr); state->cpu.cycles++; ppu_tick(state); apu_tick(state); ppu_write(state, 4, value); } }