From 3e496ae0696c4ccf74c6a6f1953c0be3be2ba62a Mon Sep 17 00:00:00 2001 From: adelikat Date: Tue, 20 Nov 2012 01:01:51 +0000 Subject: [PATCH] ColecoHawk - preliminary text savestates, not complete! still some things in VDP to add in --- .../Consoles/Coleco/ColecoVision.cs | 42 +- BizHawk.Emulation/Consoles/Coleco/TMS9918A.cs | 649 ++++++------ BizHawk.Emulation/Consoles/Sega/SMS/VDP.cs | 957 +++++++++--------- 3 files changed, 877 insertions(+), 771 deletions(-) diff --git a/BizHawk.Emulation/Consoles/Coleco/ColecoVision.cs b/BizHawk.Emulation/Consoles/Coleco/ColecoVision.cs index 2b43994f25..792a84c1f9 100644 --- a/BizHawk.Emulation/Consoles/Coleco/ColecoVision.cs +++ b/BizHawk.Emulation/Consoles/Coleco/ColecoVision.cs @@ -159,8 +159,46 @@ namespace BizHawk.Emulation.Consoles.Coleco public bool SaveRamModified { get; set; } public bool DeterministicEmulation { get { return true; } } - public void SaveStateText(TextWriter writer) { } - public void LoadStateText(TextReader reader) { } + + public void SaveStateText(TextWriter writer) + { + writer.WriteLine("[Coleco]\n"); + Cpu.SaveStateText(writer); + PSG.SaveStateText(writer); + //VDP.SaveStateText(writer); //TODO + + writer.WriteLine("Frame {0}", Frame); + writer.WriteLine("Lag {0}", _lagcount); + writer.Write("RAM "); + Ram.SaveAsHex(writer); + writer.WriteLine("[/Coleco]"); + } + + public void LoadStateText(TextReader reader) + { + while (true) + { + string[] args = reader.ReadLine().Split(' '); + if (args[0].Trim() == "") continue; + if (args[0] == "[Coleco]") continue; + if (args[0] == "[/Coleco]") break; + else if (args[0] == "Frame") + Frame = int.Parse(args[1]); + else if (args[0] == "Lag") + _lagcount = int.Parse(args[1]); + else if (args[0] == "RAM") + Ram.ReadFromHex(args[1]); + else if (args[0] == "[Z80]") + Cpu.LoadStateText(reader); + else if (args[0] == "[PSG]") + PSG.LoadStateText(reader); + else if (args[0] == "[VDP]") + VDP.LoadStateText(reader); + else + Console.WriteLine("Skipping unrecognized identifier " + args[0]); + } + } + public void SaveStateBinary(BinaryWriter bw) { } public void LoadStateBinary(BinaryReader br) { } diff --git a/BizHawk.Emulation/Consoles/Coleco/TMS9918A.cs b/BizHawk.Emulation/Consoles/Coleco/TMS9918A.cs index 19ee8d1abb..38ee02a609 100644 --- a/BizHawk.Emulation/Consoles/Coleco/TMS9918A.cs +++ b/BizHawk.Emulation/Consoles/Coleco/TMS9918A.cs @@ -1,347 +1,349 @@ using System; +using System.Globalization; +using System.IO; using BizHawk.Emulation.CPUs.Z80; namespace BizHawk.Emulation.Consoles.Coleco { - public sealed class TMS9918A : IVideoProvider - { - public byte[] VRAM = new byte[0x4000]; - byte[] Registers = new byte[8]; - byte StatusByte; + public sealed class TMS9918A : IVideoProvider + { + public byte[] VRAM = new byte[0x4000]; + byte[] Registers = new byte[8]; + byte StatusByte; - bool VdpWaitingForLatchByte; - byte VdpLatch; - ushort VdpAddress; - byte VdpBuffer; - VdpCommand vdpCommand; // TODO remove? + bool VdpWaitingForLatchByte; + byte VdpLatch; + ushort VdpAddress; + byte VdpBuffer; + VdpCommand vdpCommand; // TODO remove? - int TmsMode; + int TmsMode; - bool Mode1Bit { get { return (Registers[1] & 16) > 0; } } - bool Mode2Bit { get { return (Registers[0] & 2) > 0; } } - bool Mode3Bit { get { return (Registers[1] & 8) > 0; } } + bool Mode1Bit { get { return (Registers[1] & 16) > 0; } } + bool Mode2Bit { get { return (Registers[0] & 2) > 0; } } + bool Mode3Bit { get { return (Registers[1] & 8) > 0; } } - bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } } - bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } } - bool EnableInterrupts { get { return (Registers[1] & 32) > 0; } } - bool DisplayOn { get { return (Registers[1] & 64) > 0; } } - bool Mode4k { get { return (Registers[1] & 128)> 0; } } - // TODO, is 4/16K bit used? + bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } } + bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } } + bool EnableInterrupts { get { return (Registers[1] & 32) > 0; } } + bool DisplayOn { get { return (Registers[1] & 64) > 0; } } + bool Mode4k { get { return (Registers[1] & 128) > 0; } } + // TODO, is 4/16K bit used? - bool InterruptPending - { - get { return (StatusByte & 0x80) != 0; } - set { StatusByte = (byte)((StatusByte & ~0x02) | (value ? 0x80 : 0x00)); } - } + bool InterruptPending + { + get { return (StatusByte & 0x80) != 0; } + set { StatusByte = (byte)((StatusByte & ~0x02) | (value ? 0x80 : 0x00)); } + } - //int NameTableBase; - int ColorTableBase; - int PatternGeneratorBase; - int SpritePatternGeneratorBase; - int TmsPatternNameTableBase; - int TmsSpriteAttributeBase; + //int NameTableBase; + int ColorTableBase; + int PatternGeneratorBase; + int SpritePatternGeneratorBase; + int TmsPatternNameTableBase; + int TmsSpriteAttributeBase; - public void ExecuteFrame() - { - for (int scanLine = 0; scanLine < 262; scanLine++) - { - RenderScanline(scanLine); + public void ExecuteFrame() + { + for (int scanLine = 0; scanLine < 262; scanLine++) + { + RenderScanline(scanLine); - if (scanLine == 192) - { - InterruptPending = true; - if (EnableInterrupts) - Cpu.NonMaskableInterrupt = true; - //Console.WriteLine("Set NMI / VSYNC"); - } + if (scanLine == 192) + { + InterruptPending = true; + if (EnableInterrupts) + Cpu.NonMaskableInterrupt = true; + //Console.WriteLine("Set NMI / VSYNC"); + } - Cpu.ExecuteCycles(228); - } + Cpu.ExecuteCycles(228); + } - } + } - public void WriteVdpControl(byte value) - { - if (VdpWaitingForLatchByte) - { - VdpLatch = value; - VdpWaitingForLatchByte = false; - VdpAddress = (ushort)((VdpAddress & 0xFF00) | value); - return; - } + public void WriteVdpControl(byte value) + { + if (VdpWaitingForLatchByte) + { + VdpLatch = value; + VdpWaitingForLatchByte = false; + VdpAddress = (ushort)((VdpAddress & 0xFF00) | value); + return; + } - VdpWaitingForLatchByte = true; - VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch); - switch (value & 0xC0) - { - case 0x00: // read VRAM - vdpCommand = VdpCommand.VramRead; - VdpBuffer = VRAM[VdpAddress & 0x3FFF]; - VdpAddress++; - break; - case 0x40: // write VRAM - vdpCommand = VdpCommand.VramWrite; - break; - case 0x80: // VDP register write - vdpCommand = VdpCommand.RegisterWrite; - int reg = value & 0x0F; - WriteRegister(reg, VdpLatch); - break; - } - } + VdpWaitingForLatchByte = true; + VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch); + switch (value & 0xC0) + { + case 0x00: // read VRAM + vdpCommand = VdpCommand.VramRead; + VdpBuffer = VRAM[VdpAddress & 0x3FFF]; + VdpAddress++; + break; + case 0x40: // write VRAM + vdpCommand = VdpCommand.VramWrite; + break; + case 0x80: // VDP register write + vdpCommand = VdpCommand.RegisterWrite; + int reg = value & 0x0F; + WriteRegister(reg, VdpLatch); + break; + } + } - public void WriteVdpData(byte value) - { - VdpWaitingForLatchByte = true; - VdpBuffer = value; - - // Write VRAM and update pre-computed pattern buffer. - //UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value); - //Console.WriteLine("VRAM[{0:X4}] = {1:X2}", VdpAddress & 0x3FFF, value); - VRAM[VdpAddress & 0x3FFF] = value; - - VdpAddress++; - } + public void WriteVdpData(byte value) + { + VdpWaitingForLatchByte = true; + VdpBuffer = value; - void WriteRegister(int reg, byte data) - { - if (reg >= 8) return; + // Write VRAM and update pre-computed pattern buffer. + //UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value); + //Console.WriteLine("VRAM[{0:X4}] = {1:X2}", VdpAddress & 0x3FFF, value); + VRAM[VdpAddress & 0x3FFF] = value; - Console.WriteLine("Write register {0} : {1:X2}", reg, data); - Registers[reg] = data; - switch (reg) - { - case 0: // Mode Control Register 1 - CheckVideoMode(); - break; - case 1: // Mode Control Register 2 - CheckVideoMode(); - Cpu.NonMaskableInterrupt = (EnableInterrupts && InterruptPending); - Console.WriteLine("4k bit "+ Mode4k); - break; - case 2: // Name Table Base Address - TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00; - break; - case 3: // Color Table Base Address - ColorTableBase = (Registers[3] << 6) & 0x3FC0; - break; - case 4: // Pattern Generator Base Address - PatternGeneratorBase = (Registers[4] << 11) & 0x3800; - break; - case 5: // Sprite Attribute Table Base Address - TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80; - break; - case 6: // Sprite Pattern Generator Base Adderss - SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800; - break; - } - } + VdpAddress++; + } - public byte ReadVdpStatus() - { - VdpWaitingForLatchByte = true; - byte returnValue = StatusByte; - StatusByte &= 0x1F; - Cpu.NonMaskableInterrupt = false; + void WriteRegister(int reg, byte data) + { + if (reg >= 8) return; - //Console.WriteLine("Clear NMI / read status"); - return returnValue; - } + Console.WriteLine("Write register {0} : {1:X2}", reg, data); + Registers[reg] = data; + switch (reg) + { + case 0: // Mode Control Register 1 + CheckVideoMode(); + break; + case 1: // Mode Control Register 2 + CheckVideoMode(); + Cpu.NonMaskableInterrupt = (EnableInterrupts && InterruptPending); + Console.WriteLine("4k bit " + Mode4k); + break; + case 2: // Name Table Base Address + TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00; + break; + case 3: // Color Table Base Address + ColorTableBase = (Registers[3] << 6) & 0x3FC0; + break; + case 4: // Pattern Generator Base Address + PatternGeneratorBase = (Registers[4] << 11) & 0x3800; + break; + case 5: // Sprite Attribute Table Base Address + TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80; + break; + case 6: // Sprite Pattern Generator Base Adderss + SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800; + break; + } + } - public byte ReadData() - { - VdpWaitingForLatchByte = true; - byte value = VdpBuffer; - VdpBuffer = VRAM[VdpAddress & 0x3FFF]; - VdpAddress++; - return value; - } + public byte ReadVdpStatus() + { + VdpWaitingForLatchByte = true; + byte returnValue = StatusByte; + StatusByte &= 0x1F; + Cpu.NonMaskableInterrupt = false; - void CheckVideoMode() - { - if (Mode1Bit) TmsMode = 1; - else if (Mode2Bit) TmsMode = 2; - else if (Mode3Bit) TmsMode = 3; - else TmsMode = 0; + //Console.WriteLine("Clear NMI / read status"); + return returnValue; + } - Console.WriteLine("video mode {0}", TmsMode); - } + public byte ReadData() + { + VdpWaitingForLatchByte = true; + byte value = VdpBuffer; + VdpBuffer = VRAM[VdpAddress & 0x3FFF]; + VdpAddress++; + return value; + } - void RenderScanline(int scanLine) - { - if (scanLine >= 192) - return; + void CheckVideoMode() + { + if (Mode1Bit) TmsMode = 1; + else if (Mode2Bit) TmsMode = 2; + else if (Mode3Bit) TmsMode = 3; + else TmsMode = 0; - if (TmsMode == 2) - { - RenderBackgroundM2(scanLine); - RenderTmsSprites(scanLine); - } - else if (TmsMode == 0) - { - RenderBackgroundM0(scanLine); - RenderTmsSprites(scanLine); - } - } + Console.WriteLine("video mode {0}", TmsMode); + } - void RenderBackgroundM0(int scanLine) - { - if (DisplayOn == false) - { - Array.Clear(FrameBuffer, scanLine * 256, 256); - return; - } + void RenderScanline(int scanLine) + { + if (scanLine >= 192) + return; - int yc = scanLine / 8; - int yofs = scanLine % 8; - int FrameBufferOffset = scanLine * 256; - int PatternNameOffset = TmsPatternNameTableBase + (yc * 32); - int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F]; + if (TmsMode == 2) + { + RenderBackgroundM2(scanLine); + RenderTmsSprites(scanLine); + } + else if (TmsMode == 0) + { + RenderBackgroundM0(scanLine); + RenderTmsSprites(scanLine); + } + } - for (int xc = 0; xc < 32; xc++) - { - int pn = VRAM[PatternNameOffset++]; - int pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs]; - int colorEntry = VRAM[ColorTableBase + (pn / 8)]; - int fgIndex = (colorEntry >> 4) & 0x0F; - int bgIndex = colorEntry & 0x0F; - int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex]; - int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex]; + void RenderBackgroundM0(int scanLine) + { + if (DisplayOn == false) + { + Array.Clear(FrameBuffer, scanLine * 256, 256); + return; + } - FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor; - } - } + int yc = scanLine / 8; + int yofs = scanLine % 8; + int FrameBufferOffset = scanLine * 256; + int PatternNameOffset = TmsPatternNameTableBase + (yc * 32); + int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F]; - void RenderBackgroundM2(int scanLine) - { - if (DisplayOn == false) - { - Array.Clear(FrameBuffer, scanLine * 256, 256); - return; - } + for (int xc = 0; xc < 32; xc++) + { + int pn = VRAM[PatternNameOffset++]; + int pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs]; + int colorEntry = VRAM[ColorTableBase + (pn / 8)]; + int fgIndex = (colorEntry >> 4) & 0x0F; + int bgIndex = colorEntry & 0x0F; + int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex]; + int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex]; - int yrow = scanLine / 8; - int yofs = scanLine % 8; - int FrameBufferOffset = scanLine * 256; - int PatternNameOffset = TmsPatternNameTableBase + (yrow * 32); - int PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);// +((yrow / 8) * 0x100); - int ColorOffset = (ColorTableBase & 0x2000);// +((yrow / 8) * 0x100); - int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F]; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor; + } + } - for (int xc = 0; xc < 32; xc++) - { - int pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100); - int pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs]; - int colorEntry = VRAM[ColorOffset + (pn * 8) + yofs]; - int fgIndex = (colorEntry >> 4) & 0x0F; - int bgIndex = colorEntry & 0x0F; - int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex]; - int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex]; + void RenderBackgroundM2(int scanLine) + { + if (DisplayOn == false) + { + Array.Clear(FrameBuffer, scanLine * 256, 256); + return; + } - FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor; - FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor; - } - } + int yrow = scanLine / 8; + int yofs = scanLine % 8; + int FrameBufferOffset = scanLine * 256; + int PatternNameOffset = TmsPatternNameTableBase + (yrow * 32); + int PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);// +((yrow / 8) * 0x100); + int ColorOffset = (ColorTableBase & 0x2000);// +((yrow / 8) * 0x100); + int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F]; - byte[] ScanlinePriorityBuffer = new byte[256]; - byte[] SpriteCollisionBuffer = new byte[256]; + for (int xc = 0; xc < 32; xc++) + { + int pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100); + int pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs]; + int colorEntry = VRAM[ColorOffset + (pn * 8) + yofs]; + int fgIndex = (colorEntry >> 4) & 0x0F; + int bgIndex = colorEntry & 0x0F; + int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex]; + int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex]; - void RenderTmsSprites(int scanLine) - { - if (DisplayOn == false) return; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor; + FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor; + } + } - Array.Clear(ScanlinePriorityBuffer, 0, 256); - Array.Clear(SpriteCollisionBuffer, 0, 256); + byte[] ScanlinePriorityBuffer = new byte[256]; + byte[] SpriteCollisionBuffer = new byte[256]; - bool Double = EnableDoubledSprites; - bool LargeSprites = EnableLargeSprites; + void RenderTmsSprites(int scanLine) + { + if (DisplayOn == false) return; - int SpriteSize = 8; - if (LargeSprites) SpriteSize *= 2; - if (Double) SpriteSize *= 2; - int OneCellSize = Double ? 16 : 8; + Array.Clear(ScanlinePriorityBuffer, 0, 256); + Array.Clear(SpriteCollisionBuffer, 0, 256); - int NumSpritesOnScanline = 0; - for (int i = 0; i < 32; i++) - { - int SpriteBase = TmsSpriteAttributeBase + (i * 4); - int y = VRAM[SpriteBase++]; - int x = VRAM[SpriteBase++]; - int Pattern = VRAM[SpriteBase++]; - int Color = VRAM[SpriteBase]; + bool Double = EnableDoubledSprites; + bool LargeSprites = EnableLargeSprites; - if (y == 208) break; // terminator sprite - if (y > 224) y -= 256; // sprite Y wrap - y++; // inexplicably, sprites start on Y+1 - if (y > scanLine || y + SpriteSize <= scanLine) continue; // sprite is not on this scanline - if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment + int SpriteSize = 8; + if (LargeSprites) SpriteSize *= 2; + if (Double) SpriteSize *= 2; + int OneCellSize = Double ? 16 : 8; - if (++NumSpritesOnScanline == 5) - { - StatusByte &= 0xE0; // Clear FS0-FS4 bits - StatusByte |= (byte)i; // set 5th sprite index - StatusByte |= 0x40; // set overflow bit - break; - } + int NumSpritesOnScanline = 0; + for (int i = 0; i < 32; i++) + { + int SpriteBase = TmsSpriteAttributeBase + (i * 4); + int y = VRAM[SpriteBase++]; + int x = VRAM[SpriteBase++]; + int Pattern = VRAM[SpriteBase++]; + int Color = VRAM[SpriteBase]; - if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-byte alignment - int SpriteLine = scanLine - y; - if (Double) SpriteLine /= 2; + if (y == 208) break; // terminator sprite + if (y > 224) y -= 256; // sprite Y wrap + y++; // inexplicably, sprites start on Y+1 + if (y > scanLine || y + SpriteSize <= scanLine) continue; // sprite is not on this scanline + if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment - byte pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine]; + if (++NumSpritesOnScanline == 5) + { + StatusByte &= 0xE0; // Clear FS0-FS4 bits + StatusByte |= (byte)i; // set 5th sprite index + StatusByte |= 0x40; // set overflow bit + break; + } - for (int xp = 0; xp < SpriteSize && x + xp < 256; xp++) - { - if (x + xp < 0) continue; - if (LargeSprites && xp == OneCellSize) - pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16]; + if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-byte alignment + int SpriteLine = scanLine - y; + if (Double) SpriteLine /= 2; - if ((pv & (1 << (7 - (xp & 7)))) > 0) - { - if (Color != 0 && ScanlinePriorityBuffer[x + xp] == 0) - { - if (SpriteCollisionBuffer[x + xp] != 0) - StatusByte |= 0x20; // Set sprite collision flag - SpriteCollisionBuffer[x + xp] = 1; - ScanlinePriorityBuffer[x + xp] = 1; - FrameBuffer[(scanLine * 256) + x + xp] = PaletteTMS9918[Color & 0x0F]; - } - } - } - } - } + byte pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine]; - Z80A Cpu; - public TMS9918A(Z80A cpu) - { - this.Cpu = cpu; - } + for (int xp = 0; xp < SpriteSize && x + xp < 256; xp++) + { + if (x + xp < 0) continue; + if (LargeSprites && xp == OneCellSize) + pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16]; - public int[] FrameBuffer = new int[256 * 192]; - public int[] GetVideoBuffer() { return FrameBuffer; } + if ((pv & (1 << (7 - (xp & 7)))) > 0) + { + if (Color != 0 && ScanlinePriorityBuffer[x + xp] == 0) + { + if (SpriteCollisionBuffer[x + xp] != 0) + StatusByte |= 0x20; // Set sprite collision flag + SpriteCollisionBuffer[x + xp] = 1; + ScanlinePriorityBuffer[x + xp] = 1; + FrameBuffer[(scanLine * 256) + x + xp] = PaletteTMS9918[Color & 0x0F]; + } + } + } + } + } - public int VirtualWidth { get { return 256; } } - public int BufferWidth { get { return 256; } } - public int BufferHeight { get { return 192; } } - public int BackgroundColor { get { return 0; } } + Z80A Cpu; + public TMS9918A(Z80A cpu) + { + this.Cpu = cpu; + } - enum VdpCommand { VramRead, VramWrite, RegisterWrite } + public int[] FrameBuffer = new int[256 * 192]; + public int[] GetVideoBuffer() { return FrameBuffer; } - int[] PaletteTMS9918 = new int[] - { + public int VirtualWidth { get { return 256; } } + public int BufferWidth { get { return 256; } } + public int BufferHeight { get { return 192; } } + public int BackgroundColor { get { return 0; } } + + enum VdpCommand { VramRead, VramWrite, RegisterWrite } + + int[] PaletteTMS9918 = new int[] + { unchecked((int)0x00000000), unchecked((int)0xFF000000), unchecked((int)0xFF47B73B), @@ -359,5 +361,66 @@ namespace BizHawk.Emulation.Consoles.Coleco unchecked((int)0xFFCCCCCC), unchecked((int)0xFFFFFFFF) }; - } + + public void SaveStateText(TextWriter writer) + { + //TODO - finish + writer.WriteLine("[VDP]"); + writer.WriteLine("StatusByte {0:X2}", StatusByte); + writer.WriteLine("WaitingForLatchByte {0}", VdpWaitingForLatchByte); + writer.WriteLine("Latch {0:X2}", VdpLatch); + writer.WriteLine("ReadBuffer {0:X2}", VdpBuffer); + writer.WriteLine("VdpAddress {0:X4}", VdpAddress); + writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand)); + + writer.Write("Registers "); + Registers.SaveAsHex(writer); + writer.Write("VRAM "); + VRAM.SaveAsHex(writer); + + writer.WriteLine("[/VDP]"); + writer.WriteLine(); + } + + public void LoadStateText(TextReader reader) + { + //TODO - finish + while (true) + { + string[] args = reader.ReadLine().Split(' '); + if (args[0].Trim() == "") continue; + if (args[0] == "[/VDP]") break; + if (args[0] == "StatusByte") + StatusByte = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "WaitingForLatchByte") + VdpWaitingForLatchByte = bool.Parse(args[1]); + else if (args[0] == "Latch") + VdpLatch = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "ReadBuffer") + VdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "VdpAddress") + VdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "Command") + vdpCommand = (VdpCommand)Enum.Parse(typeof(VdpCommand), args[1]); + else if (args[0] == "Registers") + Registers.ReadFromHex(args[1]); + else if (args[0] == "VRAM") + VRAM.ReadFromHex(args[1]); + else + Console.WriteLine("Skipping unrecognized identifier " + args[0]); + } + for (int i = 0; i < Registers.Length; i++) + WriteRegister(i, Registers[i]); + } + + public void SaveStateBinary(BinaryWriter writer) + { + //TODO + } + + public void LoadStateBinary(BinaryReader reader) + { + //TODO + } + } } \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Sega/SMS/VDP.cs b/BizHawk.Emulation/Consoles/Sega/SMS/VDP.cs index 8182257089..e835f90435 100644 --- a/BizHawk.Emulation/Consoles/Sega/SMS/VDP.cs +++ b/BizHawk.Emulation/Consoles/Sega/SMS/VDP.cs @@ -5,529 +5,534 @@ using BizHawk.Emulation.CPUs.Z80; namespace BizHawk.Emulation.Consoles.Sega { - public enum VdpMode { SMS, GameGear } + public enum VdpMode { SMS, GameGear } - // Emulates the Texas Instruments TMS9918 VDP. - public sealed partial class VDP : IVideoProvider - { - // VDP State - public byte[] VRAM = new byte[0x4000]; //16kb video RAM - public byte[] CRAM; // SMS = 32 bytes, GG = 64 bytes CRAM - public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public byte StatusByte; + // Emulates the Texas Instruments TMS9918 VDP. + public sealed partial class VDP : IVideoProvider + { + // VDP State + public byte[] VRAM = new byte[0x4000]; //16kb video RAM + public byte[] CRAM; // SMS = 32 bytes, GG = 64 bytes CRAM + public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public byte StatusByte; - bool VdpWaitingForLatchByte = true; - byte VdpLatch; - byte VdpBuffer; - ushort VdpAddress; - VdpCommand vdpCommand; - int TmsMode = 4; + bool VdpWaitingForLatchByte = true; + byte VdpLatch; + byte VdpBuffer; + ushort VdpAddress; + VdpCommand vdpCommand; + int TmsMode = 4; - bool VIntPending; - bool HIntPending; + bool VIntPending; + bool HIntPending; - SMS Sms; - VdpMode mode; - DisplayType DisplayType = DisplayType.NTSC; - Z80A Cpu; + SMS Sms; + VdpMode mode; + DisplayType DisplayType = DisplayType.NTSC; + Z80A Cpu; - public bool SpriteLimit; - public int IPeriod = 228; - public VdpMode VdpMode { get { return mode; } } + public bool SpriteLimit; + public int IPeriod = 228; + public VdpMode VdpMode { get { return mode; } } - int FrameHeight = 192; - public int ScanLine; - public int[] FrameBuffer = new int[256*192]; - public int[] GameGearFrameBuffer = new int[160*144]; + int FrameHeight = 192; + public int ScanLine; + public int[] FrameBuffer = new int[256 * 192]; + public int[] GameGearFrameBuffer = new int[160 * 144]; - public bool Mode1Bit { get { return (Registers[1] & 16) > 0;} } - public bool Mode2Bit { get { return (Registers[0] & 2) > 0; } } - public bool Mode3Bit { get { return (Registers[1] & 8) > 0; } } - public bool Mode4Bit { get { return (Registers[0] & 4) > 0; } } - public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } } - public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } } - public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } } - public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } } - public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } } - public bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } } - public bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } } - public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } } - public bool DisplayOn { get { return (Registers[1] & 64) > 0; } } - public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } } - public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256: 0; } } - public byte BackdropColor { get { return (byte)(16 + (Registers[7] & 15)); } } + public bool Mode1Bit { get { return (Registers[1] & 16) > 0; } } + public bool Mode2Bit { get { return (Registers[0] & 2) > 0; } } + public bool Mode3Bit { get { return (Registers[1] & 8) > 0; } } + public bool Mode4Bit { get { return (Registers[0] & 4) > 0; } } + public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } } + public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } } + public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } } + public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } } + public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } } + public bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } } + public bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } } + public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } } + public bool DisplayOn { get { return (Registers[1] & 64) > 0; } } + public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } } + public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256 : 0; } } + public byte BackdropColor { get { return (byte)(16 + (Registers[7] & 15)); } } - int NameTableBase; - int ColorTableBase; - int PatternGeneratorBase; - int SpritePatternGeneratorBase; - int TmsPatternNameTableBase; - int TmsSpriteAttributeBase; - - // preprocessed state assist stuff. - public int[] Palette = new int[32]; - public byte[] PatternBuffer = new byte[0x8000]; + int NameTableBase; + int ColorTableBase; + int PatternGeneratorBase; + int SpritePatternGeneratorBase; + int TmsPatternNameTableBase; + int TmsSpriteAttributeBase; - byte[] ScanlinePriorityBuffer = new byte[256]; - byte[] SpriteCollisionBuffer = new byte[256]; + // preprocessed state assist stuff. + public int[] Palette = new int[32]; + public byte[] PatternBuffer = new byte[0x8000]; - static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 }; - static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 }; + byte[] ScanlinePriorityBuffer = new byte[256]; + byte[] SpriteCollisionBuffer = new byte[256]; - public VDP(SMS sms, Z80A cpu, VdpMode mode, DisplayType displayType) - { - Sms = sms; - Cpu = cpu; - this.mode = mode; - if (mode == VdpMode.SMS) CRAM = new byte[32]; - if (mode == VdpMode.GameGear) CRAM = new byte[64]; - DisplayType = displayType; - NameTableBase = CalcNameTableBase(); - } + static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 }; + static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 }; - public byte ReadData() - { - VdpWaitingForLatchByte = true; - byte value = VdpBuffer; - VdpBuffer = VRAM[VdpAddress & 0x3FFF]; - VdpAddress++; - return value; - } + public VDP(SMS sms, Z80A cpu, VdpMode mode, DisplayType displayType) + { + Sms = sms; + Cpu = cpu; + this.mode = mode; + if (mode == VdpMode.SMS) CRAM = new byte[32]; + if (mode == VdpMode.GameGear) CRAM = new byte[64]; + DisplayType = displayType; + NameTableBase = CalcNameTableBase(); + } - public byte ReadVdpStatus() - { - VdpWaitingForLatchByte = true; - byte returnValue = StatusByte; - StatusByte &= 0x1F; - HIntPending = false; - VIntPending = false; - Cpu.Interrupt = false; - return returnValue; - } + public byte ReadData() + { + VdpWaitingForLatchByte = true; + byte value = VdpBuffer; + VdpBuffer = VRAM[VdpAddress & 0x3FFF]; + VdpAddress++; + return value; + } - public byte ReadVLineCounter() - { - if (DisplayType == DisplayType.NTSC) - { - if (FrameHeight == 192) - return VLineCounterTableNTSC192[ScanLine]; - if (FrameHeight == 224) - return VLineCounterTableNTSC224[ScanLine]; - return VLineCounterTableNTSC240[ScanLine]; - } else { // PAL - if (FrameHeight == 192) - return VLineCounterTablePAL192[ScanLine]; - if (FrameHeight == 224) - return VLineCounterTablePAL224[ScanLine]; - return VLineCounterTablePAL240[ScanLine]; - } - } + public byte ReadVdpStatus() + { + VdpWaitingForLatchByte = true; + byte returnValue = StatusByte; + StatusByte &= 0x1F; + HIntPending = false; + VIntPending = false; + Cpu.Interrupt = false; + return returnValue; + } - public void WriteVdpControl(byte value) - { - if (VdpWaitingForLatchByte) - { - VdpLatch = value; - VdpWaitingForLatchByte = false; - VdpAddress = (ushort)((VdpAddress & 0xFF00) | value); - return; - } + public byte ReadVLineCounter() + { + if (DisplayType == DisplayType.NTSC) + { + if (FrameHeight == 192) + return VLineCounterTableNTSC192[ScanLine]; + if (FrameHeight == 224) + return VLineCounterTableNTSC224[ScanLine]; + return VLineCounterTableNTSC240[ScanLine]; + } + else + { // PAL + if (FrameHeight == 192) + return VLineCounterTablePAL192[ScanLine]; + if (FrameHeight == 224) + return VLineCounterTablePAL224[ScanLine]; + return VLineCounterTablePAL240[ScanLine]; + } + } - VdpWaitingForLatchByte = true; - VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch); - switch (value & 0xC0) - { - case 0x00: // read VRAM - vdpCommand = VdpCommand.VramRead; - VdpBuffer = VRAM[VdpAddress & 0x3FFF]; - VdpAddress++; - break; - case 0x40: // write VRAM - vdpCommand = VdpCommand.VramWrite; - break; - case 0x80: // VDP register write - vdpCommand = VdpCommand.RegisterWrite; - int reg = value & 0x0F; - WriteRegister(reg, VdpLatch); - break; - case 0xC0: // write CRAM / modify palette - vdpCommand = VdpCommand.CramWrite; - break; - } - } + public void WriteVdpControl(byte value) + { + if (VdpWaitingForLatchByte) + { + VdpLatch = value; + VdpWaitingForLatchByte = false; + VdpAddress = (ushort)((VdpAddress & 0xFF00) | value); + return; + } - public void WriteVdpData(byte value) - { - VdpWaitingForLatchByte = true; - VdpBuffer = value; - if (vdpCommand == VdpCommand.CramWrite) - { - // Write Palette / CRAM - int mask = VdpMode == VdpMode.SMS ? 0x1F : 0x3F; - CRAM[VdpAddress & mask] = value; - UpdatePrecomputedPalette(); - } - else - { - // Write VRAM and update pre-computed pattern buffer. - UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value); - VRAM[VdpAddress & 0x3FFF] = value; - } - VdpAddress++; - } + VdpWaitingForLatchByte = true; + VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch); + switch (value & 0xC0) + { + case 0x00: // read VRAM + vdpCommand = VdpCommand.VramRead; + VdpBuffer = VRAM[VdpAddress & 0x3FFF]; + VdpAddress++; + break; + case 0x40: // write VRAM + vdpCommand = VdpCommand.VramWrite; + break; + case 0x80: // VDP register write + vdpCommand = VdpCommand.RegisterWrite; + int reg = value & 0x0F; + WriteRegister(reg, VdpLatch); + break; + case 0xC0: // write CRAM / modify palette + vdpCommand = VdpCommand.CramWrite; + break; + } + } - public void UpdatePrecomputedPalette() - { - if (mode == VdpMode.SMS) - { - for (int i=0; i<32; i++) - { - byte value = CRAM[i]; - byte r = SMSPalXlatTable[(value & 0x03)]; - byte g = SMSPalXlatTable[(value & 0x0C) >> 2]; - byte b = SMSPalXlatTable[(value & 0x30) >> 4]; - Palette[i] = Colors.ARGB(r, g, b); - } - } else { // GameGear - for (int i=0; i<32; i++) - { - ushort value = (ushort) ((CRAM[(i*2) + 1] << 8) | CRAM[(i*2) + 0]); - byte r = GGPalXlatTable[(value & 0x000F)]; - byte g = GGPalXlatTable[(value & 0x00F0) >> 4]; - byte b = GGPalXlatTable[(value & 0x0F00) >> 8]; - Palette[i] = Colors.ARGB(r, g, b); - } - } - } + public void WriteVdpData(byte value) + { + VdpWaitingForLatchByte = true; + VdpBuffer = value; + if (vdpCommand == VdpCommand.CramWrite) + { + // Write Palette / CRAM + int mask = VdpMode == VdpMode.SMS ? 0x1F : 0x3F; + CRAM[VdpAddress & mask] = value; + UpdatePrecomputedPalette(); + } + else + { + // Write VRAM and update pre-computed pattern buffer. + UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value); + VRAM[VdpAddress & 0x3FFF] = value; + } + VdpAddress++; + } - public int CalcNameTableBase() - { - if (FrameHeight == 192) - return 1024 * (Registers[2] & 0x0E); - return (1024 * (Registers[2] & 0x0C)) + 0x0700; - } + public void UpdatePrecomputedPalette() + { + if (mode == VdpMode.SMS) + { + for (int i = 0; i < 32; i++) + { + byte value = CRAM[i]; + byte r = SMSPalXlatTable[(value & 0x03)]; + byte g = SMSPalXlatTable[(value & 0x0C) >> 2]; + byte b = SMSPalXlatTable[(value & 0x30) >> 4]; + Palette[i] = Colors.ARGB(r, g, b); + } + } + else + { // GameGear + for (int i = 0; i < 32; i++) + { + ushort value = (ushort)((CRAM[(i * 2) + 1] << 8) | CRAM[(i * 2) + 0]); + byte r = GGPalXlatTable[(value & 0x000F)]; + byte g = GGPalXlatTable[(value & 0x00F0) >> 4]; + byte b = GGPalXlatTable[(value & 0x0F00) >> 8]; + Palette[i] = Colors.ARGB(r, g, b); + } + } + } - void CheckVideoMode() - { - if (Mode4Bit == false) // check old TMS modes - { - if (Mode1Bit) TmsMode = 1; - else if (Mode2Bit) TmsMode = 2; - else if (Mode3Bit) TmsMode = 3; - else TmsMode = 0; - } + public int CalcNameTableBase() + { + if (FrameHeight == 192) + return 1024 * (Registers[2] & 0x0E); + return (1024 * (Registers[2] & 0x0C)) + 0x0700; + } - else if (Mode4Bit && Mode2Bit) // if Mode4 and Mode2 set, then check extension modes - { - TmsMode = 4; - switch (Registers[1] & 0x18) - { - case 0x00: - case 0x18: // 192-line mode - if (FrameHeight != 192) - { - FrameHeight = 192; - FrameBuffer = new int[256*192]; - NameTableBase = CalcNameTableBase(); - } - break; - case 0x10: // 224-line mode - if (FrameHeight != 224) - { - FrameHeight = 224; - FrameBuffer = new int[256*224]; - NameTableBase = CalcNameTableBase(); - } - break; - case 0x08: // 240-line mode - if (FrameHeight != 240) - { - FrameHeight = 240; - FrameBuffer = new int[256 * 240]; - NameTableBase = CalcNameTableBase(); - } - break; - } - } - - else { // default to standard 192-line mode4 - TmsMode = 4; - if (FrameHeight != 192) - { - FrameHeight = 192; - FrameBuffer = new int[256*192]; - NameTableBase = CalcNameTableBase(); - } - } - } + void CheckVideoMode() + { + if (Mode4Bit == false) // check old TMS modes + { + if (Mode1Bit) TmsMode = 1; + else if (Mode2Bit) TmsMode = 2; + else if (Mode3Bit) TmsMode = 3; + else TmsMode = 0; + } - void WriteRegister(int reg, byte data) - { - Registers[reg] = data; - switch(reg) - { - case 0: // Mode Control Register 1 - CheckVideoMode(); - Cpu.Interrupt = (EnableLineInterrupts && HIntPending); - break; - case 1: // Mode Control Register 2 - CheckVideoMode(); - Cpu.Interrupt = (EnableFrameInterrupts && VIntPending); - break; - case 2: // Name Table Base Address - NameTableBase = CalcNameTableBase(); - TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00; - break; - case 3: // Color Table Base Address - ColorTableBase = (Registers[3] << 6) & 0x3FC0; - break; - case 4: // Pattern Generator Base Address - PatternGeneratorBase = (Registers[4] << 11) & 0x3800; - break; - case 5: // Sprite Attribute Table Base Address - // ??? should I move from my property to precalculated? - TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80; - break; - case 6: // Sprite Pattern Generator Base Adderss - SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800; - break; - } - } + else if (Mode4Bit && Mode2Bit) // if Mode4 and Mode2 set, then check extension modes + { + TmsMode = 4; + switch (Registers[1] & 0x18) + { + case 0x00: + case 0x18: // 192-line mode + if (FrameHeight != 192) + { + FrameHeight = 192; + FrameBuffer = new int[256 * 192]; + NameTableBase = CalcNameTableBase(); + } + break; + case 0x10: // 224-line mode + if (FrameHeight != 224) + { + FrameHeight = 224; + FrameBuffer = new int[256 * 224]; + NameTableBase = CalcNameTableBase(); + } + break; + case 0x08: // 240-line mode + if (FrameHeight != 240) + { + FrameHeight = 240; + FrameBuffer = new int[256 * 240]; + NameTableBase = CalcNameTableBase(); + } + break; + } + } - static readonly byte[] pow2 = {1, 2, 4, 8, 16, 32, 64, 128}; - - void UpdatePatternBuffer(ushort address, byte value) - { - // writing one byte affects 8 pixels due to stupid planar storage. - for (int i=0; i<8; i++) - { - byte colorBit = pow2[address%4]; - byte sourceBit = pow2[7 - i]; - ushort dest = (ushort) (((address & 0xFFFC)*2) + i); - if ((value & sourceBit) > 0) // setting bit - PatternBuffer[dest] |= colorBit; - else // clearing bit - PatternBuffer[dest] &= (byte)~colorBit; - } - } + else + { // default to standard 192-line mode4 + TmsMode = 4; + if (FrameHeight != 192) + { + FrameHeight = 192; + FrameBuffer = new int[256 * 192]; + NameTableBase = CalcNameTableBase(); + } + } + } - int lineIntLinesRemaining; + void WriteRegister(int reg, byte data) + { + Registers[reg] = data; + switch (reg) + { + case 0: // Mode Control Register 1 + CheckVideoMode(); + Cpu.Interrupt = (EnableLineInterrupts && HIntPending); + break; + case 1: // Mode Control Register 2 + CheckVideoMode(); + Cpu.Interrupt = (EnableFrameInterrupts && VIntPending); + break; + case 2: // Name Table Base Address + NameTableBase = CalcNameTableBase(); + TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00; + break; + case 3: // Color Table Base Address + ColorTableBase = (Registers[3] << 6) & 0x3FC0; + break; + case 4: // Pattern Generator Base Address + PatternGeneratorBase = (Registers[4] << 11) & 0x3800; + break; + case 5: // Sprite Attribute Table Base Address + // ??? should I move from my property to precalculated? + TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80; + break; + case 6: // Sprite Pattern Generator Base Adderss + SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800; + break; + } + } - void ProcessFrameInterrupt() - { - if (ScanLine == FrameHeight + 1) - { - StatusByte |= 0x80; - VIntPending = true; - } + static readonly byte[] pow2 = { 1, 2, 4, 8, 16, 32, 64, 128 }; - if (VIntPending && EnableFrameInterrupts) - Cpu.Interrupt = true; - } + void UpdatePatternBuffer(ushort address, byte value) + { + // writing one byte affects 8 pixels due to stupid planar storage. + for (int i = 0; i < 8; i++) + { + byte colorBit = pow2[address % 4]; + byte sourceBit = pow2[7 - i]; + ushort dest = (ushort)(((address & 0xFFFC) * 2) + i); + if ((value & sourceBit) > 0) // setting bit + PatternBuffer[dest] |= colorBit; + else // clearing bit + PatternBuffer[dest] &= (byte)~colorBit; + } + } - void ProcessLineInterrupt() - { - if (ScanLine <= FrameHeight) - { - if (lineIntLinesRemaining-- <= 0) - { - HIntPending = true; - if (EnableLineInterrupts) - Cpu.Interrupt = true; - lineIntLinesRemaining = Registers[0x0A]; - } - return; - } - // else we're outside the active display period - lineIntLinesRemaining = Registers[0x0A]; - } + int lineIntLinesRemaining; - public void ExecFrame(bool render) - { - int scanlinesPerFrame = DisplayType == DisplayType.NTSC ? 262 : 313; - for (ScanLine = 0; ScanLine < scanlinesPerFrame; ScanLine++) - { - RenderCurrentScanline(render); + void ProcessFrameInterrupt() + { + if (ScanLine == FrameHeight + 1) + { + StatusByte |= 0x80; + VIntPending = true; + } - ProcessFrameInterrupt(); - ProcessLineInterrupt(); + if (VIntPending && EnableFrameInterrupts) + Cpu.Interrupt = true; + } - Cpu.ExecuteCycles(IPeriod); + void ProcessLineInterrupt() + { + if (ScanLine <= FrameHeight) + { + if (lineIntLinesRemaining-- <= 0) + { + HIntPending = true; + if (EnableLineInterrupts) + Cpu.Interrupt = true; + lineIntLinesRemaining = Registers[0x0A]; + } + return; + } + // else we're outside the active display period + lineIntLinesRemaining = Registers[0x0A]; + } - if (ScanLine == scanlinesPerFrame - 1) - RenderBlankingRegions(); - } - } + public void ExecFrame(bool render) + { + int scanlinesPerFrame = DisplayType == DisplayType.NTSC ? 262 : 313; + for (ScanLine = 0; ScanLine < scanlinesPerFrame; ScanLine++) + { + RenderCurrentScanline(render); - internal void RenderCurrentScanline(bool render) - { - if (ScanLine >= FrameHeight) - return; + ProcessFrameInterrupt(); + ProcessLineInterrupt(); - if (TmsMode == 4) - { - if (render == false) - { - ProcessSpriteCollisionForFrameskip(); - return; - } + Cpu.ExecuteCycles(IPeriod); - RenderBackgroundCurrentLine(Sms.CoreInputComm.SMS_ShowBG); + if (ScanLine == scanlinesPerFrame - 1) + RenderBlankingRegions(); + } + } - if (EnableDoubledSprites) - RenderSpritesCurrentLineDoubleSize(Sms.CoreInputComm.SMS_ShowOBJ); - else - RenderSpritesCurrentLine(Sms.CoreInputComm.SMS_ShowOBJ); - } - else if (TmsMode == 2) - { - if (render == false) - return; + internal void RenderCurrentScanline(bool render) + { + if (ScanLine >= FrameHeight) + return; - RenderBackgroundM2(Sms.CoreInputComm.SMS_ShowBG); - RenderTmsSprites(Sms.CoreInputComm.SMS_ShowOBJ); - } - else if (TmsMode == 0) - { - if (render == false) - return; + if (TmsMode == 4) + { + if (render == false) + { + ProcessSpriteCollisionForFrameskip(); + return; + } - RenderBackgroundM0(Sms.CoreInputComm.SMS_ShowBG); - RenderTmsSprites(Sms.CoreInputComm.SMS_ShowOBJ); - } - } + RenderBackgroundCurrentLine(Sms.CoreInputComm.SMS_ShowBG); - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[VDP]"); - writer.WriteLine("StatusByte {0:X2}", StatusByte); - writer.WriteLine("WaitingForLatchByte {0}", VdpWaitingForLatchByte); - writer.WriteLine("Latch {0:X2}", VdpLatch); - writer.WriteLine("ReadBuffer {0:X2}", VdpBuffer); - writer.WriteLine("VdpAddress {0:X4}", VdpAddress); - writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand)); + if (EnableDoubledSprites) + RenderSpritesCurrentLineDoubleSize(Sms.CoreInputComm.SMS_ShowOBJ); + else + RenderSpritesCurrentLine(Sms.CoreInputComm.SMS_ShowOBJ); + } + else if (TmsMode == 2) + { + if (render == false) + return; - writer.Write("Registers "); - Registers.SaveAsHex(writer); - writer.Write("CRAM "); - CRAM.SaveAsHex(writer); - writer.Write("VRAM "); - VRAM.SaveAsHex(writer); + RenderBackgroundM2(Sms.CoreInputComm.SMS_ShowBG); + RenderTmsSprites(Sms.CoreInputComm.SMS_ShowOBJ); + } + else if (TmsMode == 0) + { + if (render == false) + return; - writer.WriteLine("[/VDP]"); - writer.WriteLine(); - } + RenderBackgroundM0(Sms.CoreInputComm.SMS_ShowBG); + RenderTmsSprites(Sms.CoreInputComm.SMS_ShowOBJ); + } + } - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/VDP]") break; - if (args[0] == "StatusByte") - StatusByte = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "WaitingForLatchByte") - VdpWaitingForLatchByte = bool.Parse(args[1]); - else if (args[0] == "Latch") - VdpLatch = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "ReadBuffer") - VdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "VdpAddress") - VdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "Command") - vdpCommand = (VdpCommand) Enum.Parse(typeof (VdpCommand), args[1]); - else if (args[0] == "Registers") - Registers.ReadFromHex(args[1]); - else if (args[0] == "CRAM") - { - CRAM.ReadFromHex(args[1]); - UpdatePrecomputedPalette(); - } - else if (args[0] == "VRAM") - { - VRAM.ReadFromHex(args[1]); - for (ushort i=0; i