__attribute__((always_inline, hot)) static inline void ppu_write(struct nes_state *state, uint32_t offset, uint8_t value) { struct ppu_state *ppu = &state->ppu; switch(offset & 7) { case 0: { // 2000 ppu->reg_ctrl = value; ppu->temp_addr = (ppu->temp_addr & 0xf3ff) | ((value & 0x03) << 10); ppu->open_bus = value; } break; case 1: { // 2001 ppu->reg_mask = value; ppu->open_bus = value; } break; case 3: { // 2003 ppu->oam_addr = value; ppu->open_bus = value; } break; case 4: { // 2004 ppu->oam[ppu->oam_addr++] = value; ppu->open_bus = value; } break; case 5: { // 2005 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; } break; case 6: { // 2006 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; } break; case 7: { // 2007 uint32_t addr = ppu->vram_addr; if(LIKELY(addr < 0x2000)) { state->mapper.chr_write(state, addr, value); } else if(LIKELY(addr < 0x3f00)) { state->mapper.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; } break; } } __attribute__((always_inline, hot)) 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; switch(offset & 7) { case 2: { // 2002 result = ppu->reg_status; ppu->reg_status &= ~0x80; ppu->write_latch = 0; } break; case 4: { // 2004 result = ppu->oam[ppu->oam_addr]; } break; case 7: { // 2007 uint32_t addr = ppu->vram_addr; if(LIKELY(addr < 0x2000)) { result = ppu->vram_read_buffer; ppu->vram_read_buffer = state->mapper.chr_read(state, addr); } else if(LIKELY(addr < 0x3f00)) { result = ppu->vram_read_buffer; ppu->vram_read_buffer = state->mapper.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; } break; } ppu->open_bus = result; return result; } static uint8_t memory_read_dma(struct nes_state *state, uint32_t offset); __attribute__((always_inline, 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->cycles & 1) ? 1 : 2; for(uint8_t i = 0; i < idle_cycles; i++) { state->cycles++; ppu_tick(state); // apu_tick(state); } for(uint32_t i = 0; i < 256; i++) { uint32_t addr = base + i; state->cycles++; ppu_tick(state); // apu_tick(state); uint8_t value = memory_read_dma(state, addr); state->cycles++; ppu_tick(state); // apu_tick(state); ppu_write(state, 4, value); } }