diff --git a/EMU7800/Core/Maria.cs b/EMU7800/Core/Maria.cs deleted file mode 100644 index 682e3a0bc9..0000000000 --- a/EMU7800/Core/Maria.cs +++ /dev/null @@ -1,1151 +0,0 @@ -/* - * Maria.cs - * - * The Maria display device. - * - * Derived from much of Dan Boris' work with 7800 emulation - * within the MESS emulator. - * - * Thanks to Matthias Luedtke for correcting - * the BuildLineRAM320B() method to correspond closer to real hardware. - * (Matthias credited an insightful response by Eckhard Stolberg on a forum on - * Atari Age circa June 2005.) - * - * Copyright © 2004-2012 Mike Murphy - * - */ -using System; - -namespace EMU7800.Core -{ - public sealed class Maria : IDevice - { - #region Constants - - const int - INPTCTRL= 0x01, // Write: input port control (VBLANK in TIA) - INPT0 = 0x08, // Read pot port: D7 - INPT1 = 0x09, // Read pot port: D7 - INPT2 = 0x0a, // Read pot port: D7 - INPT3 = 0x0b, // Read pot port: D7 - INPT4 = 0x0c, // Read P1 joystick trigger: D7 - INPT5 = 0x0d, // Read P2 joystick trigger: D7 - AUDC0 = 0x15, // Write: audio control 0 (D3-0) - AUDC1 = 0x16, // Write: audio control 1 (D4-0) - AUDF0 = 0x17, // Write: audio frequency 0 (D4-0) - AUDF1 = 0x18, // Write: audio frequency 1 (D3-0) - AUDV0 = 0x19, // Write: audio volume 0 (D3-0) - AUDV1 = 0x1a, // Write: audio volume 1 (D3-0) - - BACKGRND= 0x20, // Background color - P0C1 = 0x21, // Palette 0 - color 1 - P0C2 = 0x22, // Palette 0 - color 2 - P0C3 = 0x23, // Palette 0 - color 3 - WSYNC = 0x24, // Wait for sync - P1C1 = 0x25, // Palette 1 - color 1 - P1C2 = 0x26, // Palette 1 - color 2 - P1C3 = 0x27, // Palette 1 - color 3 - MSTAT = 0x28, // Maria status - P2C1 = 0x29, // Palette 2 - color 1 - P2C2 = 0x2a, // Palette 2 - color 2 - P2C3 = 0x2b, // Palette 2 - color 3 - DPPH = 0x2c, // Display list list point high - P3C1 = 0x2d, // Palette 3 - color 1 - P3C2 = 0x2e, // Palette 3 - color 2 - P3C3 = 0x2f, // Palette 3 - color 3 - DPPL = 0x30, // Display list list point low - P4C1 = 0x31, // Palette 4 - color 1 - P4C2 = 0x32, // Palette 4 - color 2 - P4C3 = 0x33, // Palette 4 - color 3 - CHARBASE= 0x34, // Character base address - P5C1 = 0x35, // Palette 5 - color 1 - P5C2 = 0x36, // Palette 5 - color 2 - P5C3 = 0x37, // Palette 5 - color 3 - OFFSET = 0x38, // Future expansion (store zero here) - P6C1 = 0x39, // Palette 6 - color 1 - P6C2 = 0x3a, // Palette 6 - color 2 - P6C3 = 0x3b, // Palette 6 - color 3 - CTRL = 0x3c, // Maria control register - P7C1 = 0x3d, // Palette 7 - color 1 - P7C2 = 0x3e, // Palette 7 - color 2 - P7C3 = 0x3f; // Palette 7 - color 3 - - const int CPU_TICKS_PER_AUDIO_SAMPLE = 57; - - #endregion - - #region Fields - - readonly byte[] LineRAM = new byte[0x200]; - readonly byte[] Registers = new byte[0x40]; - - readonly Machine7800 M; - readonly TIASound TIASound; - - ulong _startOfFrameCpuClock; - int Scanline { get { return (int)(M.CPU.Clock - _startOfFrameCpuClock) / 114; } } - int HPos { get { return (int)(M.CPU.Clock - _startOfFrameCpuClock) % 114; } } - - int FirstVisibleScanline, LastVisibleScanline; - int _dmaClocks; - bool _isPal; - - // For lightgun emulation. - // Transient state, serialization unnecessary. - ulong _lightgunFirstSampleCpuClock; - int _lightgunFrameSamples, _lightgunSampledScanline, _lightgunSampledVisibleHpos; - - bool WM; - ushort DLL; - ushort DL; - int Offset; - int Holey; - int Width; - byte HPOS; - int PaletteNo; - bool INDMode; - - bool CtrlLock; - - // MARIA CNTL - bool DMAEnabled; - bool ColorKill; - bool CWidth; - bool BCntl; - bool Kangaroo; - byte RM; - - #endregion - - #region Public Members - - public void Reset() - { - CtrlLock = false; - - DMAEnabled = false; - ColorKill = false; - CWidth = false; - BCntl = false; - Kangaroo = false; - RM = 0; - - TIASound.Reset(); - - Log("{0} reset", this); - } - - public byte this[ushort addr] - { - get { return peek(addr); } - set { poke(addr, value); } - } - - public override string ToString() - { - return GetType().Name; - } - - public void StartFrame() - { - _startOfFrameCpuClock = M.CPU.Clock + (ulong)(M.CPU.RunClocks / M.CPU.RunClocksMultiple); - _lightgunFirstSampleCpuClock = 0; - - AssertDebug(M.CPU.RunClocks <= 0 && (M.CPU.RunClocks % M.CPU.RunClocksMultiple) == 0); - AssertDebug((_startOfFrameCpuClock % (114 * (ulong)M.FrameBuffer.Scanlines)) == 0); - - TIASound.StartFrame(); - } - - public int DoDMAProcessing() - { - OutputLineRAM(); - - var sl = Scanline; - - if (!DMAEnabled || sl < FirstVisibleScanline || sl >= LastVisibleScanline) - return 0; - - _dmaClocks = 0; - - if (DMAEnabled && sl == FirstVisibleScanline) - { - // DMA TIMING: End of VBLANK: DMA Startup + long shutdown - _dmaClocks += 15; - - DLL = WORD(Registers[DPPL], Registers[DPPH]); - - ConsumeNextDLLEntry(); - } - - // DMA TIMING: DMA Startup, 5-9 cycles - _dmaClocks += 5; - - BuildLineRAM(); - - if (--Offset < 0) - { - ConsumeNextDLLEntry(); - - // DMA TIMING: DMA Shutdown: Last line of zone, 10-13 cycles - _dmaClocks += 10; - } - else - { - // DMA TIMING: DMA Shutdown: Other line of zone, 4-7 cycles - _dmaClocks += 4; - } - - return _dmaClocks; - } - - public void EndFrame() - { - TIASound.EndFrame(); - } - - #endregion - - #region Constructors - - private Maria() - { - } - - public Maria(Machine7800 m, int scanlines) - { - if (m == null) - throw new ArgumentNullException("m"); - - M = m; - InitializeVisibleScanlineValues(scanlines); - TIASound = new TIASound(M, CPU_TICKS_PER_AUDIO_SAMPLE); - } - - #endregion - - #region Scanline Builders - - void BuildLineRAM() - { - var dl = DL; - - // Iterate through Display List (DL) - while (true) - { - var modeByte = DmaRead(dl + 1); - if ((modeByte & 0x5f) == 0) - break; - - INDMode = false; - ushort graphaddr; - - if ((modeByte & 0x1f) == 0) - { - // Extended DL header - var dl0 = DmaRead(dl++); // low address - var dl1 = DmaRead(dl++); // mode - var dl2 = DmaRead(dl++); // high address - var dl3 = DmaRead(dl++); // palette(7-5)/width(4-0) - var dl4 = DmaRead(dl++); // horizontal position - - graphaddr = WORD(dl0, dl2); - WM = (dl1 & 0x80) != 0; - INDMode = (dl1 & 0x20) != 0; - PaletteNo = (dl3 & 0xe0) >> 3; - Width = (~dl3 & 0x1f) + 1; - HPOS = dl4; - - // DMA TIMING: DL 5 byte header - _dmaClocks += 10; - } - else - { - // Normal DL header - var dl0 = DmaRead(dl++); // low address - var dl1 = DmaRead(dl++); // palette(7-5)/width(4-0) - var dl2 = DmaRead(dl++); // high address - var dl3 = DmaRead(dl++); // horizontal position - - graphaddr = WORD(dl0, dl2); - PaletteNo = (dl1 & 0xe0) >> 3; - Width = (~dl1 & 0x1f) + 1; - HPOS = dl3; - - // DMA TIMING: DL 4 byte header - _dmaClocks += 8; - } - - // DMA TIMING: Graphic reads - if (RM != 1) - _dmaClocks += (Width * (INDMode ? (CWidth ? 9 : 6) : 3)); - - switch (RM) - { - case 0: - if (WM) BuildLineRAM160B(graphaddr); else BuildLineRAM160A(graphaddr); - break; - case 1: - continue; - case 2: - if (WM) BuildLineRAM320B(graphaddr); else BuildLineRAM320D(graphaddr); - break; - case 3: - if (WM) BuildLineRAM320C(graphaddr); else BuildLineRAM320A(graphaddr); - break; - } - } - } - - void BuildLineRAM160A(ushort graphaddr) - { - var indbytes = (INDMode && CWidth) ? 2 : 1; - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - for (var i=0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - for (var j=0; j < indbytes; j++) - { - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 8; - dataaddr++; - AssertDebug(!Kangaroo); - continue; - } - - int d = DmaRead(dataaddr++); - - var c = (d & 0xc0) >> 6; - if (c != 0) - { - var val = (byte)(PaletteNo | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - AssertDebug(c != 0 || c == 0 && !Kangaroo); - - hpos += 2; - - c = (d & 0x30) >> 4; - if (c != 0) - { - var val = (byte)(PaletteNo | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - AssertDebug(c != 0 || c == 0 && !Kangaroo); - - hpos += 2; - - c = (d & 0x0c) >> 2; - if (c != 0) - { - var val = (byte)(PaletteNo | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - AssertDebug(c != 0 || c == 0 && !Kangaroo); - - hpos += 2; - - c = d & 0x03; - if (c != 0) - { - var val = (byte)(PaletteNo | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - AssertDebug(c != 0 || c == 0 && !Kangaroo); - - hpos += 2; - } - } - } - - void BuildLineRAM160B(ushort graphaddr) - { - var indbytes = (INDMode && CWidth) ? 2 : 1; - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - for (var i = 0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - for (var j=0; j < indbytes; j++) - { - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 4; - dataaddr++; - continue; - } - - int d = DmaRead(dataaddr++); - - var c = (d & 0xc0) >> 6; - if (c != 0) - { - var p = ((PaletteNo >> 2) & 0x04) | ((d & 0x0c) >> 2); - var val = (byte)((p << 2) | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - else if (Kangaroo) - { - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = 0; - } - - hpos += 2; - - c = (d & 0x30) >> 4; - if (c != 0) - { - var p = ((PaletteNo >> 2) & 0x04) | (d & 0x03); - var val = (byte)((p << 2) | c); - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = val; - } - else if (Kangaroo) - { - LineRAM[hpos & 0x1ff] = LineRAM[(hpos+1) & 0x1ff] = 0; - } - - hpos += 2; - } - } - } - - void BuildLineRAM320A(ushort graphaddr) - { - var color = (byte)(PaletteNo | 2); - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - AssertDebug(!CWidth); - - for (var i = 0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 8; - dataaddr++; - continue; - } - - int d = DmaRead(dataaddr++); - - if ((d & 0x80) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x40) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x20) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x10) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x08) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x04) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x02) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x01) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - } - } - - void BuildLineRAM320B(ushort graphaddr) - { - var indbytes = (INDMode && CWidth) ? 2 : 1; - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - for (var i = 0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - for (var j=0; j < indbytes; j++) - { - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 4; - dataaddr++; - continue; - } - - int d = DmaRead(dataaddr++); - - var c = ((d & 0x80) >> 6) | ((d & 0x08) >> 3); - if (c != 0) - { - if ((d & 0xc0) != 0 || Kangaroo) - LineRAM[hpos & 0x1ff] = (byte)(PaletteNo | c); - } - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - else if ((d & 0xcc) != 0) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x40) >> 5) | ((d & 0x04) >> 2); - if (c != 0) - { - if ((d & 0xc0) != 0 || Kangaroo) - LineRAM[hpos & 0x1ff] = (byte)(PaletteNo | c); - } - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - else if ((d & 0xcc) != 0) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x20) >> 4) | ((d & 0x02) >> 1); - if (c != 0) - { - if ((d & 0x30) != 0 || Kangaroo) - LineRAM[hpos & 0x1ff] = (byte)(PaletteNo | c); - } - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - else if ((d & 0x33) != 0) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x10) >> 3) | (d & 0x01); - if (c != 0) - { - if ((d & 0x30) != 0 || Kangaroo) - LineRAM[hpos & 0x1ff] = (byte)(PaletteNo | c); - } - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - else if ((d & 0x33) != 0) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - } - } - } - - void BuildLineRAM320C(ushort graphaddr) - { - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - AssertDebug(!CWidth); - - for (var i = 0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 4; - dataaddr++; - continue; - } - - int d = DmaRead(dataaddr++); - - var color = (byte)(((((d & 0x0c) >> 2) | ((PaletteNo >> 2) & 0x04)) << 2) | 2); - - if ((d & 0x80) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x40) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - color = (byte)((((d & 0x03) | ((PaletteNo >> 2) & 0x04)) << 2) | 2); - - if ((d & 0x20) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - if ((d & 0x10) != 0) - LineRAM[hpos & 0x1ff] = color; - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - } - } - - void BuildLineRAM320D(ushort graphaddr) - { - var indbytes = (INDMode && CWidth) ? 2 : 1; - var hpos = HPOS << 1; - var dataaddr = (ushort)(graphaddr + (Offset << 8)); - - for (var i = 0; i < Width; i++) - { - if (INDMode) - { - dataaddr = WORD(DmaRead(graphaddr + i), Registers[CHARBASE] + Offset); - } - - for (var j=0; j < indbytes; j++) - { - if (Holey == 0x02 && ((dataaddr & 0x9000) == 0x9000) || Holey == 0x01 && ((dataaddr & 0x8800) == 0x8800)) - { - hpos += 8; - dataaddr++; - continue; - } - - int d = DmaRead(dataaddr++); - - var c = ((d & 0x80) >> 6) | (((PaletteNo >> 2) & 2) >> 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x40) >> 5) | ((PaletteNo >> 2) & 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x20) >> 4) | (((PaletteNo >> 2) & 2) >> 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x10) >> 3) | ((PaletteNo >> 2) & 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x08) >> 2) | (((PaletteNo >> 2) & 2) >> 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x04) >> 1) | ((PaletteNo >> 2) & 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = (d & 0x02) | (((PaletteNo >> 2) & 2) >> 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - - c = ((d & 0x01) << 1) | ((PaletteNo >> 2) & 1); - if (c != 0) - LineRAM[hpos & 0x1ff] = (byte)((PaletteNo & 0x10) | c); - else if (Kangaroo) - LineRAM[hpos & 0x1ff] = 0; - - hpos++; - } - } - } - - void OutputLineRAM() - { - var fbi = ((Scanline + 1) * M.FrameBuffer.VisiblePitch) % M.FrameBuffer.VideoBufferByteLength; - - for (int i = 0; i < M.FrameBuffer.VisiblePitch; i++) - { - var colorIndex = LineRAM[i]; - M.FrameBuffer.VideoBuffer[fbi++] = Registers[BACKGRND + ((colorIndex & 3) == 0 ? 0 : colorIndex)]; - if (fbi == M.FrameBuffer.VideoBufferByteLength) - fbi = 0; - } - - for (var i = 0; i < LineRAM.Length; i++) - { - LineRAM[i] = 0; - } - } - - #endregion - - #region Maria Peek - - byte peek(ushort addr) - { - addr &= 0x3f; - var mi = M.InputState; - - switch(addr) - { - case MSTAT: - var sl = Scanline; - return (sl < FirstVisibleScanline || sl >= LastVisibleScanline) - ? (byte)0x80 // VBLANK ON - : (byte)0; // VBLANK OFF - case INPT0: return mi.SampleCapturedControllerActionState(0, ControllerAction.Trigger) ? (byte)0x80 : (byte)0; // player1,button R - case INPT1: return mi.SampleCapturedControllerActionState(0, ControllerAction.Trigger2) ? (byte)0x80 : (byte)0; // player1,button L - case INPT2: return mi.SampleCapturedControllerActionState(1, ControllerAction.Trigger) ? (byte)0x80 : (byte)0; // player2,button R - case INPT3: return mi.SampleCapturedControllerActionState(1, ControllerAction.Trigger2) ? (byte)0x80 : (byte)0; // player2,button L - case INPT4: return SampleINPTLatched(4) ? (byte)0 : (byte)0x80; // player1,button L/R - case INPT5: return SampleINPTLatched(5) ? (byte)0 : (byte)0x80; // player2,button L/R - default: - LogDebug("Maria: Unhandled peek at ${0:x4}, PC=${1:x4}", addr, M.CPU.PC); - var retval = Registers[addr]; - return retval; - } - } - - #endregion - - #region Maria Poke - - void poke(ushort addr, byte data) - { - addr &= 0x3f; - - switch (addr) - { - // INPUT PORT CONTROL - // Only the first four bits of INPTCTRL are used: - // D0: lock mode (after this bit has been set high, no more mode changes can be done until the console is turned off) - // D1: 0=disable MARIA (only RIOT RAM is available); 1=enable MARIA (also enables system RAM) - // D2: 0=enable BIOS at $8000-$FFFF (actually NTSC only uses 4KB and PAL uses 16KB); 1=disable BIOS and enable cartridge - // D3: 0=disable TIA video pull-ups (video output is MARIA instead of TIA); 1=enable TIA video pull-ups (video output is TIA instead of MARIA) - // - case INPTCTRL: - if (CtrlLock) - { - Log("Maria: INPTCTRL: LOCKED: Ignoring: ${0:x2}, PC=${1:x4}", data, M.CPU.PC); - break; - } - - CtrlLock = (data & (1 << 0)) != 0; - var mariaEnable = (data & (1 << 1)) != 0; - var biosDisable = (data & (1 << 2)) != 0; - var tiaopEnable = (data & (1 << 3)) != 0; - - Log("Maria: INPTCTRL: ${0:x2}, PC=${1:x4}, lockMode={2}, mariaEnable={3} biosDisable={4} tiaOutput={5}", - data, M.CPU.PC, CtrlLock, mariaEnable, biosDisable, tiaopEnable); - - if (biosDisable) - { - M.SwapOutBIOS(); - } - else - { - M.SwapInBIOS(); - } - break; - case WSYNC: - // Request a CPU preemption to service the delay request - M.CPU.EmulatorPreemptRequest = true; - break; - case CTRL: - ColorKill = (data & 0x80) != 0; - DMAEnabled = (data & 0x60) == 0x40; - CWidth = (data & 0x10) != 0; - BCntl = (data & 0x08) != 0; - Kangaroo = (data & 0x04) != 0; - RM = (byte)(data & 0x03); - break; - case MSTAT: - break; - case CHARBASE: - case DPPH: - case DPPL: - Registers[addr] = data; - break; - case BACKGRND: - case P0C1: - case P0C2: - case P0C3: - case P1C1: - case P1C2: - case P1C3: - case P2C1: - case P2C2: - case P2C3: - case P3C1: - case P3C2: - case P3C3: - case P4C1: - case P4C2: - case P4C3: - case P5C1: - case P5C2: - case P5C3: - case P6C1: - case P6C2: - case P6C3: - case P7C1: - case P7C2: - case P7C3: - Registers[addr] = data; - break; - case AUDC0: - case AUDC1: - case AUDF0: - case AUDF1: - case AUDV0: - case AUDV1: - TIASound.Update(addr, data); - break; - case OFFSET: - Log("Maria: OFFSET: ROM wrote ${0:x2}, PC=${1:x4} (reserved for future expansion)", data, M.CPU.PC); - break; - default: - Registers[addr] = data; - LogDebug("Maria: Unhandled poke:${0:x4} w/${1:x2}, PC=${2:x4}", addr, data, M.CPU.PC); - break; - } - } - - #endregion - - #region Input Helpers - - bool SampleINPTLatched(int inpt) - { - var mi = M.InputState; - var playerNo = inpt - 4; - - switch (playerNo == 0 ? mi.LeftControllerJack : mi.RightControllerJack) - { - case Controller.Joystick: - return mi.SampleCapturedControllerActionState(playerNo, ControllerAction.Trigger); - case Controller.ProLineJoystick: - var portbline = 4 << (playerNo << 1); - if ((M.PIA.DDRB & portbline) != 0 && (M.PIA.WrittenPortB & portbline) == 0) - return false; - return mi.SampleCapturedControllerActionState(playerNo, ControllerAction.Trigger) - || mi.SampleCapturedControllerActionState(playerNo, ControllerAction.Trigger2); - case Controller.Lightgun: - - // This is one area where always running fixed at the faster CPU frequency creates emulation challenges. - // Fortunately since lightgun sampling is a dedicated activity on a frame, the job of compensating is tractable. - - // Track the number of samples this frame, the time of the first sample, and capture the lightgun location. - if (_lightgunFirstSampleCpuClock == 0) - { - _lightgunFirstSampleCpuClock = M.CPU.Clock; - _lightgunFrameSamples = 0; - mi.SampleCapturedLightGunPosition(playerNo, out _lightgunSampledScanline, out _lightgunSampledVisibleHpos); - } - _lightgunFrameSamples++; - - // Magic Adjustment Factor - // Seems sufficient to account for the timing impact of successive lightrun reads (i.e., 'slow' memory accesses.) - // Obtained through through trial-and-error. - const float magicAdjustmentFactor = 2.135f; - - var firstLightgunSampleMariaFrameClock = (int)((_lightgunFirstSampleCpuClock - _startOfFrameCpuClock) << 2); - var mariaClocksSinceFirstLightgunSample = (int)((M.CPU.Clock - _lightgunFirstSampleCpuClock) << 2); - var adjustmentMariaClocks = (int)Math.Round(_lightgunFrameSamples * magicAdjustmentFactor); - var actualMariaFrameClock = firstLightgunSampleMariaFrameClock + mariaClocksSinceFirstLightgunSample + adjustmentMariaClocks; - var actualScanline = actualMariaFrameClock / 456; - var actualHpos = actualMariaFrameClock % 456; - - // Lightgun sampling looks intended to begin at the start of the scanline. - // Compensate with another magic constant since we're always off by a fixed amount. - actualHpos -= 62; - if (actualHpos < 0) - { - actualHpos += 456; - actualScanline--; - } - - var sampledScanline = _lightgunSampledScanline; - var sampledVisibleHpos = _lightgunSampledVisibleHpos; - - // Seems reasonable the gun sees more than a single pixel (more like a circle or oval) and triggers sooner accordingly. - // These adjustments were obtained through trial-and-error. - if (_isPal) - { - sampledScanline -= 19; - } - else - { - sampledScanline -= 16; - sampledVisibleHpos += 4; - } - return (actualScanline >= sampledScanline) && (actualHpos >= (sampledVisibleHpos + 136 /* HBLANK clocks */)); - } - return false; - } - - #endregion - - #region Serialization Members - - public Maria(DeserializationContext input, Machine7800 m, int scanlines) - { - if (input == null) - throw new ArgumentNullException("input"); - if (m == null) - throw new ArgumentNullException("m"); - - M = m; - InitializeVisibleScanlineValues(scanlines); - TIASound = new TIASound(input, M, CPU_TICKS_PER_AUDIO_SAMPLE); - - var version = input.CheckVersion(1, 2); - LineRAM = input.ReadExpectedBytes(512); - if (version == 1) - { - // formerly persisted values, MariaPalette[8,4] - for (var i = 0; i < 32; i++) - input.ReadByte(); - } - Registers = input.ReadExpectedBytes(0x40); - if (version == 1) - { - // formerly persisted value, Scanline - input.ReadInt32(); - } - switch (version) - { - case 1: - WM = (input.ReadByte() != 0); - break; - case 2: - WM = input.ReadBoolean(); - break; - } - DLL = input.ReadUInt16(); - DL = input.ReadUInt16(); - Offset = input.ReadInt32(); - Holey = input.ReadInt32(); - Width = input.ReadInt32(); - HPOS = input.ReadByte(); - PaletteNo = input.ReadByte(); - INDMode = input.ReadBoolean(); - if (version == 1) - { - // formerly persisted value (DLI) - input.ReadBoolean(); - } - CtrlLock = input.ReadBoolean(); - if (version == 1) - { - // formerly persisted value (VBLANK) - input.ReadByte(); - } - DMAEnabled = input.ReadBoolean(); - if (version == 1) - { - // formerly persisted value (DMAOn) - input.ReadBoolean(); - } - ColorKill = input.ReadBoolean(); - CWidth = input.ReadBoolean(); - BCntl = input.ReadBoolean(); - Kangaroo = input.ReadBoolean(); - RM = input.ReadByte(); - } - - public void GetObjectData(SerializationContext output) - { - if (output == null) - throw new ArgumentNullException("output"); - - output.Write(TIASound); - - output.WriteVersion(2); - output.Write(LineRAM); - output.Write(Registers); - output.Write(WM); - output.Write(DLL); - output.Write(DL); - output.Write(Offset); - output.Write(Holey); - output.Write(Width); - output.Write(HPOS); - output.Write((byte)PaletteNo); - output.Write(INDMode); - output.Write(CtrlLock); - output.Write(DMAEnabled); - output.Write(ColorKill); - output.Write(CWidth); - output.Write(BCntl); - output.Write(Kangaroo); - output.Write(RM); - } - - #endregion - - #region Helpers - - void ConsumeNextDLLEntry() - { - // Display List List (DLL) entry - var dll0 = DmaRead(DLL++); // DLI, Holey, Offset - var dll1 = DmaRead(DLL++); // High DL address - var dll2 = DmaRead(DLL++); // Low DL address - - var dli = (dll0 & 0x80) != 0; - Holey = (dll0 & 0x60) >> 5; - Offset = dll0 & 0x0f; - - // Update current Display List (DL) - DL = WORD(dll2, dll1); - - if (dli) - { - M.CPU.NMIInterruptRequest = true; - - // DMA TIMING: One tick between DMA Shutdown and DLI - _dmaClocks += 1; - } - } - - void InitializeVisibleScanlineValues(int scanlines) - { - switch (scanlines) - { - case 262: // NTSC - FirstVisibleScanline = 11; - LastVisibleScanline = FirstVisibleScanline + 242; - _isPal = false; - break; - case 312: // PAL - FirstVisibleScanline = 11; - LastVisibleScanline = FirstVisibleScanline + 292; - _isPal = true; - break; - default: - throw new ArgumentException("scanlines must be 262 or 312."); - } - } - - void Log(string format, params object[] args) - { - if (M == null || M.Logger == null) - return; - M.Logger.WriteLine(format, args); - } - - // convenience overload - static ushort WORD(int lsb, int msb) - { - return WORD((byte)lsb, (byte)msb); - } - - static ushort WORD(byte lsb, byte msb) - { - return (ushort)(lsb | msb << 8); - } - - // convenience overload - byte DmaRead(int addr) - { - return DmaRead((ushort)addr); - } - - byte DmaRead(ushort addr) - { -#if DEBUG - if (addr < 0x1800) - LogDebug("Maria: Questionable DMA read at ${0:x4} by PC=${1:x4}", addr, M.CPU.PC); -#endif - return M.Mem[addr]; - } - - [System.Diagnostics.Conditional("DEBUG")] - void LogDebug(string format, params object[] args) - { - if (M == null || M.Logger == null) - return; - M.Logger.WriteLine(format, args); - } - - [System.Diagnostics.Conditional("DEBUG")] - void AssertDebug(bool cond) - { - if (!cond) - System.Diagnostics.Debugger.Break(); - } - - #endregion - } -}