void CPU::scanline() { synchronize_smp(); synchronize_ppu(); synchronize_coprocessor(); system.scanline(); if(vcounter() == 0) hdma_init(); queue.enqueue(534, QueueEvent::DramRefresh); if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { queue.enqueue(1104 + 8, QueueEvent::HdmaRun); } if(vcounter() == input.latchy) { queue.enqueue(input.latchx, QueueEvent::ControllerLatch); } bool nmi_valid = status.nmi_valid; status.nmi_valid = vcounter() >= (ppu.overscan() == false ? 225 : 240); if(!nmi_valid && status.nmi_valid) { status.nmi_line = true; if(status.nmi_enabled) status.nmi_transition = true; } else if(nmi_valid && !status.nmi_valid) { status.nmi_line = false; } if(status.auto_joypad_poll_enabled && vcounter() == (ppu.overscan() == false ? 227 : 242)) { input.poll(); run_auto_joypad_poll(); } }
//called by ppu.tick() when Hcounter=0 void CPU::scanline() { status.lineclocks = lineclocks(); //forcefully sync S-CPU to other processors, in case chips are not communicating synchronize_ppu(); synchronize_smp(); synchronize_coprocessor(); system.scanline(); if(vcounter() == 0) { //HDMA init triggers once every frame status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); status.hdma_init_triggered = false; status.auto_joypad_counter = 0; } //DRAM refresh occurs once every scanline if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); status.dram_refreshed = false; //HDMA triggers once every visible scanline if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { status.hdma_position = 1104; status.hdma_triggered = false; } }
void CPU::add_clocks(unsigned clocks) { if(status.hirq_enabled) { if(status.virq_enabled) { unsigned cpu_time = vcounter() * 1364 + hcounter(); unsigned irq_time = status.vtime * 1364 + status.htime * 4; unsigned framelines = (system.region() == System::Region::NTSC ? 262 : 312) + field(); if(cpu_time > irq_time) irq_time += framelines * 1364; bool irq_valid = status.irq_valid; status.irq_valid = cpu_time <= irq_time && cpu_time + clocks > irq_time; if(!irq_valid && status.irq_valid) status.irq_line = true; } else { unsigned irq_time = status.htime * 4; if(hcounter() > irq_time) irq_time += 1364; bool irq_valid = status.irq_valid; status.irq_valid = hcounter() <= irq_time && hcounter() + clocks > irq_time; if(!irq_valid && status.irq_valid) status.irq_line = true; } if(status.irq_line) status.irq_transition = true; } else if(status.virq_enabled) { bool irq_valid = status.irq_valid; status.irq_valid = vcounter() == status.vtime; if(!irq_valid && status.irq_valid) status.irq_line = true; if(status.irq_line) status.irq_transition = true; } else { status.irq_valid = false; } tick(clocks); queue.tick(clocks); step(clocks); }
void CPU::add_clocks(unsigned clocks) { if(status.hirq_enabled) { if(status.virq_enabled) { unsigned cpu_time = vcounter() * 1364 + hcounter(); unsigned irq_time = status.virq_pos * 1364 + status.hirq_pos * 4; if(cpu_time > irq_time) irq_time += fieldlines() * 1364; bool irq_valid = status.irq_valid; status.irq_valid = cpu_time <= irq_time && cpu_time + clocks > irq_time; if(!irq_valid && status.irq_valid) status.irq_line = true; } else { unsigned irq_time = status.hirq_pos * 4; if(hcounter() > irq_time) irq_time += 1364; bool irq_valid = status.irq_valid; status.irq_valid = hcounter() <= irq_time && hcounter() + clocks > irq_time; if(!irq_valid && status.irq_valid) status.irq_line = true; } if(status.irq_line) status.irq_transition = true; } else if(status.virq_enabled) { bool irq_valid = status.irq_valid; status.irq_valid = vcounter() == status.virq_pos; if(!irq_valid && status.irq_valid) status.irq_line = true; if(status.irq_line) status.irq_transition = true; } else { status.irq_valid = false; } tick(clocks); queue.tick(clocks); step(clocks); }
//HVBJOY //7 = VBLANK acknowledge //6 = HBLANK acknowledge //5-1 = MDR //0 = JOYPAD acknowledge uint8 CPU::mmio_r4212() { uint8 r = (regs.mdr & 0x3e); uint16 vs = ppu.overscan() == false ? 225 : 240; if(vcounter() >= vs && vcounter() <= (vs + 2)) r |= 0x01; //auto joypad polling if(hcounter() <= 2 || hcounter() >= 1096) r |= 0x40; //hblank if(vcounter() >= vs) r |= 0x80; //vblank return r; }
void PPU::enter() { while(true) { if(scheduler.sync == Scheduler::SynchronizeMode::All) { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); } scanline(); if(vcounter() < display.height && vcounter()) { add_clocks(512); render_scanline(); add_clocks(lineclocks() - 512); } else { add_clocks(lineclocks()); } } }
void PPU::scanline() { if(vcounter() == 0) { frame(); bg1.frame(); bg2.frame(); bg3.frame(); bg4.frame(); } bg1.scanline(); bg2.scanline(); bg3.scanline(); bg4.scanline(); oam.scanline(); window.scanline(); screen.scanline(); }
void PPU::scanline() { line = vcounter(); if(line == 0) { frame(); //RTO flag reset regs.time_over = false; regs.range_over = false; } if(line == 1) { //mosaic reset for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; } else { for(int bg = BG1; bg <= BG4; bg++) { if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line; } if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; regs.mosaic_countdown--; } }
uint8 CPU::mmio_read(unsigned addr) { if((addr & 0xffc0) == 0x2140) { synchronize_smp(); return smp.port_read(addr & 3); } switch(addr & 0xffff) { case 0x2180: { uint8 result = bus.read(0x7e0000 | status.wram_addr); status.wram_addr = (status.wram_addr + 1) & 0x01ffff; return result; } case 0x4016: { uint8 result = regs.mdr & 0xfc; result |= input.port1->data() & 3; return result; } case 0x4017: { uint8 result = (regs.mdr & 0xe0) | 0x1c; result |= input.port2->data() & 3; if (!status.auto_joypad_poll_enabled) interface()->inputNotify(0x4017); return result; } case 0x4210: { uint8 result = (regs.mdr & 0x70); result |= status.nmi_line << 7; result |= 0x02; //CPU revision status.nmi_line = false; return result; } case 0x4211: { uint8 result = (regs.mdr & 0x7f); result |= status.irq_line << 7; status.irq_line = false; return result; } case 0x4212: { uint8 result = (regs.mdr & 0x3e); unsigned vbstart = ppu.overscan() == false ? 225 : 240; if(vcounter() >= vbstart && vcounter() <= vbstart + 2) result |= 0x01; if(hcounter() <= 2 || hcounter() >= 1096) result |= 0x40; if(vcounter() >= vbstart) result |= 0x80; return result; } case 0x4213: // interface()->inputNotify(0x4213); // if there are lag counter issues with super scope, uncomment this return status.pio; case 0x4214: return status.rddiv >> 0; case 0x4215: return status.rddiv >> 8; case 0x4216: return status.rdmpy >> 0; case 0x4217: return status.rdmpy >> 8; case 0x4218: interface()->inputNotify(0x4218); return status.joy1l; case 0x4219: interface()->inputNotify(0x4219); return status.joy1h; case 0x421a: interface()->inputNotify(0x421a); return status.joy2l; case 0x421b: interface()->inputNotify(0x421b); return status.joy2h; case 0x421c: interface()->inputNotify(0x421c); return status.joy3l; case 0x421d: interface()->inputNotify(0x421d); return status.joy3h; case 0x421e: interface()->inputNotify(0x421e); return status.joy4l; case 0x421f: interface()->inputNotify(0x421f); return status.joy4h; } if((addr & 0xff80) == 0x4300) { unsigned i = (addr >> 4) & 7; switch(addr & 0xff8f) { case 0x4300: { return (channel[i].direction << 7) | (channel[i].indirect << 6) | (channel[i].unused << 5) | (channel[i].reverse_transfer << 4) | (channel[i].fixed_transfer << 3) | (channel[i].transfer_mode << 0); } case 0x4301: return channel[i].dest_addr; case 0x4302: return channel[i].source_addr >> 0; case 0x4303: return channel[i].source_addr >> 8; case 0x4304: return channel[i].source_bank; case 0x4305: return channel[i].transfer_size >> 0; case 0x4306: return channel[i].transfer_size >> 8; case 0x4307: return channel[i].indirect_bank; case 0x4308: return channel[i].hdma_addr >> 0; case 0x4309: return channel[i].hdma_addr >> 8; case 0x430a: return channel[i].line_counter; case 0x430b: case 0x430f: return channel[i].unknown; } }
void PPU::scanline() { display.width = !hires() ? 256 : 512; display.height = !overscan() ? 225 : 240; if(vcounter() == 0) frame(); if(vcounter() == display.height && regs.display_disable == false) oam.address_reset(); }
uint16 PPUcounter::hdot() const { if(system.region.i == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) { return (hcounter() >> 2); } else { return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2;