976 lines
25 KiB
C#
976 lines
25 KiB
C#
![]() |
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace BizHawk.Emulation.Computers.Commodore64.MOS
|
|||
|
{
|
|||
|
public abstract partial class Vic : IStandardIO
|
|||
|
{
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
private class Sprite
|
|||
|
{
|
|||
|
public bool collideData;
|
|||
|
public bool collideSprite;
|
|||
|
public uint color;
|
|||
|
public bool enable;
|
|||
|
public uint mc;
|
|||
|
public uint mcbase;
|
|||
|
public bool multicolor;
|
|||
|
public uint pointer;
|
|||
|
public bool priority;
|
|||
|
public uint sr;
|
|||
|
public uint x;
|
|||
|
public bool xExpand;
|
|||
|
public uint y;
|
|||
|
public bool yExpand;
|
|||
|
|
|||
|
public void HardReset()
|
|||
|
{
|
|||
|
collideData = false;
|
|||
|
collideSprite = false;
|
|||
|
color = 0;
|
|||
|
enable = false;
|
|||
|
mc = 0;
|
|||
|
mcbase = 0;
|
|||
|
multicolor = false;
|
|||
|
pointer = 0;
|
|||
|
priority = false;
|
|||
|
sr = 0;
|
|||
|
x = 0;
|
|||
|
xExpand = false;
|
|||
|
y = 0;
|
|||
|
yExpand = false;
|
|||
|
}
|
|||
|
}
|
|||
|
private Sprite[] sprites;
|
|||
|
|
|||
|
private uint backgroundColor0;
|
|||
|
private uint backgroundColor1;
|
|||
|
private uint backgroundColor2;
|
|||
|
private uint backgroundColor3;
|
|||
|
private uint baCount;
|
|||
|
private bool badline;
|
|||
|
private bool badlineEnable;
|
|||
|
private bool bitmapMode;
|
|||
|
private bool borderCheckLEnable;
|
|||
|
private bool borderCheckREnable;
|
|||
|
private uint borderColor;
|
|||
|
private bool borderOnMain;
|
|||
|
private bool borderOnVertical;
|
|||
|
private uint[] bufferC;
|
|||
|
private uint[] bufferG;
|
|||
|
private byte bus;
|
|||
|
private bool columnSelect;
|
|||
|
private uint cycle;
|
|||
|
private uint cycleIndex;
|
|||
|
private uint dataC;
|
|||
|
private uint dataG;
|
|||
|
private bool displayEnable;
|
|||
|
private bool enableIntLightPen;
|
|||
|
private bool enableIntRaster;
|
|||
|
private bool enableIntSpriteCollision;
|
|||
|
private bool enableIntSpriteDataCollision;
|
|||
|
private bool extraColorMode;
|
|||
|
private bool idle;
|
|||
|
private bool intLightPen;
|
|||
|
private bool intRaster;
|
|||
|
private bool intSpriteCollision;
|
|||
|
private bool intSpriteDataCollision;
|
|||
|
private uint lastRasterLine;
|
|||
|
private uint lightPenX;
|
|||
|
private uint lightPenY;
|
|||
|
private bool multicolorMode;
|
|||
|
private uint pointerCB;
|
|||
|
private uint pointerVM;
|
|||
|
private uint rasterInterruptLine;
|
|||
|
private uint rasterLine;
|
|||
|
private uint rasterX;
|
|||
|
private uint rc;
|
|||
|
private uint refreshCounter;
|
|||
|
private bool rowSelect;
|
|||
|
private uint spriteMulticolor0;
|
|||
|
private uint spriteMulticolor1;
|
|||
|
private uint sr;
|
|||
|
private uint srCount;
|
|||
|
private uint vc;
|
|||
|
private uint vcbase;
|
|||
|
private uint vmli;
|
|||
|
private uint xScroll;
|
|||
|
private uint yScroll;
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
private C64Chips chips;
|
|||
|
private int cyclesPerSec;
|
|||
|
private bool pinAEC;
|
|||
|
private bool pinBA;
|
|||
|
private bool pinIRQ;
|
|||
|
private uint[][] pipeline;
|
|||
|
private uint totalCycles;
|
|||
|
private uint totalLines;
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
public Vic(C64Chips newChips, uint newCycles, uint newLines, uint[][] newPipeline, int newCyclesPerSec)
|
|||
|
{
|
|||
|
chips = newChips;
|
|||
|
totalCycles = newCycles;
|
|||
|
totalLines = newLines;
|
|||
|
pipeline = newPipeline;
|
|||
|
cyclesPerSec = newCyclesPerSec;
|
|||
|
|
|||
|
bufWidth = (int)(totalCycles * 8);
|
|||
|
bufHeight = (int)(totalLines);
|
|||
|
buf = new int[bufWidth * bufHeight];
|
|||
|
bufLength = (uint)buf.Length;
|
|||
|
|
|||
|
sprites = new Sprite[8];
|
|||
|
for (uint i = 0; i < 8; i++)
|
|||
|
sprites[i] = new Sprite();
|
|||
|
|
|||
|
bufferC = new uint[40];
|
|||
|
bufferG = new uint[40];
|
|||
|
}
|
|||
|
|
|||
|
public void HardReset()
|
|||
|
{
|
|||
|
pinAEC = true;
|
|||
|
pinBA = true;
|
|||
|
pinIRQ = false;
|
|||
|
|
|||
|
bufOffset = 0;
|
|||
|
|
|||
|
backgroundColor0 = 0;
|
|||
|
backgroundColor1 = 0;
|
|||
|
backgroundColor2 = 0;
|
|||
|
backgroundColor3 = 0;
|
|||
|
baCount = 4;
|
|||
|
badline = false;
|
|||
|
badlineEnable = false;
|
|||
|
bitmapMode = false;
|
|||
|
borderCheckLEnable = false;
|
|||
|
borderCheckREnable = false;
|
|||
|
borderColor = 0;
|
|||
|
borderOnMain = true;
|
|||
|
borderOnVertical = true;
|
|||
|
columnSelect = false;
|
|||
|
displayEnable = false;
|
|||
|
enableIntLightPen = false;
|
|||
|
enableIntRaster = false;
|
|||
|
enableIntSpriteCollision = false;
|
|||
|
enableIntSpriteDataCollision = false;
|
|||
|
extraColorMode = false;
|
|||
|
idle = true;
|
|||
|
intLightPen = false;
|
|||
|
intRaster = false;
|
|||
|
intSpriteCollision = false;
|
|||
|
intSpriteDataCollision = false;
|
|||
|
lastRasterLine = 0;
|
|||
|
lightPenX = 0;
|
|||
|
lightPenY = 0;
|
|||
|
multicolorMode = false;
|
|||
|
pointerCB = 0;
|
|||
|
pointerVM = 0;
|
|||
|
rasterInterruptLine = 0;
|
|||
|
rasterLine = 0;
|
|||
|
rasterX = 0;
|
|||
|
rc = 7;
|
|||
|
refreshCounter = 0xFF;
|
|||
|
rowSelect = false;
|
|||
|
spriteMulticolor0 = 0;
|
|||
|
spriteMulticolor1 = 0;
|
|||
|
sr = 0;
|
|||
|
srCount = 0;
|
|||
|
vc = 0;
|
|||
|
vcbase = 0;
|
|||
|
vmli = 0;
|
|||
|
xScroll = 0;
|
|||
|
yScroll = 0;
|
|||
|
|
|||
|
for (uint i = 0; i < 8; i++)
|
|||
|
sprites[i].HardReset();
|
|||
|
|
|||
|
for (uint i = 0; i < 40; i++)
|
|||
|
{
|
|||
|
bufferC[i] = 0;
|
|||
|
bufferG[i] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void UpdateBA()
|
|||
|
{
|
|||
|
if (pinBA)
|
|||
|
baCount = 4;
|
|||
|
else if (baCount > 0)
|
|||
|
baCount--;
|
|||
|
}
|
|||
|
|
|||
|
private void UpdatePins()
|
|||
|
{
|
|||
|
pinIRQ = !(
|
|||
|
(enableIntRaster & intRaster) |
|
|||
|
(enableIntSpriteDataCollision & intSpriteDataCollision) |
|
|||
|
(enableIntSpriteCollision & intSpriteCollision) |
|
|||
|
(enableIntLightPen & intLightPen));
|
|||
|
}
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
protected const uint pipelineUpdateVc = 1;
|
|||
|
protected const uint pipelineChkSprChunch = 2;
|
|||
|
protected const uint pipelineUpdateMcBase = 4;
|
|||
|
protected const uint pipelineChkBrdL1 = 8;
|
|||
|
protected const uint pipelineChkBrdL0 = 16;
|
|||
|
protected const uint pipelineChkSprDma = 32;
|
|||
|
protected const uint pipelineChkBrdR0 = 64;
|
|||
|
protected const uint pipelineChkSprExp = 128;
|
|||
|
protected const uint pipelineChkBrdR1 = 256;
|
|||
|
protected const uint pipelineChkSprDisp = 512;
|
|||
|
protected const uint pipelineUpdateRc = 1024;
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
public void ExecutePhase1()
|
|||
|
{
|
|||
|
// always assert access over the bus in ph1
|
|||
|
pinAEC = false;
|
|||
|
|
|||
|
// raster IRQ compare
|
|||
|
if ((cycle == 0 && rasterLine > 0) || (cycle == 1 && rasterLine == 0))
|
|||
|
{
|
|||
|
if (rasterLine != lastRasterLine)
|
|||
|
if (rasterLine == rasterInterruptLine)
|
|||
|
intRaster = true;
|
|||
|
lastRasterLine = rasterLine;
|
|||
|
}
|
|||
|
|
|||
|
// display enable compare
|
|||
|
if (rasterLine == 0x030)
|
|||
|
badlineEnable |= displayEnable;
|
|||
|
|
|||
|
// badline compare
|
|||
|
if (badlineEnable && rasterLine >= 0x030 && rasterLine < 0x0F7 && ((rasterLine & 0x7) == yScroll))
|
|||
|
badline = true;
|
|||
|
|
|||
|
if (badline)
|
|||
|
idle = false;
|
|||
|
|
|||
|
ParseCycle();
|
|||
|
|
|||
|
Render();
|
|||
|
|
|||
|
// must always come last
|
|||
|
UpdatePins();
|
|||
|
}
|
|||
|
|
|||
|
public void ExecutePhase2()
|
|||
|
{
|
|||
|
ParseCycle();
|
|||
|
|
|||
|
// advance cycle and optionally raster line
|
|||
|
cycle++;
|
|||
|
if (cycle == totalCycles)
|
|||
|
{
|
|||
|
vcbase = 0;
|
|||
|
cycleIndex = 0;
|
|||
|
cycle = 0;
|
|||
|
rasterLine++;
|
|||
|
if (rasterLine == totalLines)
|
|||
|
rasterLine = 0;
|
|||
|
}
|
|||
|
|
|||
|
// if the BA counter is nonzero, allow CPU bus access
|
|||
|
UpdateBA();
|
|||
|
if (baCount > 0)
|
|||
|
pinAEC = true;
|
|||
|
|
|||
|
Render();
|
|||
|
|
|||
|
// must always come last
|
|||
|
UpdatePins();
|
|||
|
}
|
|||
|
|
|||
|
private void ParseCycle()
|
|||
|
{
|
|||
|
ushort addr = 0x3FFF;
|
|||
|
uint cycleBAsprite0;
|
|||
|
uint cycleBAsprite1;
|
|||
|
uint cycleBAsprite2;
|
|||
|
uint cycleFetchSpriteIndex;
|
|||
|
uint fetch = pipeline[1][cycleIndex];
|
|||
|
uint ba = pipeline[2][cycleIndex];
|
|||
|
uint act = pipeline[3][cycleIndex];
|
|||
|
|
|||
|
// apply X location
|
|||
|
rasterX = pipeline[0][cycleIndex];
|
|||
|
|
|||
|
// perform fetch
|
|||
|
switch (fetch & 0xFF00)
|
|||
|
{
|
|||
|
case 0x0100:
|
|||
|
// fetch R
|
|||
|
refreshCounter = (refreshCounter - 1) & 0xFF;
|
|||
|
addr = (ushort)(0x3F00 | refreshCounter);
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
break;
|
|||
|
case 0x0200:
|
|||
|
// fetch C
|
|||
|
if (!idle)
|
|||
|
{
|
|||
|
if (badline)
|
|||
|
{
|
|||
|
addr = (ushort)((pointerVM << 10) | vc);
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
dataC = bus;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dataC = bufferC[vmli];
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dataC = 0;
|
|||
|
}
|
|||
|
break;
|
|||
|
case 0x0300:
|
|||
|
// fetch G
|
|||
|
if (idle)
|
|||
|
addr = 0x3FFF;
|
|||
|
else
|
|||
|
{
|
|||
|
if (bitmapMode)
|
|||
|
addr = (ushort)(rc | (vc << 3) | ((pointerCB & 0x4) << 11));
|
|||
|
else
|
|||
|
addr = (ushort)(rc | (dataG << 3) | (pointerCB << 9));
|
|||
|
}
|
|||
|
if (extraColorMode)
|
|||
|
addr &= 0x39FF;
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
dataG = bus;
|
|||
|
vmli++;
|
|||
|
vc++;
|
|||
|
break;
|
|||
|
case 0x0400:
|
|||
|
// fetch I
|
|||
|
addr = (extraColorMode ? (ushort)0x39FF : (ushort)0x3FFF);
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
dataG = bus;
|
|||
|
dataC = 0;
|
|||
|
break;
|
|||
|
case 0x0500:
|
|||
|
// no fetch
|
|||
|
break;
|
|||
|
default:
|
|||
|
cycleFetchSpriteIndex = (fetch & 0x7);
|
|||
|
switch (fetch & 0xF0)
|
|||
|
{
|
|||
|
case 0x00:
|
|||
|
// fetch P
|
|||
|
addr = (ushort)(0x1F8 | (pointerVM << 10) | cycleFetchSpriteIndex);
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
sprites[cycleFetchSpriteIndex].pointer = bus;
|
|||
|
break;
|
|||
|
case 0x10:
|
|||
|
case 0x20:
|
|||
|
case 0x30:
|
|||
|
// fetch S
|
|||
|
addr = (ushort)(sprites[cycleFetchSpriteIndex].mc | sprites[cycleFetchSpriteIndex].pointer << 6);
|
|||
|
bus = chips.pla.ReadVic(addr);
|
|||
|
sprites[cycleFetchSpriteIndex].sr <<= 8;
|
|||
|
sprites[cycleFetchSpriteIndex].sr |= bus;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// perform BA flag manipulation
|
|||
|
switch (ba)
|
|||
|
{
|
|||
|
case 0x0000:
|
|||
|
pinBA = true;
|
|||
|
break;
|
|||
|
case 0x1000:
|
|||
|
pinBA = !badline;
|
|||
|
break;
|
|||
|
default:
|
|||
|
cycleBAsprite0 = ba & 0x000F;
|
|||
|
cycleBAsprite1 = ba & 0x00F0;
|
|||
|
cycleBAsprite2 = ba & 0x0F00;
|
|||
|
if ((cycleBAsprite0 < 8 && sprites[cycleBAsprite0].enable) ||
|
|||
|
(cycleBAsprite1 < 8 && sprites[cycleBAsprite1].enable) ||
|
|||
|
(cycleBAsprite2 < 8 && sprites[cycleBAsprite2].enable))
|
|||
|
pinBA = false;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// perform actions
|
|||
|
borderCheckLEnable = false;
|
|||
|
borderCheckREnable = false;
|
|||
|
|
|||
|
if (!columnSelect && (act & pipelineChkBrdL0) != 0)
|
|||
|
borderCheckLEnable = true;
|
|||
|
if (columnSelect && (act & pipelineChkBrdL1) != 0)
|
|||
|
borderCheckLEnable = true;
|
|||
|
if (!columnSelect && (act & pipelineChkBrdR0) != 0)
|
|||
|
borderCheckREnable = true;
|
|||
|
if (columnSelect && (act & pipelineChkBrdR1) != 0)
|
|||
|
borderCheckREnable = true;
|
|||
|
if ((act & pipelineChkSprChunch) != 0)
|
|||
|
{
|
|||
|
}
|
|||
|
if ((act & pipelineChkSprDisp) != 0)
|
|||
|
{
|
|||
|
}
|
|||
|
if ((act & pipelineChkSprDma) != 0)
|
|||
|
{
|
|||
|
}
|
|||
|
if ((act & pipelineChkSprExp) != 0)
|
|||
|
{
|
|||
|
}
|
|||
|
if ((act & pipelineUpdateMcBase) != 0)
|
|||
|
{
|
|||
|
}
|
|||
|
if ((act & pipelineUpdateRc) != 0)
|
|||
|
{
|
|||
|
if (rc == 7)
|
|||
|
{
|
|||
|
idle = true;
|
|||
|
vcbase = vc;
|
|||
|
}
|
|||
|
if (!idle)
|
|||
|
rc++;
|
|||
|
}
|
|||
|
if ((act & pipelineUpdateVc) != 0)
|
|||
|
{
|
|||
|
vc = vcbase;
|
|||
|
vmli = 0;
|
|||
|
if (badline)
|
|||
|
rc = 0;
|
|||
|
}
|
|||
|
|
|||
|
cycleIndex++;
|
|||
|
}
|
|||
|
|
|||
|
private void Render()
|
|||
|
{
|
|||
|
if (borderCheckLEnable)
|
|||
|
{
|
|||
|
}
|
|||
|
if (borderCheckREnable)
|
|||
|
{
|
|||
|
}
|
|||
|
WritePixel(0);
|
|||
|
WritePixel(0);
|
|||
|
WritePixel(0);
|
|||
|
WritePixel(0);
|
|||
|
}
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
public bool AEC { get { return pinAEC; } }
|
|||
|
public bool BA { get { return pinBA; } }
|
|||
|
public bool IRQ { get { return pinIRQ; } }
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
public int CyclesPerFrame
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return (int)(totalCycles * totalLines);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public int CyclesPerSecond
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
return cyclesPerSec;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// ------------------------------------
|
|||
|
|
|||
|
public byte Peek(int addr)
|
|||
|
{
|
|||
|
return ReadRegister((ushort)(addr & 0x3F));
|
|||
|
}
|
|||
|
|
|||
|
public void Poke(int addr, byte val)
|
|||
|
{
|
|||
|
WriteRegister((ushort)(addr & 0x3F), val);
|
|||
|
}
|
|||
|
|
|||
|
public byte Read(ushort addr)
|
|||
|
{
|
|||
|
byte result;
|
|||
|
addr &= 0x3F;
|
|||
|
|
|||
|
switch (addr)
|
|||
|
{
|
|||
|
case 0x1E:
|
|||
|
case 0x1F:
|
|||
|
// reading clears these
|
|||
|
result = ReadRegister(addr);
|
|||
|
WriteRegister(addr, 0);
|
|||
|
break;
|
|||
|
default:
|
|||
|
result = ReadRegister((ushort)(addr & 0x3F));
|
|||
|
break;
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
private byte ReadRegister(ushort addr)
|
|||
|
{
|
|||
|
byte result = 0xFF; //unused bit value
|
|||
|
|
|||
|
switch (addr)
|
|||
|
{
|
|||
|
case 0x00:
|
|||
|
case 0x02:
|
|||
|
case 0x04:
|
|||
|
case 0x06:
|
|||
|
case 0x08:
|
|||
|
case 0x0A:
|
|||
|
case 0x0C:
|
|||
|
case 0x0E:
|
|||
|
result = (byte)(sprites[addr >> 1].x & 0xFF);
|
|||
|
break;
|
|||
|
case 0x01:
|
|||
|
case 0x03:
|
|||
|
case 0x05:
|
|||
|
case 0x07:
|
|||
|
case 0x09:
|
|||
|
case 0x0B:
|
|||
|
case 0x0D:
|
|||
|
case 0x0F:
|
|||
|
result = (byte)(sprites[addr >> 1].y & 0xFF);
|
|||
|
break;
|
|||
|
case 0x10:
|
|||
|
result = (byte)(
|
|||
|
((sprites[0].x >> 8) & 0x01) |
|
|||
|
((sprites[1].x >> 7) & 0x02) |
|
|||
|
((sprites[2].x >> 6) & 0x04) |
|
|||
|
((sprites[3].x >> 5) & 0x08) |
|
|||
|
((sprites[4].x >> 4) & 0x10) |
|
|||
|
((sprites[5].x >> 3) & 0x20) |
|
|||
|
((sprites[6].x >> 2) & 0x40) |
|
|||
|
((sprites[7].x >> 1) & 0x80)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x11:
|
|||
|
result = (byte)(
|
|||
|
(byte)(yScroll & 0x7) |
|
|||
|
(rowSelect ? (byte)0x08 : (byte)0x00) |
|
|||
|
(displayEnable ? (byte)0x10 : (byte)0x00) |
|
|||
|
(bitmapMode ? (byte)0x20 : (byte)0x00) |
|
|||
|
(extraColorMode ? (byte)0x40 : (byte)0x00) |
|
|||
|
(byte)((rasterLine & 0x100) >> 1)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x12:
|
|||
|
result = (byte)(rasterLine & 0xFF);
|
|||
|
break;
|
|||
|
case 0x13:
|
|||
|
result = (byte)(lightPenX & 0xFF);
|
|||
|
break;
|
|||
|
case 0x14:
|
|||
|
result = (byte)(lightPenY & 0xFF);
|
|||
|
break;
|
|||
|
case 0x15:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].enable ? 0x01 : 0x00) |
|
|||
|
(sprites[1].enable ? 0x02 : 0x00) |
|
|||
|
(sprites[2].enable ? 0x04 : 0x00) |
|
|||
|
(sprites[3].enable ? 0x08 : 0x00) |
|
|||
|
(sprites[4].enable ? 0x10 : 0x00) |
|
|||
|
(sprites[5].enable ? 0x20 : 0x00) |
|
|||
|
(sprites[6].enable ? 0x40 : 0x00) |
|
|||
|
(sprites[7].enable ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x16:
|
|||
|
result &= 0xBF;
|
|||
|
result |= (byte)(
|
|||
|
(byte)(xScroll & 0x7) |
|
|||
|
(columnSelect ? (byte)0x08 : (byte)0x00) |
|
|||
|
(multicolorMode ? (byte)0x10 : (byte)0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x17:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].yExpand ? 0x01 : 0x00) |
|
|||
|
(sprites[1].yExpand ? 0x02 : 0x00) |
|
|||
|
(sprites[2].yExpand ? 0x04 : 0x00) |
|
|||
|
(sprites[3].yExpand ? 0x08 : 0x00) |
|
|||
|
(sprites[4].yExpand ? 0x10 : 0x00) |
|
|||
|
(sprites[5].yExpand ? 0x20 : 0x00) |
|
|||
|
(sprites[6].yExpand ? 0x40 : 0x00) |
|
|||
|
(sprites[7].yExpand ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x18:
|
|||
|
result &= 0xFE;
|
|||
|
result |= (byte)(
|
|||
|
((pointerVM & 0xF) << 4) |
|
|||
|
((pointerCB & 0x7) << 1)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x19:
|
|||
|
result &= 0x70;
|
|||
|
result |= (byte)(
|
|||
|
(intRaster ? 0x01 : 0x00) |
|
|||
|
(intSpriteDataCollision ? 0x02 : 0x00) |
|
|||
|
(intSpriteCollision ? 0x04 : 0x00) |
|
|||
|
(intLightPen ? 0x08 : 0x00) |
|
|||
|
(pinIRQ ? 0x00 : 0x80)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1A:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(
|
|||
|
(enableIntRaster ? 0x01 : 0x00) |
|
|||
|
(enableIntSpriteDataCollision ? 0x02 : 0x00) |
|
|||
|
(enableIntSpriteCollision ? 0x04 : 0x00) |
|
|||
|
(enableIntLightPen ? 0x08 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1B:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].priority ? 0x01 : 0x00) |
|
|||
|
(sprites[1].priority ? 0x02 : 0x00) |
|
|||
|
(sprites[2].priority ? 0x04 : 0x00) |
|
|||
|
(sprites[3].priority ? 0x08 : 0x00) |
|
|||
|
(sprites[4].priority ? 0x10 : 0x00) |
|
|||
|
(sprites[5].priority ? 0x20 : 0x00) |
|
|||
|
(sprites[6].priority ? 0x40 : 0x00) |
|
|||
|
(sprites[7].priority ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1C:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].multicolor ? 0x01 : 0x00) |
|
|||
|
(sprites[1].multicolor ? 0x02 : 0x00) |
|
|||
|
(sprites[2].multicolor ? 0x04 : 0x00) |
|
|||
|
(sprites[3].multicolor ? 0x08 : 0x00) |
|
|||
|
(sprites[4].multicolor ? 0x10 : 0x00) |
|
|||
|
(sprites[5].multicolor ? 0x20 : 0x00) |
|
|||
|
(sprites[6].multicolor ? 0x40 : 0x00) |
|
|||
|
(sprites[7].multicolor ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1D:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].xExpand ? 0x01 : 0x00) |
|
|||
|
(sprites[1].xExpand ? 0x02 : 0x00) |
|
|||
|
(sprites[2].xExpand ? 0x04 : 0x00) |
|
|||
|
(sprites[3].xExpand ? 0x08 : 0x00) |
|
|||
|
(sprites[4].xExpand ? 0x10 : 0x00) |
|
|||
|
(sprites[5].xExpand ? 0x20 : 0x00) |
|
|||
|
(sprites[6].xExpand ? 0x40 : 0x00) |
|
|||
|
(sprites[7].xExpand ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1E:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].collideSprite ? 0x01 : 0x00) |
|
|||
|
(sprites[1].collideSprite ? 0x02 : 0x00) |
|
|||
|
(sprites[2].collideSprite ? 0x04 : 0x00) |
|
|||
|
(sprites[3].collideSprite ? 0x08 : 0x00) |
|
|||
|
(sprites[4].collideSprite ? 0x10 : 0x00) |
|
|||
|
(sprites[5].collideSprite ? 0x20 : 0x00) |
|
|||
|
(sprites[6].collideSprite ? 0x40 : 0x00) |
|
|||
|
(sprites[7].collideSprite ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x1F:
|
|||
|
result = (byte)(
|
|||
|
(sprites[0].collideData ? 0x01 : 0x00) |
|
|||
|
(sprites[1].collideData ? 0x02 : 0x00) |
|
|||
|
(sprites[2].collideData ? 0x04 : 0x00) |
|
|||
|
(sprites[3].collideData ? 0x08 : 0x00) |
|
|||
|
(sprites[4].collideData ? 0x10 : 0x00) |
|
|||
|
(sprites[5].collideData ? 0x20 : 0x00) |
|
|||
|
(sprites[6].collideData ? 0x40 : 0x00) |
|
|||
|
(sprites[7].collideData ? 0x80 : 0x00)
|
|||
|
);
|
|||
|
break;
|
|||
|
case 0x20:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(borderColor & 0x0F);
|
|||
|
break;
|
|||
|
case 0x21:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(backgroundColor0 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x22:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(backgroundColor1 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x23:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(backgroundColor2 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x24:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(backgroundColor3 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x25:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(spriteMulticolor0 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x26:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(spriteMulticolor1 & 0x0F);
|
|||
|
break;
|
|||
|
case 0x27:
|
|||
|
case 0x28:
|
|||
|
case 0x29:
|
|||
|
case 0x2A:
|
|||
|
case 0x2B:
|
|||
|
case 0x2C:
|
|||
|
case 0x2D:
|
|||
|
case 0x2E:
|
|||
|
result &= 0xF0;
|
|||
|
result |= (byte)(sprites[addr - 0x27].color & 0xF);
|
|||
|
break;
|
|||
|
default:
|
|||
|
// not connected
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
public void Write(ushort addr, byte val)
|
|||
|
{
|
|||
|
addr &= 0x3F;
|
|||
|
switch (addr)
|
|||
|
{
|
|||
|
case 0x19:
|
|||
|
// interrupts are cleared by writing a 1
|
|||
|
if ((val & 0x01) != 0)
|
|||
|
intRaster = false;
|
|||
|
if ((val & 0x02) != 0)
|
|||
|
intSpriteDataCollision = false;
|
|||
|
if ((val & 0x04) != 0)
|
|||
|
intSpriteCollision = false;
|
|||
|
if ((val & 0x08) != 0)
|
|||
|
intLightPen = false;
|
|||
|
break;
|
|||
|
case 0x1E:
|
|||
|
case 0x1F:
|
|||
|
// can't write to these
|
|||
|
break;
|
|||
|
case 0x2F:
|
|||
|
case 0x30:
|
|||
|
case 0x31:
|
|||
|
case 0x32:
|
|||
|
case 0x33:
|
|||
|
case 0x34:
|
|||
|
case 0x35:
|
|||
|
case 0x36:
|
|||
|
case 0x37:
|
|||
|
case 0x38:
|
|||
|
case 0x39:
|
|||
|
case 0x3A:
|
|||
|
case 0x3B:
|
|||
|
case 0x3C:
|
|||
|
case 0x3D:
|
|||
|
case 0x3E:
|
|||
|
case 0x3F:
|
|||
|
// not connected
|
|||
|
break;
|
|||
|
default:
|
|||
|
WriteRegister(addr, val);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void WriteRegister(ushort addr, byte val)
|
|||
|
{
|
|||
|
switch (addr)
|
|||
|
{
|
|||
|
case 0x00:
|
|||
|
case 0x02:
|
|||
|
case 0x04:
|
|||
|
case 0x06:
|
|||
|
case 0x08:
|
|||
|
case 0x0A:
|
|||
|
case 0x0C:
|
|||
|
case 0x0E:
|
|||
|
sprites[addr >> 1].x &= 0x100;
|
|||
|
sprites[addr >> 1].x |= val;
|
|||
|
break;
|
|||
|
case 0x01:
|
|||
|
case 0x03:
|
|||
|
case 0x05:
|
|||
|
case 0x07:
|
|||
|
case 0x09:
|
|||
|
case 0x0B:
|
|||
|
case 0x0D:
|
|||
|
case 0x0F:
|
|||
|
sprites[addr >> 1].y = val;
|
|||
|
break;
|
|||
|
case 0x10:
|
|||
|
sprites[0].x = (sprites[0].x & 0xFF) | ((uint)(val & 0x01) << 8);
|
|||
|
sprites[1].x = (sprites[1].x & 0xFF) | ((uint)(val & 0x02) << 7);
|
|||
|
sprites[2].x = (sprites[2].x & 0xFF) | ((uint)(val & 0x04) << 6);
|
|||
|
sprites[3].x = (sprites[3].x & 0xFF) | ((uint)(val & 0x08) << 5);
|
|||
|
sprites[4].x = (sprites[4].x & 0xFF) | ((uint)(val & 0x10) << 4);
|
|||
|
sprites[5].x = (sprites[5].x & 0xFF) | ((uint)(val & 0x20) << 3);
|
|||
|
sprites[6].x = (sprites[6].x & 0xFF) | ((uint)(val & 0x40) << 2);
|
|||
|
sprites[7].x = (sprites[7].x & 0xFF) | ((uint)(val & 0x80) << 1);
|
|||
|
break;
|
|||
|
case 0x11:
|
|||
|
yScroll = (val & (uint)0x07);
|
|||
|
rowSelect = ((val & 0x08) != 0);
|
|||
|
displayEnable = ((val & 0x10) != 0);
|
|||
|
bitmapMode = ((val & 0x20) != 0);
|
|||
|
extraColorMode = ((val & 0x40) != 0);
|
|||
|
rasterInterruptLine &= 0xFF;
|
|||
|
rasterInterruptLine |= (uint)(val & 0x80) << 1;
|
|||
|
break;
|
|||
|
case 0x12:
|
|||
|
rasterInterruptLine &= 0x100;
|
|||
|
rasterInterruptLine |= val;
|
|||
|
break;
|
|||
|
case 0x13:
|
|||
|
lightPenX = val;
|
|||
|
break;
|
|||
|
case 0x14:
|
|||
|
lightPenY = val;
|
|||
|
break;
|
|||
|
case 0x15:
|
|||
|
sprites[0].enable = ((val & 0x01) != 0);
|
|||
|
sprites[1].enable = ((val & 0x02) != 0);
|
|||
|
sprites[2].enable = ((val & 0x04) != 0);
|
|||
|
sprites[3].enable = ((val & 0x08) != 0);
|
|||
|
sprites[4].enable = ((val & 0x10) != 0);
|
|||
|
sprites[5].enable = ((val & 0x20) != 0);
|
|||
|
sprites[6].enable = ((val & 0x40) != 0);
|
|||
|
sprites[7].enable = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x16:
|
|||
|
xScroll = (val & (uint)0x07);
|
|||
|
columnSelect = ((val & 0x08) != 0);
|
|||
|
multicolorMode = ((val & 0x10) != 0);
|
|||
|
break;
|
|||
|
case 0x17:
|
|||
|
sprites[0].yExpand = ((val & 0x01) != 0);
|
|||
|
sprites[1].yExpand = ((val & 0x02) != 0);
|
|||
|
sprites[2].yExpand = ((val & 0x04) != 0);
|
|||
|
sprites[3].yExpand = ((val & 0x08) != 0);
|
|||
|
sprites[4].yExpand = ((val & 0x10) != 0);
|
|||
|
sprites[5].yExpand = ((val & 0x20) != 0);
|
|||
|
sprites[6].yExpand = ((val & 0x40) != 0);
|
|||
|
sprites[7].yExpand = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x18:
|
|||
|
pointerVM = (uint)((val >> 4) & 0xF);
|
|||
|
pointerCB = (uint)((val >> 1) & 0x7);
|
|||
|
break;
|
|||
|
case 0x19:
|
|||
|
intRaster = ((val & 0x01) != 0);
|
|||
|
intSpriteDataCollision = ((val & 0x02) != 0);
|
|||
|
intSpriteCollision = ((val & 0x04) != 0);
|
|||
|
intLightPen = ((val & 0x08) != 0);
|
|||
|
break;
|
|||
|
case 0x1A:
|
|||
|
enableIntRaster = ((val & 0x01) != 0);
|
|||
|
enableIntSpriteDataCollision = ((val & 0x02) != 0);
|
|||
|
enableIntSpriteCollision = ((val & 0x04) != 0);
|
|||
|
enableIntLightPen = ((val & 0x08) != 0);
|
|||
|
break;
|
|||
|
case 0x1B:
|
|||
|
sprites[0].priority = ((val & 0x01) != 0);
|
|||
|
sprites[1].priority = ((val & 0x02) != 0);
|
|||
|
sprites[2].priority = ((val & 0x04) != 0);
|
|||
|
sprites[3].priority = ((val & 0x08) != 0);
|
|||
|
sprites[4].priority = ((val & 0x10) != 0);
|
|||
|
sprites[5].priority = ((val & 0x20) != 0);
|
|||
|
sprites[6].priority = ((val & 0x40) != 0);
|
|||
|
sprites[7].priority = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x1C:
|
|||
|
sprites[0].multicolor = ((val & 0x01) != 0);
|
|||
|
sprites[1].multicolor = ((val & 0x02) != 0);
|
|||
|
sprites[2].multicolor = ((val & 0x04) != 0);
|
|||
|
sprites[3].multicolor = ((val & 0x08) != 0);
|
|||
|
sprites[4].multicolor = ((val & 0x10) != 0);
|
|||
|
sprites[5].multicolor = ((val & 0x20) != 0);
|
|||
|
sprites[6].multicolor = ((val & 0x40) != 0);
|
|||
|
sprites[7].multicolor = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x1D:
|
|||
|
sprites[0].xExpand = ((val & 0x01) != 0);
|
|||
|
sprites[1].xExpand = ((val & 0x02) != 0);
|
|||
|
sprites[2].xExpand = ((val & 0x04) != 0);
|
|||
|
sprites[3].xExpand = ((val & 0x08) != 0);
|
|||
|
sprites[4].xExpand = ((val & 0x10) != 0);
|
|||
|
sprites[5].xExpand = ((val & 0x20) != 0);
|
|||
|
sprites[6].xExpand = ((val & 0x40) != 0);
|
|||
|
sprites[7].xExpand = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x1E:
|
|||
|
sprites[0].collideSprite = ((val & 0x01) != 0);
|
|||
|
sprites[1].collideSprite = ((val & 0x02) != 0);
|
|||
|
sprites[2].collideSprite = ((val & 0x04) != 0);
|
|||
|
sprites[3].collideSprite = ((val & 0x08) != 0);
|
|||
|
sprites[4].collideSprite = ((val & 0x10) != 0);
|
|||
|
sprites[5].collideSprite = ((val & 0x20) != 0);
|
|||
|
sprites[6].collideSprite = ((val & 0x40) != 0);
|
|||
|
sprites[7].collideSprite = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x1F:
|
|||
|
sprites[0].collideData = ((val & 0x01) != 0);
|
|||
|
sprites[1].collideData = ((val & 0x02) != 0);
|
|||
|
sprites[2].collideData = ((val & 0x04) != 0);
|
|||
|
sprites[3].collideData = ((val & 0x08) != 0);
|
|||
|
sprites[4].collideData = ((val & 0x10) != 0);
|
|||
|
sprites[5].collideData = ((val & 0x20) != 0);
|
|||
|
sprites[6].collideData = ((val & 0x40) != 0);
|
|||
|
sprites[7].collideData = ((val & 0x80) != 0);
|
|||
|
break;
|
|||
|
case 0x20:
|
|||
|
borderColor = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x21:
|
|||
|
backgroundColor0 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x22:
|
|||
|
backgroundColor1 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x23:
|
|||
|
backgroundColor2 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x24:
|
|||
|
backgroundColor3 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x25:
|
|||
|
spriteMulticolor0 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x26:
|
|||
|
spriteMulticolor1 = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
case 0x27:
|
|||
|
case 0x28:
|
|||
|
case 0x29:
|
|||
|
case 0x2A:
|
|||
|
case 0x2B:
|
|||
|
case 0x2C:
|
|||
|
case 0x2D:
|
|||
|
case 0x2E:
|
|||
|
sprites[addr - 0x27].color = (uint)(val & 0xF);
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|