using System; using System.IO; using System.Threading; using allegro; public class Nes : Allegro { // Universal constants public const uint Ticks_Per_Scanline = 113; public Cartridge Cartridge { get; set; } public ProcessorNes6502 my6502; public Mapper myMapper; public PPU myPPU; Joypad myJoypad; public bool isQuitting; public bool hasQuit; public bool isDebugging; public bool isSaveRAMReadonly; public bool isPaused; public bool fix_bgchange; public bool fix_spritehit; public bool fix_scrolloffset1; public bool fix_scrolloffset2; public bool fix_scrolloffset3; byte[][] scratchRam; byte[] saveRam; public uint numOfInstructions; public byte ReadMemory8(ushort address) { byte returnvalue = 0; if (address < 0x2000) { if (address < 0x800) { returnvalue = scratchRam[0][address]; } else if (address < 0x1000) { returnvalue = scratchRam[1][address - 0x800]; } else if (address < 0x1800) { returnvalue = scratchRam[2][address - 0x1000]; } else { returnvalue = scratchRam[3][address - 0x1800]; } } else if (address < 0x6000) { switch (address) { case (0x2002): returnvalue = myPPU.Status_Register_Read(); break; case (0x2007): returnvalue = myPPU.VRAM_IO_Register_Read(); break; case (0x4016): returnvalue = myJoypad.Joypad_1_Read(); break; case (0x4017): returnvalue = myJoypad.Joypad_2_Read(); break; } } else if (address < 0x8000) { returnvalue = saveRam[address - 0x6000]; if (Cartridge.mapper == 5) returnvalue = 1; } else { returnvalue = myMapper.ReadPrgRom(address); } return returnvalue; } //This is optimized for the places the PC can be //Except, it doesn't seem to be faster public byte ReadMemory8PC(ushort address) { byte returnvalue = 0; /* if ((address >= 0x2000) && (address < 0x6000)) { Console.WriteLine("ERROR: PC = {0:x}", address); isQuitting = true; } */ if (address < 0x800) { returnvalue = scratchRam[0][address]; } else if (address < 0x1000) { returnvalue = scratchRam[1][address - 0x800]; } else if (address < 0x1800) { returnvalue = scratchRam[2][address - 0x1000]; } else if (address < 0x2000) { returnvalue = scratchRam[3][address - 0x1800]; } else if (/*(address >= 0x6000)&&*/(address < 0x8000)) { returnvalue = saveRam[address - 0x6000]; } //else if (address >= 0x8000){ else { returnvalue = myMapper.ReadPrgRom((ushort)address); } return returnvalue; } public ushort ReadMemory16(ushort address) { byte data_1; byte data_2; if (address < 0x2000) { if (address < 0x800) { data_1 = scratchRam[0][address]; data_2 = scratchRam[0][address + 1]; } else if (address < 0x1000) { data_1 = scratchRam[1][address - 0x800]; data_2 = scratchRam[1][address - 0x800 + 1]; } else if (address < 0x1800) { data_1 = scratchRam[2][address - 0x1000]; data_2 = scratchRam[2][address - 0x1000 + 1]; } else { data_1 = scratchRam[3][address - 0x1800]; data_2 = scratchRam[3][address - 0x1800 + 1]; } } else if (address < 0x8000) { data_1 = saveRam[address - 0x6000]; data_2 = saveRam[address - 0x6000 + 1]; //FIXME: At some point I need to fix mapper 5 //if (myCartridge.mapper == 5) // returnvalue = 1; } else { data_1 = myMapper.ReadPrgRom(address); data_2 = myMapper.ReadPrgRom((ushort)(address + 1)); } ushort data = (ushort)((data_2 << 8) + data_1); return data; } public byte WriteMemory8(ushort address, byte data) { if (address < 0x800) { scratchRam[0][address] = data; } else if (address < 0x1000) { scratchRam[1][address - 0x800] = data; } else if (address < 0x1800) { scratchRam[2][address - 0x1000] = data; } else if (address < 0x2000) { scratchRam[3][address - 0x1800] = data; } else if (address < 0x4000) { switch (address) { case (0x2000): myPPU.Control_Register_1_Write(data); break; case (0x2001): myPPU.Control_Register_2_Write(data); break; case (0x2003): myPPU.SpriteRam_Address_Register_Write(data); break; case (0x2005): myPPU.VRAM_Address_Register_1_Write(data); break; case (0x2006): myPPU.VRAM_Address_Register_2_Write(data); break; case (0x2007): myPPU.VRAM_IO_Register_Write(data); break; } } else if (address < 0x6000) { switch (address) { case (0x4014): myPPU.SpriteRam_DMA_Begin(data); break; case (0x4016): myJoypad.Joypad_1_Write(data); break; case (0x4017): myJoypad.Joypad_2_Write(data); break; //default: Console.WriteLine("UNKOWN WRITE: {0:x}", address); break; } } else if (address < 0x8000) { if (!isSaveRAMReadonly) saveRam[address - 0x6000] = data; } return 1; } public byte WriteMemory16(ushort address, ushort data) { Console.WriteLine("** ERROR: WriteMemory16 was used **"); return 255; } public void CheckForEvents() { if (key[KEY_ESC]) { QuitEngine(); } } public void InitializeEngine() { Cartridge = new Cartridge(); my6502 = new ProcessorNes6502(this); myMapper = new Mapper(this); myPPU = new PPU(this); myJoypad = new Joypad(); scratchRam = new byte[4][]; scratchRam[0] = new byte[0x800]; scratchRam[1] = new byte[0x800]; scratchRam[2] = new byte[0x800]; scratchRam[3] = new byte[0x800]; saveRam = new byte[0x2000]; isSaveRAMReadonly = false; isDebugging = false; isQuitting = false; isPaused = false; hasQuit = false; fix_bgchange = false; fix_spritehit = false; fix_scrolloffset1 = false; fix_scrolloffset2 = false; fix_scrolloffset3 = false; } public void RestartEngine() { isSaveRAMReadonly = false; isDebugging = false; isQuitting = false; isPaused = false; hasQuit = false; fix_bgchange = false; fix_spritehit = false; fix_scrolloffset1 = false; fix_scrolloffset2 = false; fix_scrolloffset3 = false; myPPU.RestartPPU(); } public void QuitEngine() { isQuitting = true; } public void RenderNextScanline() { //This is a tie-in function to the PPU //If we decide later that the CPU should have direct visibility, this //won't be needed if (myPPU.RenderNextScanline() == true) { my6502.Push16(my6502.pc_register); my6502.PushStatus(); my6502.pc_register = ReadMemory16(0xFFFA); } if (Cartridge.mapper == 4) { myMapper.TickTimer(); } } public void RunCart() { myMapper.mapperCartridge = Cartridge; myMapper.SetUpMapperDefaults(); my6502.pc_register = ReadMemory16(0xFFFC); my6502.RunProcessor(); hasQuit = true; } public Nes() { InitializeEngine(); myPPU.myVideo.StartVideo(); } }