diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index e245a6da57..c8de8c4e79 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -78,6 +78,11 @@ + + + + + diff --git a/BizHawk.Emulation/Computers/Commodore64/C64.core.cs b/BizHawk.Emulation/Computers/Commodore64/C64.core.cs index bde26b7f3b..a082a39812 100644 --- a/BizHawk.Emulation/Computers/Commodore64/C64.core.cs +++ b/BizHawk.Emulation/Computers/Commodore64/C64.core.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using BizHawk.Emulation.CPUs.M6502; @@ -8,25 +9,76 @@ namespace BizHawk.Emulation.Computers.Commodore64 { public partial class C64 : IEmulator { - public byte[] rom; + // source + public Cartridge cart; + public bool cartInserted; + public byte[] inputFile; + + // chipset + public Cia cia1; + public Cia cia2; public MOS6502X cpu; + public MemoryBus mem; + public Sid sid; + public VicII vic; + public VicSignals vicSignal; private void HardReset() { cpu = new MOS6502X(); cpu.ReadMemory = ReadMemory; cpu.WriteMemory = WriteMemory; - cpu.DummyReadMemory = ReadMemory; + cpu.DummyReadMemory = PeekMemory; + + // initialize cia timers + cia1 = new Cia(); + cia2 = new Cia(); + + // initialize vic + vicSignal = new VicSignals(); + vic = new VicII(vicSignal, VicIIMode.NTSC); + + // initialize sid + sid = new Sid(); + + // initialize memory (this must be done AFTER all other chips are initialized) + string romPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "C64Kernal"); + mem = new MemoryBus(romPath, vic, sid, cia1, cia2); + + // initialize media + Cartridge cart = new Cartridge(inputFile); + if (cart.valid) + { + mem.ApplyCartridge(cart); + } + + // initialize cpu (hard reset vector) + cpu.PC = (ushort)(ReadMemory(0xFFFC) + (ReadMemory(0xFFFD) << 8)); } + public byte PeekMemory(ushort addr) + { + return mem.Peek(addr); + } + + public byte PeekMemoryInt(int addr) + { + return mem.Peek((ushort)(addr & 0xFFFF)); + } + + public void PokeMemoryInt(int addr, byte val) + { + // todo + } + public byte ReadMemory(ushort addr) { - return 0; + return mem.Read(addr); } public void WriteMemory(ushort addr, byte value) { - //TODO + mem.Write(addr, value); } } } diff --git a/BizHawk.Emulation/Computers/Commodore64/C64.cs b/BizHawk.Emulation/Computers/Commodore64/C64.cs index 3abaf0881a..f1571fc4e1 100644 --- a/BizHawk.Emulation/Computers/Commodore64/C64.cs +++ b/BizHawk.Emulation/Computers/Commodore64/C64.cs @@ -11,11 +11,12 @@ namespace BizHawk.Emulation.Computers.Commodore64 { public C64(GameInfo game, byte[] rom, string romextension) { - videoProvider = new MyVideoProvider(this); - SetupMemoryDomains(); - CoreOutputComm = new CoreOutputComm(); - CoreInputComm = new CoreInputComm(); - HardReset(); + inputFile = rom; + SetupMemoryDomains(); + CoreOutputComm = new CoreOutputComm(); + CoreInputComm = new CoreInputComm(); + HardReset(); + videoProvider = new MyVideoProvider(vic); } public string SystemId { get { return "C64"; } } @@ -45,7 +46,6 @@ namespace BizHawk.Emulation.Computers.Commodore64 } /*TODO*/ - public int[] frameBuffer = new int[256 * 192]; //TODO public ISyncSoundProvider SyncSoundProvider { get { return null; } } //TODO public bool StartAsyncSound() { return true; } //TODO public void EndAsyncSound() { } //TODO @@ -58,12 +58,11 @@ namespace BizHawk.Emulation.Computers.Commodore64 public IController Controller { get; set; } public static readonly ControllerDefinition Atari7800ControllerDefinition = new ControllerDefinition { - Name = "Atari 7800 Basic Controller", //TODO + Name = "Commodore 64 Controller", //TODO BoolButtons = { - "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", - "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button", - "Reset", "Select" + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button" } }; @@ -89,8 +88,23 @@ namespace BizHawk.Emulation.Computers.Commodore64 _frame++; _islag = true; - //TODO - //Do stuff here + int cyclesPerSecond = (14318181 / 14 / 60); + + for (int i = 0; i < cyclesPerSecond; i++) + { + if (vicSignal.Interrupt) + { + cpu.IRQ = true; + } + if (vicSignal.AllowCpu) + { + cpu.ExecuteOne(); + } + vic.PerformCycle(); + sid.PerformCycle(); + cia1.PerformCycle(); + cia2.PerformCycle(); + } if (_islag) { @@ -104,27 +118,31 @@ namespace BizHawk.Emulation.Computers.Commodore64 private MySoundProvider soundProvider; private MyVideoProvider videoProvider; + class MyVideoProvider : IVideoProvider { - public int top = 0; //TODO - public int bottom = 262; //TODO - public int left = 0; //TODO - public int right = 320; //TODO + public int top; + public int bottom; + public int left; + public int right; - C64 emu; - public MyVideoProvider(C64 emu) + VicII vic; + public MyVideoProvider(VicII vic) { - this.emu = emu; + this.vic = vic; + + buffer = new int[vic.rasterWidth * vic.rasterTotalLines]; + top = 0; + bottom = vic.rasterTotalLines-1; + left = 0; + right = vic.rasterWidth-1; } - int[] buffer = new int[262 * 320]; + int[] buffer; public void FillFrameBuffer() { - for (int i = 0; i < buffer.Length; i++) - { - buffer[i] = 0; //TODO - } + Array.Copy(vic.buffer, buffer, buffer.Length); } public int[] GetVideoBuffer() @@ -141,7 +159,7 @@ namespace BizHawk.Emulation.Computers.Commodore64 private void SetupMemoryDomains() { var domains = new List(1); - domains.Add(new MemoryDomain("Main RAM", 1, Endian.Little, addr => 0xFF, null)); //TODO + domains.Add(new MemoryDomain("RAM", 0x10000, Endian.Little, new Func(PeekMemoryInt), new Action(PokeMemoryInt))); //TODO memoryDomains = domains.AsReadOnly(); } diff --git a/BizHawk.Emulation/Computers/Commodore64/Cartridge.cs b/BizHawk.Emulation/Computers/Commodore64/Cartridge.cs new file mode 100644 index 0000000000..f96df5a218 --- /dev/null +++ b/BizHawk.Emulation/Computers/Commodore64/Cartridge.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Computers.Commodore64 +{ + public class CartridgeChip + { + public int address; + public int bank; + public byte[] data; + public ushort romMask; + public int type; + } + + public class Cartridge + { + public List chips; + public bool exRomPin; + public bool gamePin; + public int type; + public bool valid; + public int version; + + public Cartridge(byte[] rom) + { + chips = new List(); + + if (rom.Length >= 0x50) + { + MemoryStream source = new MemoryStream(rom); + BinaryReader reader = new BinaryReader(source); + string idString; + + // note: cartridge files store values big-endian. + + idString = new string(reader.ReadChars(16)); + if (idString == "C64 CARTRIDGE ") + { + int headerLength = 0; + headerLength = reader.ReadByte(); + headerLength <<= 8; + headerLength |= reader.ReadByte(); + headerLength <<= 8; + headerLength |= reader.ReadByte(); + headerLength <<= 8; + headerLength |= reader.ReadByte(); + + version = reader.ReadByte(); + version <<= 8; + version |= reader.ReadByte(); + + type = reader.ReadByte(); + type <<= 8; + type |= reader.ReadByte(); + + exRomPin = (reader.ReadByte() == 1); + gamePin = (reader.ReadByte() == 1); + + reader.ReadBytes(6); // reserved + reader.ReadBytes(32); // name + + // skip the rest, don't need this info + if (headerLength > 0x40) + { + reader.ReadBytes(headerLength - 0x40); + } + + while (source.Position < rom.Length) + { + string chipID = new string(reader.ReadChars(4)); + + if (chipID == "CHIP") + { + CartridgeChip chip = new CartridgeChip(); + + int packetLength; + packetLength = reader.ReadByte(); + packetLength <<= 8; + packetLength |= reader.ReadByte(); + packetLength <<= 8; + packetLength |= reader.ReadByte(); + packetLength <<= 8; + packetLength |= reader.ReadByte(); + packetLength -= 16; + + chip.type = reader.ReadByte(); + chip.type <<= 8; + chip.type |= reader.ReadByte(); + + chip.bank = reader.ReadByte(); + chip.bank <<= 8; + chip.bank |= reader.ReadByte(); + + chip.address = reader.ReadByte(); + chip.address <<= 8; + chip.address |= reader.ReadByte(); + + int size; + size = reader.ReadByte(); + size <<= 8; + size |= reader.ReadByte(); + + chip.data = reader.ReadBytes(size); + chip.romMask = (ushort)(size - 1); + + packetLength -= size; + if (packetLength > 0) + { + // discard extra bytes + reader.ReadBytes(packetLength); + } + + chips.Add(chip); + } + else + { + break; + } + } + + valid = (chips.Count > 0); + } + } + } + + public byte Read(ushort addr) + { + CartridgeChip currentChip = chips[0]; + return currentChip.data[addr & currentChip.romMask]; + } + + public void Write(ushort addr, byte val) + { + // can't write to rom but we can process DE00/DF00 here + } + } +} diff --git a/BizHawk.Emulation/Computers/Commodore64/Cia.cs b/BizHawk.Emulation/Computers/Commodore64/Cia.cs new file mode 100644 index 0000000000..7139809590 --- /dev/null +++ b/BizHawk.Emulation/Computers/Commodore64/Cia.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Computers.Commodore64 +{ + public class Cia + { + public int cycles; + public byte[] regs; + + public Cia() + { + regs = new byte[0x10]; + } + + public void PerformCycle() + { + unchecked + { + cycles++; + } + } + + public byte Read(ushort addr) + { + byte result = 0; + + switch (addr & 0x0F) + { + case 0x00: + break; + case 0x01: + break; + case 0x02: + break; + case 0x03: + break; + case 0x04: + break; + case 0x05: + break; + case 0x06: + break; + case 0x07: + break; + case 0x08: + break; + case 0x09: + break; + case 0x0A: + break; + case 0x0B: + break; + case 0x0C: + break; + case 0x0D: + break; + case 0x0E: + break; + case 0x0F: + break; + default: + break; + } + + return result; + } + + public void Write(ushort addr, byte val) + { + switch (addr & 0x0F) + { + case 0x00: + break; + case 0x01: + break; + case 0x02: + break; + case 0x03: + break; + case 0x04: + break; + case 0x05: + break; + case 0x06: + break; + case 0x07: + break; + case 0x08: + break; + case 0x09: + break; + case 0x0A: + break; + case 0x0B: + break; + case 0x0C: + break; + case 0x0D: + break; + case 0x0E: + break; + case 0x0F: + break; + default: + break; + } + } + } +} diff --git a/BizHawk.Emulation/Computers/Commodore64/MemBus.cs b/BizHawk.Emulation/Computers/Commodore64/MemBus.cs new file mode 100644 index 0000000000..b8d02d6501 --- /dev/null +++ b/BizHawk.Emulation/Computers/Commodore64/MemBus.cs @@ -0,0 +1,451 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Computers.Commodore64 +{ + public enum MemoryBusDesignation + { + Disabled, + RAM, + Basic, + Kernal, + IO, + Character, + ROMLo, + ROMHi, + Vic, + Sid, + ColorRam, + Cia1, + Cia2, + Expansion1, + Expansion2 + } + + public class MemoryBusLayout + { + public MemoryBusDesignation Mem1000 = MemoryBusDesignation.RAM; + public MemoryBusDesignation Mem8000 = MemoryBusDesignation.RAM; + public MemoryBusDesignation MemA000 = MemoryBusDesignation.RAM; + public MemoryBusDesignation MemC000 = MemoryBusDesignation.RAM; + public MemoryBusDesignation MemD000 = MemoryBusDesignation.RAM; + public MemoryBusDesignation MemE000 = MemoryBusDesignation.RAM; + } + + public class MemoryBus + { + // chips + public Cia cia1; + public Cia cia2; + public VicII vic; + public Sid sid; + + // storage + public Cartridge cart; + public bool cartInserted = false; + + // roms + public byte[] basicRom; + public byte[] charRom; + public bool exRomPin = true; + public bool gamePin = true; + public byte[] kernalRom; + public MemoryBusLayout layout; + + // ram + public byte[] colorRam; + public byte[] ram; + + // registers + public byte busData; + public byte cpu00; // register $00 + public byte cpu01; // register $01 + public bool readTrigger = true; + public bool writeTrigger = true; + + public MemoryBus(string sourceFolder, VicII newVic, Sid newSid, Cia newCia1, Cia newCia2) + { + ram = new byte[0x10000]; + WipeMemory(); + + string basicFile = "basic"; + string charFile = "chargen"; + string kernalFile = "kernal"; + + basicRom = File.ReadAllBytes(Path.Combine(sourceFolder, basicFile)); + charRom = File.ReadAllBytes(Path.Combine(sourceFolder, charFile)); + kernalRom = File.ReadAllBytes(Path.Combine(sourceFolder, kernalFile)); + colorRam = new byte[0x1000]; + + vic = newVic; + sid = newSid; + cia1 = newCia1; + cia2 = newCia2; + cpu00 = 0x2F; + cpu01 = 0x37; + + layout = new MemoryBusLayout(); + UpdateLayout(); + } + + public void ApplyCartridge(Cartridge newCart) + { + cart = newCart; + cartInserted = true; + exRomPin = cart.exRomPin; + gamePin = cart.gamePin; + UpdateLayout(); + } + + public MemoryBusDesignation GetDesignation(ushort addr) + { + MemoryBusDesignation result; + + if (addr < 0x1000) + { + result = MemoryBusDesignation.RAM; + } + else if (addr < 0x8000) + { + result = layout.Mem1000; + } + else if (addr < 0xA000) + { + result = layout.Mem8000; + } + else if (addr < 0xC000) + { + result = layout.MemA000; + } + else if (addr < 0xD000) + { + result = layout.MemC000; + } + else if (addr < 0xE000) + { + result = layout.MemD000; + } + else + { + result = layout.MemE000; + } + + if (result == MemoryBusDesignation.IO) + { + addr &= 0x0FFF; + if (addr < 0x0400) + { + result = MemoryBusDesignation.Vic; + } + else if (addr < 0x0800) + { + result = MemoryBusDesignation.Sid; + } + else if (addr < 0x0C00) + { + result = MemoryBusDesignation.ColorRam; + } + else if (addr < 0x0D00) + { + result = MemoryBusDesignation.Cia1; + } + else if (addr < 0x0E00) + { + result = MemoryBusDesignation.Cia2; + } + else if (addr < 0x0F00) + { + result = MemoryBusDesignation.Expansion1; + } + else + { + result = MemoryBusDesignation.Expansion2; + } + } + + return result; + } + + public byte Peek(ushort addr) + { + byte result; + + if (addr == 0x0000) + { + result = cpu00; + } + else if (addr == 0x0001) + { + result = cpu01; + } + else + { + MemoryBusDesignation des = GetDesignation(addr); + + switch (des) + { + case MemoryBusDesignation.Basic: + result = basicRom[addr & 0x1FFF]; + break; + case MemoryBusDesignation.Character: + result = charRom[addr & 0x0FFF]; + break; + case MemoryBusDesignation.Vic: + result = vic.regs[addr & 0x3F]; + break; + case MemoryBusDesignation.Sid: + result = sid.regs[addr & 0x1F]; + break; + case MemoryBusDesignation.ColorRam: + result = colorRam[addr & 0x03FF]; + break; + case MemoryBusDesignation.Cia1: + result = cia1.regs[addr & 0x0F]; + break; + case MemoryBusDesignation.Cia2: + result = cia2.regs[addr & 0x0F]; + break; + case MemoryBusDesignation.Expansion1: + result = 0; + break; + case MemoryBusDesignation.Expansion2: + result = 0; + break; + case MemoryBusDesignation.Kernal: + result = kernalRom[addr & 0x1FFF]; + break; + case MemoryBusDesignation.RAM: + result = ram[addr]; + break; + case MemoryBusDesignation.ROMHi: + result = cart.chips[0].data[addr & cart.chips[0].romMask]; + break; + case MemoryBusDesignation.ROMLo: + result = cart.chips[0].data[addr & cart.chips[0].romMask]; + break; + default: + return 0; + } + } + + busData = result; + return result; + } + + public byte Read(ushort addr) + { + byte result; + + if (addr == 0x0000) + { + result = cpu00; + } + else if (addr == 0x0001) + { + result = cpu01; + } + else + { + MemoryBusDesignation des = GetDesignation(addr); + + switch (des) + { + case MemoryBusDesignation.Basic: + result = basicRom[addr & 0x1FFF]; + break; + case MemoryBusDesignation.Character: + result = charRom[addr & 0x0FFF]; + break; + case MemoryBusDesignation.Vic: + result = vic.Read(addr); + break; + case MemoryBusDesignation.Sid: + result = sid.Read(addr); + break; + case MemoryBusDesignation.ColorRam: + result = (byte)((busData & 0xF0) | (colorRam[addr & 0x03FF])); + break; + case MemoryBusDesignation.Cia1: + result = cia1.Read(addr); + break; + case MemoryBusDesignation.Cia2: + result = cia2.Read(addr); + break; + case MemoryBusDesignation.Expansion1: + result = 0; + break; + case MemoryBusDesignation.Expansion2: + result = 0; + break; + case MemoryBusDesignation.Kernal: + result = kernalRom[addr & 0x1FFF]; + break; + case MemoryBusDesignation.RAM: + result = ram[addr]; + break; + case MemoryBusDesignation.ROMHi: + result = cart.Read(addr); + break; + case MemoryBusDesignation.ROMLo: + result = cart.Read(addr); + break; + default: + return 0; + } + } + + busData = result; + return result; + } + + public void UpdateLayout() + { + bool loRom = ((cpu01 & 0x01) != 0); + bool hiRom = ((cpu01 & 0x02) != 0); + bool ioEnable = ((cpu01 & 0x04) != 0); + + if (loRom && hiRom && exRomPin && gamePin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.Basic; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.Kernal; + } + else if (loRom && !hiRom && exRomPin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.RAM; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.RAM; + } + else if (loRom && !hiRom && !exRomPin && !gamePin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.RAM; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.RAM; + layout.MemE000 = MemoryBusDesignation.RAM; + } + else if ((!loRom && hiRom && gamePin) || (!loRom && !hiRom && !exRomPin)) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.RAM; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.Kernal; + } + else if (!loRom && !hiRom && gamePin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.RAM; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = MemoryBusDesignation.RAM; + layout.MemE000 = MemoryBusDesignation.RAM; + } + else if (loRom && hiRom && gamePin && !exRomPin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.ROMLo; + layout.MemA000 = MemoryBusDesignation.Basic; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.Kernal; + } + else if (!loRom && hiRom && !gamePin && !exRomPin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.RAM; + layout.MemA000 = MemoryBusDesignation.ROMHi; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.Kernal; + } + else if (loRom && hiRom && !gamePin && !exRomPin) + { + layout.Mem1000 = MemoryBusDesignation.RAM; + layout.Mem8000 = MemoryBusDesignation.ROMLo; + layout.MemA000 = MemoryBusDesignation.ROMHi; + layout.MemC000 = MemoryBusDesignation.RAM; + layout.MemD000 = ioEnable ? MemoryBusDesignation.IO : MemoryBusDesignation.Character; + layout.MemE000 = MemoryBusDesignation.Kernal; + } + else if (!gamePin && exRomPin) + { + layout.Mem1000 = MemoryBusDesignation.Disabled; + layout.Mem8000 = MemoryBusDesignation.ROMLo; + layout.MemA000 = MemoryBusDesignation.Disabled; + layout.MemC000 = MemoryBusDesignation.Disabled; + layout.MemD000 = MemoryBusDesignation.IO; + layout.MemE000 = MemoryBusDesignation.ROMHi; + } + } + + public void WipeMemory() + { + for (int i = 0; i < 0x10000; i += 0x80) + { + for (int j = 0; j < 0x40; j++) + ram[i + j] = 0x00; + for (int j = 0x40; j < 0x80; j++) + ram[i + j] = 0xFF; + } + } + + public void Write(ushort addr, byte val) + { + if (addr == 0x0000) + { + cpu00 = val; + } + else if (addr == 0x0001) + { + cpu01 &= (byte)(~cpu00); + cpu01 |= (byte)(cpu00 & val); + UpdateLayout(); + } + else + { + MemoryBusDesignation des = GetDesignation(addr); + + switch (des) + { + case MemoryBusDesignation.Vic: + vic.Write(addr, val); + break; + case MemoryBusDesignation.Sid: + sid.Write(addr, val); + break; + case MemoryBusDesignation.ColorRam: + colorRam[addr & 0x03FF] = (byte)(val & 0x0F); + break; + case MemoryBusDesignation.Cia1: + cia1.Write(addr, val); + break; + case MemoryBusDesignation.Cia2: + cia2.Write(addr, val); + break; + case MemoryBusDesignation.Expansion1: + break; + case MemoryBusDesignation.Expansion2: + break; + case MemoryBusDesignation.RAM: + ram[addr] = val; + break; + default: + break; + } + } + busData = val; + } + } +} diff --git a/BizHawk.Emulation/Computers/Commodore64/Sid.cs b/BizHawk.Emulation/Computers/Commodore64/Sid.cs new file mode 100644 index 0000000000..b8ddad567c --- /dev/null +++ b/BizHawk.Emulation/Computers/Commodore64/Sid.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Computers.Commodore64 +{ + public enum SidMode + { + Sid6581, + Sid8580 + } + + public class Sid + { + public byte[] regs; + + public Sid() + { + regs = new byte[0x20]; + } + + public void PerformCycle() + { + } + + public byte Read(ushort addr) + { + switch (addr & 0x1F) + { + default: + return 0; + } + } + + public void Write(ushort addr, byte val) + { + switch (addr & 0x1F) + { + default: + break; + } + } + } +} diff --git a/BizHawk.Emulation/Computers/Commodore64/VicII.cs b/BizHawk.Emulation/Computers/Commodore64/VicII.cs new file mode 100644 index 0000000000..ac41cf1228 --- /dev/null +++ b/BizHawk.Emulation/Computers/Commodore64/VicII.cs @@ -0,0 +1,446 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Computers.Commodore64 +{ + public enum VicIIMode + { + NTSC, + PAL + } + + public class VicII + { + // buffer + public int[] buffer; + public int bufferSize; + + // palette + public int[] palette = + { + Colors.ARGB(0x00, 0x00, 0x00), + Colors.ARGB(0xFF, 0xFF, 0xFF), + Colors.ARGB(0x68, 0x37, 0x2B), + Colors.ARGB(0x70, 0xA4, 0xB2), + Colors.ARGB(0x6F, 0x3D, 0x86), + Colors.ARGB(0x58, 0x8D, 0x43), + Colors.ARGB(0x35, 0x28, 0x79), + Colors.ARGB(0xB8, 0xC7, 0x6F), + Colors.ARGB(0x6F, 0x4F, 0x25), + Colors.ARGB(0x43, 0x39, 0x00), + Colors.ARGB(0x9A, 0x67, 0x59), + Colors.ARGB(0x44, 0x44, 0x44), + Colors.ARGB(0x6C, 0x6C, 0x6C), + Colors.ARGB(0x9A, 0xD2, 0x84), + Colors.ARGB(0x6C, 0x5E, 0xB5), + Colors.ARGB(0x95, 0x95, 0x95) + }; + + // interrupts + public bool interrupt; + public bool lightPenInterrupt; + public bool lightPenInterruptEnabled; + public bool rasterInterrupt; + public bool rasterInterruptEnabled; + public bool spriteBackgroundInterrupt; + public bool spriteBackgroundInterruptEnabled; + public bool spriteSpriteInterrupt; + public bool spriteSpriteInterruptEnabled; + + // memory + public int characterMemoryOffset; + public int screenMemoryOffset; + + // lightpen + public int lightPenX; + public int lightPenY; + + // raster + public int[] backgroundColor; + public bool backgroundMode; + public bool bitmapMode; + public int borderColor; + public bool borderOn; + public byte[] charBuffer; + public bool extendHeight; + public bool extendWidth; + public int horizontalScroll; + public bool multiColorMode; + public int rasterInterruptLine; + public int rasterOffset; + public int rasterOffsetX; + public int rasterOffsetY; + public int rasterTotalLines; + public int rasterWidth; + public bool screenEnabled; + public int verticalScroll; + public int visibleHeight; + public int visibleWidth; + + // sprites + public bool[] spriteBackgroundCollision; + public bool[] spriteCollision; + public int[] spriteColor; + public bool[] spriteEnabled; + public int[] spriteExtraColor; + public bool[] spriteMultiColor; + public bool[] spritePriority; + public bool[] spriteStretchHorizontal; + public bool[] spriteStretchVertical; + public int[] spriteX; + public int[] spriteY; + + public VicSignals cpuSignal; + public byte[] regs; + + public VicII(VicSignals signals, VicIIMode videoMode) + { + cpuSignal = signals; + + switch (videoMode) + { + case VicIIMode.NTSC: + rasterWidth = 512; + rasterTotalLines = 263; + visibleWidth = 368; + visibleHeight = 235; + break; + case VicIIMode.PAL: + break; + default: + break; + } + + // initialize raster + backgroundColor = new int[4]; + charBuffer = new byte[40]; + + // initialize sprites + spriteBackgroundCollision = new bool[8]; + spriteCollision = new bool[8]; + spriteColor = new int[8]; + spriteEnabled = new bool[8]; + spriteExtraColor = new int[2]; + spriteMultiColor = new bool[8]; + spritePriority = new bool[8]; + spriteStretchHorizontal = new bool[8]; + spriteStretchVertical = new bool[8]; + spriteX = new int[8]; + spriteY = new int[8]; + + // initialize buffer + buffer = new int[rasterWidth * rasterTotalLines]; + bufferSize = buffer.Length; + + // initialize registers + regs = new byte[0x40]; + for (int i = 0x2F; i <= 0x3F; i++) + regs[i] = 0xFF; + UpdateRegs(); + } + + public void LockBus() + { + cpuSignal.Lock(); + } + + public void PerformCycle() + { + for (int i = 0; i < 8; i++) + WritePixel(borderColor); + + if (rasterInterruptEnabled && (rasterOffsetY == rasterInterruptLine) && (rasterOffsetX == 0)) + { + // removed for now + //rasterInterrupt = true; + } + + interrupt = + (rasterInterrupt & rasterInterruptEnabled) | + (spriteSpriteInterrupt & spriteSpriteInterruptEnabled) | + (spriteBackgroundInterrupt & spriteBackgroundInterruptEnabled) | + (lightPenInterrupt & lightPenInterruptEnabled); + + cpuSignal.Interrupt = interrupt; + UpdateRegs(); + } + + public byte Read(ushort addr) + { + return regs[addr & 0x3F]; + } + + public void UnlockBus() + { + cpuSignal.Unlock(); + } + + public void UpdateRegs() + { + // these registers update on their own + + regs[0x11] = (byte) + ((verticalScroll & 0x07) | + (extendHeight ? 0x08 : 0x00) | + (screenEnabled ? 0x10 : 0x00) | + (bitmapMode ? 0x20 : 0x00) | + (backgroundMode ? 0x40 : 0x00) | + ((rasterOffsetY & 0x100) >> 1)); + regs[0x12] = (byte)(rasterOffsetY & 0xFF); + regs[0x13] = (byte)(lightPenX >> 1); + regs[0x14] = (byte)(lightPenY); + regs[0x19] = (byte) + ((rasterInterrupt ? 0x01 : 0x00) | + (spriteBackgroundInterrupt ? 0x02 : 0x00) | + (spriteSpriteInterrupt ? 0x04 : 0x00) | + (lightPenInterrupt ? 0x08 : 0x00) | + (interrupt ? 0x80 : 0x00)); + } + + public void Write(ushort addr, byte val) + { + int index = 0; + bool allowWrite = true; + addr &= 0x3F; + + switch (addr & 0x3F) + { + case 0x00: + case 0x02: + case 0x04: + case 0x06: + case 0x08: + case 0x0A: + case 0x0C: + case 0x0E: + index = addr >> 1; + spriteX[index] &= 0xFF; + spriteX[index] |= val; + break; + case 0x01: + case 0x03: + case 0x05: + case 0x07: + case 0x09: + case 0x0B: + case 0x0D: + case 0x0F: + index = addr >> 1; + spriteY[index] &= 0xFF; + spriteY[index] |= val; + break; + case 0x10: + spriteX[0] = (spriteX[0] & 0xFF) | ((val & 0x01) << 8); + spriteX[1] = (spriteX[1] & 0xFF) | ((val & 0x02) << 8); + spriteX[2] = (spriteX[2] & 0xFF) | ((val & 0x04) << 8); + spriteX[3] = (spriteX[3] & 0xFF) | ((val & 0x08) << 8); + spriteX[4] = (spriteX[4] & 0xFF) | ((val & 0x10) << 8); + spriteX[5] = (spriteX[5] & 0xFF) | ((val & 0x20) << 8); + spriteX[6] = (spriteX[6] & 0xFF) | ((val & 0x40) << 8); + spriteX[7] = (spriteX[7] & 0xFF) | ((val & 0x80) << 8); + break; + case 0x11: + verticalScroll = val & 0x07; + extendHeight = ((val & 0x08) != 0x00); + screenEnabled = ((val & 0x10) != 0x00); + bitmapMode = ((val & 0x20) != 0x00); + backgroundMode = ((val & 0x40) != 0x00); + rasterInterruptLine = (rasterInterruptLine & 0xFF) | ((val & 0x80) << 1); + val = (byte)((val & 0x7F) | ((rasterOffsetY & 0x100) >> 1)); + break; + case 0x12: + rasterInterruptLine = (rasterInterruptLine & 0x100) | val; + allowWrite = false; + break; + case 0x15: + spriteEnabled[0] = ((val & 0x01) != 0x00); + spriteEnabled[1] = ((val & 0x02) != 0x00); + spriteEnabled[2] = ((val & 0x04) != 0x00); + spriteEnabled[3] = ((val & 0x08) != 0x00); + spriteEnabled[4] = ((val & 0x10) != 0x00); + spriteEnabled[5] = ((val & 0x20) != 0x00); + spriteEnabled[6] = ((val & 0x40) != 0x00); + spriteEnabled[7] = ((val & 0x80) != 0x00); + break; + case 0x16: + horizontalScroll = val & 0x07; + extendWidth = ((val & 0x08) != 0x00); + multiColorMode = ((val & 0x10) != 0x00); + bitmapMode = ((val & 0x20) != 0x00); + val |= 0xC0; + break; + case 0x17: + spriteStretchVertical[0] = ((val & 0x01) != 0x00); + spriteStretchVertical[1] = ((val & 0x02) != 0x00); + spriteStretchVertical[2] = ((val & 0x04) != 0x00); + spriteStretchVertical[3] = ((val & 0x08) != 0x00); + spriteStretchVertical[4] = ((val & 0x10) != 0x00); + spriteStretchVertical[5] = ((val & 0x20) != 0x00); + spriteStretchVertical[6] = ((val & 0x40) != 0x00); + spriteStretchVertical[7] = ((val & 0x80) != 0x00); + break; + case 0x18: + characterMemoryOffset = (int)(val & 0x0E) << 10; + screenMemoryOffset = (int)(val & 0xF0) << 6; + break; + case 0x19: + rasterInterrupt = ((val & 0x01) != 0); + spriteSpriteInterrupt = ((val & 0x02) != 0); + spriteBackgroundInterrupt = ((val & 0x04) != 0); + lightPenInterrupt = ((val & 0x08) != 0); + allowWrite = false; + break; + case 0x1A: + rasterInterruptEnabled = ((val & 0x01) != 0); + spriteSpriteInterruptEnabled = ((val & 0x02) != 0); + spriteBackgroundInterruptEnabled = ((val & 0x04) != 0); + lightPenInterruptEnabled = ((val & 0x08) != 0); + break; + case 0x1B: + spritePriority[0] = ((val & 0x01) != 0x00); + spritePriority[1] = ((val & 0x02) != 0x00); + spritePriority[2] = ((val & 0x04) != 0x00); + spritePriority[3] = ((val & 0x08) != 0x00); + spritePriority[4] = ((val & 0x10) != 0x00); + spritePriority[5] = ((val & 0x20) != 0x00); + spritePriority[6] = ((val & 0x40) != 0x00); + spritePriority[7] = ((val & 0x80) != 0x00); + break; + case 0x1C: + spriteMultiColor[0] = ((val & 0x01) != 0x00); + spriteMultiColor[1] = ((val & 0x02) != 0x00); + spriteMultiColor[2] = ((val & 0x04) != 0x00); + spriteMultiColor[3] = ((val & 0x08) != 0x00); + spriteMultiColor[4] = ((val & 0x10) != 0x00); + spriteMultiColor[5] = ((val & 0x20) != 0x00); + spriteMultiColor[6] = ((val & 0x40) != 0x00); + spriteMultiColor[7] = ((val & 0x80) != 0x00); + break; + case 0x1D: + spriteStretchHorizontal[0] = ((val & 0x01) != 0x00); + spriteStretchHorizontal[1] = ((val & 0x02) != 0x00); + spriteStretchHorizontal[2] = ((val & 0x04) != 0x00); + spriteStretchHorizontal[3] = ((val & 0x08) != 0x00); + spriteStretchHorizontal[4] = ((val & 0x10) != 0x00); + spriteStretchHorizontal[5] = ((val & 0x20) != 0x00); + spriteStretchHorizontal[6] = ((val & 0x40) != 0x00); + spriteStretchHorizontal[7] = ((val & 0x80) != 0x00); + break; + case 0x1E: + spriteCollision[0] = ((val & 0x01) != 0x00); + spriteCollision[1] = ((val & 0x02) != 0x00); + spriteCollision[2] = ((val & 0x04) != 0x00); + spriteCollision[3] = ((val & 0x08) != 0x00); + spriteCollision[4] = ((val & 0x10) != 0x00); + spriteCollision[5] = ((val & 0x20) != 0x00); + spriteCollision[6] = ((val & 0x40) != 0x00); + spriteCollision[7] = ((val & 0x80) != 0x00); + break; + case 0x1F: + spriteBackgroundCollision[0] = ((val & 0x01) != 0x00); + spriteBackgroundCollision[1] = ((val & 0x02) != 0x00); + spriteBackgroundCollision[2] = ((val & 0x04) != 0x00); + spriteBackgroundCollision[3] = ((val & 0x08) != 0x00); + spriteBackgroundCollision[4] = ((val & 0x10) != 0x00); + spriteBackgroundCollision[5] = ((val & 0x20) != 0x00); + spriteBackgroundCollision[6] = ((val & 0x40) != 0x00); + spriteBackgroundCollision[7] = ((val & 0x80) != 0x00); + break; + case 0x20: + borderColor = val; + break; + case 0x21: + backgroundColor[0] = val; + break; + case 0x22: + backgroundColor[1] = val; + break; + case 0x23: + backgroundColor[2] = val; + break; + case 0x24: + backgroundColor[3] = val; + break; + case 0x25: + spriteExtraColor[0] = val; + break; + case 0x26: + spriteExtraColor[1] = val; + break; + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + index = addr - 0x27; + spriteColor[index] = val; + break; + default: + allowWrite = false; + break; + } + + if (allowWrite) + regs[addr] = val; + } + + private void WritePixel(int value) + { + buffer[rasterOffset] = palette[value]; + rasterOffset++; + if (rasterOffset >= bufferSize) + rasterOffset = 0; + + rasterOffsetX = (rasterOffset & 0x1FF); + rasterOffsetY = (rasterOffset >> 9); + } + } + + public class VicSignals + { + public bool AllowCpu; + public bool Interrupt; + public int LockCounter; + + public VicSignals() + { + AllowCpu = true; + Interrupt = false; + LockCounter = 0; + } + + public void Lock() + { + if (AllowCpu) + { + LockCounter = 4; + } + } + + public void PerformCycle() + { + if (AllowCpu) + { + if (LockCounter > 0) + { + LockCounter--; + if (LockCounter == 0) + { + AllowCpu = false; + } + } + } + } + + public void Unlock() + { + AllowCpu = true; + } + + } +}