public override bool OnUserCreate() { // Load the cartridge cartridge = new Cartridge("../../../../TestRoms/nestest.nes"); if (!cartridge.IsImageValid) { return(false); } // Insert into NES nes.InsertCartridge(cartridge); // Extract dissassembly mapAsm = nes.Cpu6502.Disassemble(0x8000, 0xFFFF); // Init channels for (var i = 0; i < audio.Length; i++) { audio[i] = new Deque <ushort>(); for (int j = 0; j < 120; j++) { audio[i].AddToBack(0); } } nes.Reset(); // Initialise PGEX sound system, and give it a function to // call which returns a sound sample on demand pInstance = this; nes.SetSampleFrequency(44100); olcPGEXSoundManaged.InitialiseAudio(44100, 1, 8, 512); olcPGEXSoundManaged.SetUserSynthFunction(SoundOut); return(true); }
public void LoadCartridge(string path) { cartridge = new Cartridge(path); switch (cartridge.mapperNumber) { case 0: mapper = new NROM(this); break; default: System.Console.WriteLine("Mapper is not supported"); break; } cpu.PowerUp(); cpuMemory.Reset(); }
public void CpuWrite(ushort address, byte data) { // The cartridge "sees all" and has the facility to veto // the propagation of the bus transaction if it requires. // This allows the cartridge to map any address to some // other data, including the facility to divert transactions // with other physical devices. The NES does not do this // but I figured it might be quite a flexible way of adding // "custom" hardware to the NES in the future! if (Cartridge.CpuWrite(address, data)) { return; } if (address >= 0x0000 && address <= 0x1FFF) { // System RAM Address Range. The range covers 8KB, though // there is only 2KB available. That 2KB is "mirrored" // through this address range. Using bitwise AND to mask // the bottom 11 bits is the same as addr % 2048. Ram[address & 0x07FF] = data; } else if (address >= 0x2000 && address <= 0x3FFF) { // PPU Address range. The PPU only has 8 primary registers // and these are repeated throughout this range. We can // use bitwise AND operation to mask the bottom 3 bits, // which is the equivalent of addr % 8. Ppu2C02.CpuWrite((ushort)(address & 0x0007), data); } else if ((address >= 0x4000 && address <= 0x4013) || address == 0x4015 || address == 0x4017) // NES APU { Apu2A03.CpuWrite(address, data); } else if (address == 0x4014) { // A write to this address initiates a DMA transfer dma_page = data; dma_addr = 0x00; dma_transfer = true; } else if (address >= 0x4016 && address <= 0x4017) { // "Lock In" controller state at this time ControllerState[address & 0x0001] = Controller[address & 0x0001]; } }
public bool Clock() { // Clocking. The heart and soul of an emulator. The running // frequency is controlled by whatever calls this function. // So here we "divide" the clock as necessary and call // the peripheral devices clock() function at the correct // times. // The fastest clock frequency the digital system cares // about is equivalent to the PPU clock. So the PPU is clocked // each time this function is called... Ppu2C02.Clock(); // ...also clock the APU Apu2A03.Clock(); // The CPU runs 3 times slower than the PPU so we only call its // clock() function every 3 times this function is called. We // have a global counter to keep track of this. if (SystemClockCounter % 3 == 0) { // Is the system performing a DMA transfer form CPU memory to // OAM memory on PPU?... if (dma_transfer) { // ...Yes! We need to wait until the next even CPU clock cycle // before it starts... if (dma_dummy) { // ...So hang around in here each clock until 1 or 2 cycles // have elapsed... if (SystemClockCounter % 2 == 1) { // ...and finally allow DMA to start dma_dummy = false; } } else { // DMA can take place! if (SystemClockCounter % 2 == 0) { // On even clock cycles, read from CPU bus dma_data = CpuRead((ushort)(dma_page << 8 | dma_addr), false); } else { // On odd clock cycles, write to PPU OAM Ppu2C02.OAM[dma_addr] = dma_data; // Increment the lo byte of the address dma_addr++; // If this wraps around, we know that 256 // bytes have been written, so end the DMA // transfer, and proceed as normal if (dma_addr == 0x00) { dma_transfer = false; dma_dummy = true; } } } } else { // No DMA happening, the CPU is in control of its // own destiny. Go forth my friend and calculate // awesomeness for many generations to come... Cpu6502.Clock(); } } // Synchronising with Audio bool bAudioSampleReady = false; dAudioTime += dAudioTimePerNESClock; if (dAudioTime >= dAudioTimePerSystemSample) { dAudioTime -= dAudioTimePerSystemSample; dAudioSample = Apu2A03.GetOutputSample(); bAudioSampleReady = true; } // The PPU is capable of emitting an interrupt to indicate the // vertical blanking period has been entered. If it has, we need // to send that irq to the CPU. if (Ppu2C02.Nmi) { Ppu2C02.Nmi = false; Cpu6502.NMI(); } // Check if cartridge is requesting IRQ if (Cartridge.GetMapper().IrqState) { Cartridge.GetMapper().IrqClear(); Cpu6502.IRQ(); } SystemClockCounter++; return bAudioSampleReady; }
public void InsertCartridge(Cartridge cartridge) { Cartridge = cartridge; Ppu2C02.ConnectCartridge(cartridge); Devices.Add(Cartridge); }