From 2a5be0d42ca8b9e4f37ee591e2b70337c4a20b57 Mon Sep 17 00:00:00 2001
From: saxxonpike <saxxonpike@gmail.com>
Date: Mon, 12 Nov 2012 16:37:11 +0000
Subject: [PATCH] commodore64: rewrote cycle processing code, VIC should be
 100% cycle accurate now. Also added preparation for PAL support

---
 .../Computers/Commodore64/C64.PeekPoke.cs     |  10 +-
 .../Computers/Commodore64/C64.cs              |   2 +-
 .../Computers/Commodore64/MemBus.cs           |   8 +-
 .../Computers/Commodore64/VicII.cs            | 531 ++++++++++++------
 4 files changed, 379 insertions(+), 172 deletions(-)

diff --git a/BizHawk.Emulation/Computers/Commodore64/C64.PeekPoke.cs b/BizHawk.Emulation/Computers/Commodore64/C64.PeekPoke.cs
index ee90ce9fa1..0a46592896 100644
--- a/BizHawk.Emulation/Computers/Commodore64/C64.PeekPoke.cs
+++ b/BizHawk.Emulation/Computers/Commodore64/C64.PeekPoke.cs
@@ -9,12 +9,12 @@ namespace BizHawk.Emulation.Computers.Commodore64
 	{
 		public byte PeekCia0(int addr)
 		{
-			return cia0.Peek(addr);
+			return cia0.regs[addr];
 		}
 
 		public byte PeekCia1(int addr)
 		{
-			return cia1.Peek(addr);
+			return cia1.regs[addr];
 		}
 
 		public byte PeekColorRAM(int addr)
@@ -34,17 +34,17 @@ namespace BizHawk.Emulation.Computers.Commodore64
 
 		public byte PeekRAM(int addr)
 		{
-			return mem.PeekRam(addr);
+			return mem.ram[addr];
 		}
 
 		public byte PeekSid(int addr)
 		{
-			return sid.Peek(addr);
+			return sid.regs[addr];
 		}
 
 		public byte PeekVic(int addr)
 		{
-			return vic.Peek(addr);
+			return vic.regs[addr];
 		}
 
 		public void PokeCia0(int addr, byte val)
diff --git a/BizHawk.Emulation/Computers/Commodore64/C64.cs b/BizHawk.Emulation/Computers/Commodore64/C64.cs
index 4f12476d51..b1c83681fa 100644
--- a/BizHawk.Emulation/Computers/Commodore64/C64.cs
+++ b/BizHawk.Emulation/Computers/Commodore64/C64.cs
@@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
 		private void SetupMemoryDomains()
 		{
 			var domains = new List<MemoryDomain>(1);
-			domains.Add(new MemoryDomain("Memory", 0x10000, Endian.Little, new Func<int, byte>(PeekMemoryInt), new Action<int, byte>(PokeMemoryInt)));
+			domains.Add(new MemoryDomain("System Bus", 0x10000, Endian.Little, new Func<int, byte>(PeekMemoryInt), new Action<int, byte>(PokeMemoryInt)));
 			domains.Add(new MemoryDomain("RAM", 0x10000, Endian.Little, new Func<int, byte>(PeekRAM), new Action<int, byte>(PokeRAM)));
 			domains.Add(new MemoryDomain("CIA0", 0x10, Endian.Little, new Func<int, byte>(PeekCia0), new Action<int, byte>(PokeCia0)));
 			domains.Add(new MemoryDomain("CIA1", 0x10, Endian.Little, new Func<int, byte>(PeekCia1), new Action<int, byte>(PokeCia1)));
diff --git a/BizHawk.Emulation/Computers/Commodore64/MemBus.cs b/BizHawk.Emulation/Computers/Commodore64/MemBus.cs
index b58f79a774..7c129c5b1e 100644
--- a/BizHawk.Emulation/Computers/Commodore64/MemBus.cs
+++ b/BizHawk.Emulation/Computers/Commodore64/MemBus.cs
@@ -219,19 +219,19 @@ namespace BizHawk.Emulation.Computers.Commodore64
 						result = charRom[addr & 0x0FFF];
 						break;
 					case MemoryDesignation.Vic:
-						result = vic.regs[addr & 0x3F];
+						result = vic.Peek(addr & 0x3F);
 						break;
 					case MemoryDesignation.Sid:
-						result = sid.regs[addr & 0x1F];
+						result = sid.Peek(addr & 0x1F);
 						break;
 					case MemoryDesignation.ColorRam:
 						result = (byte)(colorRam[addr & 0x03FF] | (busData & 0xF0));
 						break;
 					case MemoryDesignation.Cia0:
-						result = cia0.regs[addr & 0x0F];
+						result = cia0.Peek(addr & 0x0F);
 						break;
 					case MemoryDesignation.Cia1:
-						result = cia1.regs[addr & 0x0F];
+						result = cia1.Peek(addr & 0x0F);
 						break;
 					case MemoryDesignation.Expansion0:
 						result = 0;
diff --git a/BizHawk.Emulation/Computers/Commodore64/VicII.cs b/BizHawk.Emulation/Computers/Commodore64/VicII.cs
index f21a96bd12..9fad93ac9e 100644
--- a/BizHawk.Emulation/Computers/Commodore64/VicII.cs
+++ b/BizHawk.Emulation/Computers/Commodore64/VicII.cs
@@ -518,6 +518,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
 		private Action FetchC;
 		private Action FetchG;
 		private Func<int> Plotter;
+		private Action PerformCycleFunction;
 
 		public VicII(ChipSignals newSignal, Region newRegion)
 		{
@@ -540,32 +541,13 @@ namespace BizHawk.Emulation.Computers.Commodore64
 					visibleWidth = 352;
 					visibleHeight = 232;
 					renderOffset = 0;
+					PerformCycleFunction = PerformCycleNTSC;
 					break;
 				case Region.PAL:
 					break;
 				default:
 					break;
 			}
-
-			// initialize raster
-			rasterWidth = totalCycles * 8;
-			rasterOffsetX = rasterLineLeft;
-			borderOnMain = true;
-			borderOnVertical = true;
-
-			// initialize buffer
-			buffer = new int[visibleWidth * visibleHeight];
-			bufferSize = buffer.Length;
-
-			// initialize screen buffer
-			characterMemory = new byte[40];
-			colorMemory = new byte[40];
-			pixelBuffer = new int[12];
-			pixelBufferForeground = new bool[12];
-			pixelBufferIndex = 0;
-
-			// initialize registers
-			spriteFetchIndex = 0;
 			HardReset();
 		}
 
@@ -642,15 +624,35 @@ namespace BizHawk.Emulation.Computers.Commodore64
 
 		public void HardReset()
 		{
+			// initialize raster
+			rasterWidth = totalCycles * 8;
+			rasterOffsetX = rasterLineLeft;
+			borderOnMain = true;
+			borderOnVertical = true;
+
+			// initialize buffer
+			buffer = new int[visibleWidth * visibleHeight];
+			bufferSize = buffer.Length;
+
+			// initialize screen buffer
+			characterMemory = new byte[40];
+			colorMemory = new byte[40];
+			pixelBuffer = new int[12];
+			pixelBufferForeground = new bool[12];
+			pixelBufferIndex = 0;
+
+			// initialize registers
+			spriteFetchIndex = 0;
 			idle = true;
 			refreshAddress = 0x3FFF;
 			regs = new VicIIRegs();
 			regs.RC = 7;
 			signal.VicAEC = true;
 			signal.VicIRQ = false;
+
+			// initialize screen
 			UpdateBorder();
 			UpdatePlotter();
-			spriteFetchIndex = 17;
 		}
 
 		public byte Peek(int addr)
@@ -658,6 +660,14 @@ namespace BizHawk.Emulation.Computers.Commodore64
 			return regs[addr & 0x3F];
 		}
 
+		private void PerformBorderCheck()
+		{
+			if ((regs.RASTER == borderTop) && (regs.DEN))
+				borderOnVertical = false;
+			if (regs.RASTER == borderBottom)
+				borderOnVertical = true;
+		}
+
 		public void PerformCycle()
 		{
 			if (cycle >= totalCycles)
@@ -666,7 +676,8 @@ namespace BizHawk.Emulation.Computers.Commodore64
 				AdvanceRaster();
 			}
 
-			ProcessDisplayRegisters();
+			PerformCycleCommon();
+			PerformCycleFunction();
 			RenderCycle();
 
 			// increment cycle
@@ -675,12 +686,8 @@ namespace BizHawk.Emulation.Computers.Commodore64
 			signal.VicIRQ = regs.IRQ;
 		}
 
-		public void Poke(int addr, byte val)
-		{
-			regs[addr & 0x3F] = val;
-		}
-
-		private void ProcessDisplayRegisters()
+		// these operations are done every cycle
+		private void PerformCycleCommon()
 		{
 			// display enable check on line 030 (this must be run every cycle)
 			if (regs.RASTER == 0x030)
@@ -699,163 +706,363 @@ namespace BizHawk.Emulation.Computers.Commodore64
 			for (int i = 0; i < 8; i++)
 				if (!regs.MxYE[i])
 					regs.MYE[i] = true;
+		}
 
-			// these actions are exclusive
-			if ((regs.RASTER == 0x000 && cycle == 1) || (regs.RASTER > 0x000 && cycle == 0))
+		// operations timed to NTSC
+		private void PerformCycleNTSC()
+		{
+			switch (cycle)
 			{
-				// IRQ is processed on cycle 1 of line 0 and cycle 0 on all other lines
-				if (regs.RASTER == rasterInterruptLine)
-					regs.IRST = true;
+				case 0:
+					// rasterline IRQ happens on cycle 1 on rasterline 0
+					if (regs.RASTER > 0 && regs.RASTER == rasterInterruptLine)
+						regs.IRST = true;
+					PerformSpritePointerFetch(3);
+					break;
+				case 1:
+					// rasterline IRQ happens on cycle 1 on rasterline 0
+					if (regs.RASTER == 0 && regs.RASTER == rasterInterruptLine)
+						regs.IRST = true;
+					PerformSpriteDataFetch(3);
+					break;
+				case 2:
+					PerformSpritePointerFetch(4);
+					break;
+				case 3:
+					PerformSpriteDataFetch(4);
+					break;
+				case 4:
+					PerformSpritePointerFetch(5);
+					break;
+				case 5:
+					PerformSpriteDataFetch(5);
+					break;
+				case 6:
+					PerformSpritePointerFetch(6);
+					break;
+				case 7:
+					PerformSpriteDataFetch(6);
+					break;
+				case 8:
+					PerformSpritePointerFetch(7);
+					break;
+				case 9:
+					PerformSpriteDataFetch(7);
+					break;
+				case 10:
+					PerformDRAMRefresh();
+					break;
+				case 11:
+					PerformDRAMRefresh();
+					break;
+				case 12:
+					PerformDRAMRefresh();
+					break;
+				case 13:
+					PerformVCReset();
+					PerformDRAMRefresh();
+					break;
+				case 14:
+					PerformDRAMRefresh();
+					break;
+				case 15:
+					PerformSpriteMCBASEAdvance();
+					PerformScreenCAccess();
+					break;
+				case 16:
+					PerformScreenCAccess();
+					break;
+				case 17:
+					PerformScreenCAccess();
+					break;
+				case 18:
+					PerformScreenCAccess();
+					break;
+				case 19:
+					PerformScreenCAccess();
+					break;
+				case 20:
+					PerformScreenCAccess();
+					break;
+				case 21:
+					PerformScreenCAccess();
+					break;
+				case 22:
+					PerformScreenCAccess();
+					break;
+				case 23:
+					PerformScreenCAccess();
+					break;
+				case 24:
+					PerformScreenCAccess();
+					break;
+				case 25:
+					PerformScreenCAccess();
+					break;
+				case 26:
+					PerformScreenCAccess();
+					break;
+				case 27:
+					PerformScreenCAccess();
+					break;
+				case 28:
+					PerformScreenCAccess();
+					break;
+				case 29:
+					PerformScreenCAccess();
+					break;
+				case 30:
+					PerformScreenCAccess();
+					break;
+				case 31:
+					PerformScreenCAccess();
+					break;
+				case 32:
+					PerformScreenCAccess();
+					break;
+				case 33:
+					PerformScreenCAccess();
+					break;
+				case 34:
+					PerformScreenCAccess();
+					break;
+				case 35:
+					PerformScreenCAccess();
+					break;
+				case 36:
+					PerformScreenCAccess();
+					break;
+				case 37:
+					PerformScreenCAccess();
+					break;
+				case 38:
+					PerformScreenCAccess();
+					break;
+				case 39:
+					PerformScreenCAccess();
+					break;
+				case 40:
+					PerformScreenCAccess();
+					break;
+				case 41:
+					PerformScreenCAccess();
+					break;
+				case 42:
+					PerformScreenCAccess();
+					break;
+				case 43:
+					PerformScreenCAccess();
+					break;
+				case 44:
+					PerformScreenCAccess();
+					break;
+				case 45:
+					PerformScreenCAccess();
+					break;
+				case 46:
+					PerformScreenCAccess();
+					break;
+				case 47:
+					PerformScreenCAccess();
+					break;
+				case 48:
+					PerformScreenCAccess();
+					break;
+				case 49:
+					PerformScreenCAccess();
+					break;
+				case 50:
+					PerformScreenCAccess();
+					break;
+				case 51:
+					PerformScreenCAccess();
+					break;
+				case 52:
+					PerformScreenCAccess();
+					break;
+				case 53:
+					PerformScreenCAccess();
+					break;
+				case 54:
+					PerformScreenCAccess();
+					PerformSpriteYExpansionFlip();
+					PerformSpriteComparison();
+					break;
+				case 55:
+					PerformSpriteComparison();
+					break;
+				case 56:
+					break;
+				case 57:
+					PerformSpriteDMAEnable();
+					PerformRCReset();
+					break;
+				case 58:
+					break;
+				case 59:
+					PerformSpritePointerFetch(0);
+					break;
+				case 60:
+					PerformSpriteDataFetch(0);
+					break;
+				case 61:
+					PerformSpritePointerFetch(1);
+					break;
+				case 62:
+					PerformSpriteDataFetch(1);
+					break;
+				case 63:
+					PerformSpritePointerFetch(2);
+					PerformBorderCheck();
+					break;
+				case 64:
+					PerformSpriteDataFetch(2);
+					break;
 			}
-			else if (cycle >= 10 && cycle < 15)
-			{
-				// dram refresh
-				mem.VicRead((ushort)refreshAddress);
-				refreshAddress = (refreshAddress - 1) & 0xFF;
-				refreshAddress |= 0x3F00;
+		}
 
-				// VC reset
-				if (cycle == 13)
+		// operations timed to PAL
+		private void PerformCyclePAL()
+		{
+		}
+
+		private void PerformDRAMRefresh()
+		{
+			// dram refresh
+			mem.VicRead((ushort)refreshAddress);
+			refreshAddress = (refreshAddress - 1) & 0xFF;
+			refreshAddress |= 0x3F00;
+		}
+
+		private void PerformRCReset()
+		{
+			// row counter processing
+			if (regs.RC == 7)
+			{
+				idle = true;
+				regs.VCBASE = regs.VC;
+			}
+			if (!idle)
+				regs.RC = (regs.RC + 1) & 0x07;
+		}
+
+		private void PerformScreenCAccess()
+		{
+			// screen memory c-access
+			if (badLine)
+			{
+				FetchC();
+				colorMemory[regs.VMLI] = colorDataBus;
+				characterMemory[regs.VMLI] = characterDataBus;
+			}
+			signal.VicAEC = !badLine;
+		}
+
+		private void PerformSpriteComparison()
+		{
+			// reenable cpu if disabled
+			signal.VicAEC = true;
+
+			// sprite comparison
+			for (int i = 0; i < 8; i++)
+			{
+				if (regs.MYE[i] && regs.MCBASE[i] == 63)
 				{
-					regs.VC = regs.VCBASE;
-					regs.VMLI = 0;
-					characterColumn = 0;
-					if (badLine)
-					{
-						regs.RC = 0;
-					}
-					bitmapData = 0;
-					colorData = 0;
-					characterData = 0;
+					regs.MD[i] = false;
+					regs.MDMA[i] = false;
 				}
-			}
-			else if (cycle >= 15 && cycle < 55)
-			{
-				// screen memory c-access
-				if (badLine)
-				{
-					FetchC();
-					colorMemory[regs.VMLI] = colorDataBus;
-					characterMemory[regs.VMLI] = characterDataBus;
-				}
-				signal.VicAEC = !badLine;
-			}
-			else if (cycle == 55)
-			{
-				// reenable cpu if disabled
-				signal.VicAEC = true;
 
-				// sprite comparison
-				for (int i = 0; i < 8; i++)
+				if (regs.MxE[i] == true && regs.MxY[i] == (regs.RASTER & 0xFF) && regs.MDMA[i] == false)
 				{
-					if (regs.MYE[i] && regs.MCBASE[i] == 63)
-					{
-						regs.MD[i] = false;
-						regs.MDMA[i] = false;
-					}
-
+					regs.MDMA[i] = true;
+					regs.MCBASE[i] = 0;
 					if (regs.MxYE[i])
-						regs.MYE[i] = !regs.MYE[i];
+						regs.MYE[i] = false;
+				}
+			}
+		}
 
-					if (regs.MxE[i] == true && regs.MxY[i] == (regs.RASTER & 0xFF) && regs.MDMA[i] == false)
+		private void PerformSpriteDataFetch(int spriteIndex)
+		{
+			// second half of the fetch cycle
+			signal.VicAEC = !regs.MDMA[spriteIndex];
+			if (regs.MDMA[spriteIndex])
+			{
+				for (int i = 0; i < 2; i++)
+				{
+					regs.MSR[spriteIndex] <<= 8;
+					regs.MSR[spriteIndex] |= mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
+					regs.MC[spriteIndex]++;
+				}
+			}
+		}
+
+		private void PerformSpriteDMAEnable()
+		{
+			// sprite MC processing
+			for (int i = 0; i < 8; i++)
+			{
+				regs.MC[i] = regs.MCBASE[i];
+				if (regs.MDMA[i] && regs.MxY[i] == (regs.RASTER & 0xFF))
+					regs.MD[i] = true;
+			}
+		}
+
+		private void PerformSpriteMCBASEAdvance()
+		{
+			for (int i = 0; i < 8; i++)
+			{
+				if (regs.MYE[i])
+				{
+					if (regs.MD[i])
 					{
-						regs.MDMA[i] = true;
-						regs.MCBASE[i] = 0;
-						if (regs.MxYE[i])
-							regs.MYE[i] = false;
+						regs.MCBASE[i] += 3;
 					}
 				}
 			}
-			else if (cycle == 57)
-			{
-				// row counter processing
-				if (regs.RC == 7)
-				{
-					idle = true;
-					regs.VCBASE = regs.VC;
-				}
-				if (!idle)
-					regs.RC = (regs.RC + 1) & 0x07;
+		}
 
-				// sprite MC processing
-				for (int i = 0; i < 8; i++)
-				{
-					regs.MC[i] = regs.MCBASE[i];
-					if (regs.MDMA[i] && regs.MxY[i] == (regs.RASTER & 0xFF))
-						regs.MD[i] = true;
-				}
-			}
+		private void PerformSpritePointerFetch(int spriteIndex)
+		{
+			// first half of the fetch cycle, always fetch pointer
+			ushort pointerOffset = (ushort)((regs.VM << 10) | 0x3F8 | spriteIndex);
+			regs.MPTR[spriteIndex] = mem.VicRead(pointerOffset);
 
-			// MCBASE reset if applicable
-			if (cycle == 15)
+			// also fetch upper 8 bits if enabled
+			signal.VicAEC = !regs.MDMA[spriteIndex];
+			if (regs.MDMA[spriteIndex])
 			{
-				for (int i = 0; i < 8; i++)
-				{
-					if (regs.MYE[i])
-					{
-						if (regs.MD[i])
-						{
-							regs.MCBASE[i] += 3;
-						}
-					}
-				}
+				regs.MSRC[spriteIndex] = 24;
+				regs.MSR[spriteIndex] = mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
+				regs.MC[spriteIndex]++;
 			}
+		}
 
-			// sprite fetch
-			if (cycle == spriteFetchStartCycle)
-			{
-				spriteFetchIndex = 0;
-			}
-			if (spriteFetchIndex < 16)
-			{
-				int spriteIndex = spriteFetchIndex >> 1;
+		private void PerformSpriteYExpansionFlip()
+		{
+			for (int i = 0; i < 8; i++)
+				if (regs.MxYE[i])
+					regs.MYE[i] = !regs.MYE[i];
+		}
 
-				if ((spriteFetchIndex & 1) == 0)
-				{
-					// first half of the fetch cycle, always fetch pointer
-					ushort pointerOffset = (ushort)((regs.VM << 10) | 0x3F8 | spriteIndex);
-					regs.MPTR[spriteIndex] = mem.VicRead(pointerOffset);
-
-					// also fetch upper 8 bits if enabled
-					signal.VicAEC = !regs.MDMA[spriteIndex];
-					if (regs.MDMA[spriteIndex])
-					{
-						regs.MSRC[spriteIndex] = 24;
-						regs.MSR[spriteIndex] = mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
-						regs.MC[spriteIndex]++;
-					}
-				}
-				else
-				{
-					// second half of the fetch cycle
-					signal.VicAEC = !regs.MDMA[spriteIndex];
-					if (regs.MDMA[spriteIndex])
-					{
-						for (int i = 0; i < 2; i++)
-						{
-							regs.MSR[spriteIndex] <<= 8;
-							regs.MSR[spriteIndex] |= mem.VicRead((ushort)((regs.MPTR[spriteIndex] << 6) | (regs.MC[spriteIndex])));
-							regs.MC[spriteIndex]++;
-						}
-					}
-				}
-				spriteFetchIndex++;
-			}
-			else if (spriteFetchIndex == 16)
+		private void PerformVCReset()
+		{
+			// VC reset
+			regs.VC = regs.VCBASE;
+			regs.VMLI = 0;
+			characterColumn = 0;
+			if (badLine)
 			{
-				// set AEC after sprite fetches
-				signal.VicAEC = true;
-				spriteFetchIndex++;
+				regs.RC = 0;
 			}
+			bitmapData = 0;
+			colorData = 0;
+			characterData = 0;
+		}
 
-			// border check
-			if (cycle == 63)
-			{
-				if ((regs.RASTER == borderTop) && (regs.DEN))
-					borderOnVertical = false;
-				if (regs.RASTER == borderBottom)
-					borderOnVertical = true;
-			}
+		public void Poke(int addr, byte val)
+		{
+			regs[addr & 0x3F] = val;
 		}
 
 		// standard text mode