private static byte Read(int address) { BUS_RW_P = BUS_RW; BUS_ADDRESS = address; BUS_RW = true; ClockComponents(); if (address < 0x2000)// Internal 2K Work RAM (mirrored to 800h-1FFFh) { return(WRAM[address & 0x7FF]); } else if (address < 0x4000) { #region Internal PPU Registers (mirrored to 2008h-3FFFh) switch (address & 7) { case 2: // $2002 { ppu_2002_temp = 0; if (vbl_flag) { ppu_2002_temp |= 0x80; } if (spr_0Hit) { ppu_2002_temp |= 0x40; } if (spr_overflow) { ppu_2002_temp |= 0x20; } vbl_flag_temp = false; vram_flipflop = false; // NMI disable effect only at vbl set period (HClock between 1 and 3) if ((VClock == vbl_vclock_Start) && (HClock <= 3)) { NMI_Current = (vbl_flag_temp & nmi_enabled); } return(ppu_2002_temp); } case 4: // $2004 { ppu_2004_temp = oam_ram[oam_address]; if (VClock < 240 && IsRenderingOn()) { if (HClock < 64) { ppu_2004_temp = 0xFF; } else if (HClock < 192) { ppu_2004_temp = oam_ram[((HClock - 64) << 1) & 0xFC]; } else if (HClock < 256) { ppu_2004_temp = ((HClock & 1) == 1) ? oam_ram[0xFC] : oam_ram[((HClock - 192) << 1) & 0xFC]; } else if (HClock < 320) { ppu_2004_temp = 0xFF; } else { ppu_2004_temp = oam_ram[0]; } } return(ppu_2004_temp); } case 7: // $2007 { ppu_2007_temp = 0; if ((vram_address & 0x3F00) == 0x3F00) { // The return value should be from the palettes bank ppu_2007_temp = (byte)(palettes_bank[vram_address & ((vram_address & 0x03) == 0 ? 0x0C : 0x1F)] & grayscale); // fill buffer from chr or nametables vram_address_temp_access1 = vram_address & 0x2FFF; if (vram_address_temp_access1 < 0x2000) { reg2007buffer = board.ReadCHR(ref vram_address_temp_access1, false); } else { reg2007buffer = board.ReadNMT(ref vram_address_temp_access1); } } else { ppu_2007_temp = reg2007buffer; // fill buffer vram_address_temp_access1 = vram_address & 0x3FFF; if (vram_address_temp_access1 < 0x2000) { reg2007buffer = board.ReadCHR(ref vram_address_temp_access1, false); } else if (vram_address_temp_access1 < 0x3F00) { reg2007buffer = board.ReadNMT(ref vram_address_temp_access1); } else { reg2007buffer = palettes_bank[vram_address_temp_access1 & ((vram_address_temp_access1 & 0x03) == 0 ? 0x0C : 0x1F)]; } } vram_address = (vram_address + vram_increament) & 0x7FFF; board.OnPPUAddressUpdate(ref vram_address); return(ppu_2007_temp); } } #endregion } else if (address < 0x4020) { #region Internal APU Registers switch (address) { case 0x4015: { temp_4015 = 0; // Channels enable if (sq1_duration_counter > 0) { temp_4015 |= 0x01; } if (sq2_duration_counter > 0) { temp_4015 |= 0x02; } if (trl_duration_counter > 0) { temp_4015 |= 0x04; } if (noz_duration_counter > 0) { temp_4015 |= 0x08; } if (dmc_dmaSize > 0) { temp_4015 |= 0x10; } // IRQ if (FrameIrqFlag) { temp_4015 |= 0x40; } if (DeltaIrqOccur) { temp_4015 |= 0x80; } FrameIrqFlag = false; IRQFlags &= ~IRQ_APU; return(temp_4015); } case 0x4016: { temp_4016 = (byte)(PORT0 & 1); PORT0 >>= 1; if (IsZapperConnected) { temp_4016 |= zapper.GetData(); } if (IsVSUnisystem) { temp_4016 |= VSUnisystemDIP.GetData4016(); } return(temp_4016); } case 0x4017: { temp_4017 = (byte)(PORT1 & 1); PORT1 >>= 1; if (IsZapperConnected) { temp_4017 |= zapper.GetData(); } if (IsVSUnisystem) { temp_4017 |= VSUnisystemDIP.GetData4017(); } return(temp_4017); } } #endregion } else if (address < 0x6000)// Cartridge Expansion Area almost 8K { return(board.ReadEXP(ref address)); } else if (address < 0x8000)// Cartridge SRAM Area 8K { return(board.ReadSRM(ref address)); } else if (address <= 0xFFFF)// Cartridge PRG-ROM Area 32K { return(board.ReadPRG(ref address)); } // Should not reach here ! return(0); }
public byte Read(int address) { BUS_RW_P = BUS_RW; BUS_ADDRESS = address; BUS_RW = true; #region Clock Components this.ppu.Clock(); /* * NMI edge detector polls the status of the NMI line during φ2 of each CPU cycle * (i.e., during the second half of each cycle) */ this.interrupts.PollInterruptStatus(); this.ppu.Clock(); this.ppu.Clock(); if (this.emulator.DoPalAdditionalClock) // In pal system .. { this.emulator.palCyc++; if (this.emulator.palCyc == 5) { this.ppu.Clock(); this.emulator.palCyc = 0; } } this.apu.Clock(); this.dma.Clock(); board.OnCPUClock(); #endregion if (address < 0x2000) // Internal 2K Work RAM (mirrored to 800h-1FFFh) { return(WRAM[address & 0x7FF]); } else if (address < 0x4000) { #region Internal PPU Registers (mirrored to 2008h-3FFFh) switch (address & 7) { case 2: // $2002 { this.ppu.ppu_2002_temp = 0; if (this.interrupts.vbl_flag) { this.ppu.ppu_2002_temp |= 0x80; } if (this.ppu.spr_0Hit) { this.ppu.ppu_2002_temp |= 0x40; } if (this.ppu.spr_overflow) { this.ppu.ppu_2002_temp |= 0x20; } this.interrupts.vbl_flag_temp = false; this.ppu.vram_flipflop = false; this.interrupts.CheckNMI(); // NMI disable effect only at vbl set period (HClock between 1 and 3) return(this.ppu.ppu_2002_temp); } case 4: // $2004 { this.ppu.ppu_2004_temp = oam_ram[this.ppu.oam_address]; if (this.ppu.VClock < 240 && this.ppu.IsRenderingOn()) { if (this.ppu.HClock < 64) { this.ppu.ppu_2004_temp = 0xFF; } else if (this.ppu.HClock < 192) { this.ppu.ppu_2004_temp = oam_ram[((this.ppu.HClock - 64) << 1) & 0xFC]; } else if (this.ppu.HClock < 256) { this.ppu.ppu_2004_temp = ((this.ppu.HClock & 1) == 1) ? oam_ram[0xFC] : oam_ram[((this.ppu.HClock - 192) << 1) & 0xFC]; } else if (this.ppu.HClock < 320) { this.ppu.ppu_2004_temp = 0xFF; } else { this.ppu.ppu_2004_temp = oam_ram[0]; } } return(this.ppu.ppu_2004_temp); } case 7: // $2007 { this.ppu.ppu_2007_temp = 0; if ((this.ppu.vram_address & 0x3F00) == 0x3F00) { // The return value should be from the palettes bank this.ppu.ppu_2007_temp = (byte)(palettes_bank[this.ppu.vram_address & ((this.ppu.vram_address & 0x03) == 0 ? 0x0C : 0x1F)] & this.ppu.grayscale); // fill buffer from chr or nametables this.ppu.vram_address_temp_access1 = this.ppu.vram_address & 0x2FFF; if (this.ppu.vram_address_temp_access1 < 0x2000) { this.ppu.reg2007buffer = board.ReadCHR(ref this.ppu.vram_address_temp_access1, false); } else { this.ppu.reg2007buffer = board.ReadNMT(ref this.ppu.vram_address_temp_access1); } } else { this.ppu.ppu_2007_temp = this.ppu.reg2007buffer; // fill buffer this.ppu.vram_address_temp_access1 = this.ppu.vram_address & 0x3FFF; if (this.ppu.vram_address_temp_access1 < 0x2000) { this.ppu.reg2007buffer = board.ReadCHR(ref this.ppu.vram_address_temp_access1, false); } else if (this.ppu.vram_address_temp_access1 < 0x3F00) { this.ppu.reg2007buffer = board.ReadNMT(ref this.ppu.vram_address_temp_access1); } else { this.ppu.reg2007buffer = palettes_bank[this.ppu.vram_address_temp_access1 & ((this.ppu.vram_address_temp_access1 & 0x03) == 0 ? 0x0C : 0x1F)]; } } this.ppu.vram_address = (this.ppu.vram_address + this.ppu.vram_increament) & 0x7FFF; board.OnPPUAddressUpdate(ref this.ppu.vram_address); return(this.ppu.ppu_2007_temp); } } #endregion } else if (address < 0x4020) { #region Internal APU Registers switch (address) { case 0x4015: { temp_4015 = 0; // Channels enable if (this.apu.pulse1Channel.Duration_counter > 0) { temp_4015 |= 0x01; } if (this.apu.pulse2Channel.Duration_counter > 0) { temp_4015 |= 0x02; } if (this.apu.triangleChannel.Duration_counter > 0) { temp_4015 |= 0x04; } if (this.apu.noiseChannel.Duration_counter > 0) { temp_4015 |= 0x08; } if (this.apu.dmcChannel.dmc_dmaSize > 0) { temp_4015 |= 0x10; } // IRQ if (this.apu.FrameIrqFlag) { temp_4015 |= 0x40; } if (this.apu.dmcChannel.DeltaIrqOccur) { temp_4015 |= 0x80; } this.apu.FrameIrqFlag = false; Interrupts.IRQFlags &= ~Interrupts.IRQ_APU; return(temp_4015); } case 0x4016: { temp_4016 = (byte)(this.input.PORT0 & 1); this.input.PORT0 >>= 1; if (this.input.IsZapperConnected) { temp_4016 |= this.input.zapper.GetData(); } if (this.legacy.IsVSUnisystem) { temp_4016 |= this.input.VSUnisystemDIP.GetData4016(); } return(temp_4016); } case 0x4017: { temp_4017 = (byte)(this.input.PORT1 & 1); this.input.PORT1 >>= 1; if (this.input.IsZapperConnected) { temp_4017 |= this.input.zapper.GetData(); } if (this.legacy.IsVSUnisystem) { temp_4017 |= this.input.VSUnisystemDIP.GetData4017(); } return(temp_4017); } } #endregion } else if (address < 0x6000) // Cartridge Expansion Area almost 8K { return(board.ReadEXP(ref address)); } else if (address < 0x8000) // Cartridge SRAM Area 8K { return(board.ReadSRM(ref address)); } else if (address <= 0xFFFF) // Cartridge PRG-ROM Area 32K { return(board.ReadPRG(ref address)); } // Should not reach here ! return(0); }