c64 - more CIA timer regs, VIC border and background rendering

This commit is contained in:
saxxonpike 2012-11-03 10:15:44 +00:00
parent 81f33754e0
commit 59bb49ae20
4 changed files with 274 additions and 53 deletions

View File

@ -49,7 +49,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
Cartridge cart = new Cartridge(inputFile);
if (cart.valid)
{
mem.ApplyCartridge(cart);
//mem.ApplyCartridge(cart);
}
// initialize cpu (hard reset vector)

View File

@ -92,7 +92,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
for (int i = 0; i < cyclesPerSecond; i++)
{
if (vicSignal.Interrupt)
if (vicSignal.Interrupt || cia1.interrupt || cia2.interrupt)
{
cpu.IRQ = true;
}
@ -131,11 +131,11 @@ namespace BizHawk.Emulation.Computers.Commodore64
{
this.vic = vic;
buffer = new int[vic.rasterWidth * vic.rasterTotalLines];
buffer = new int[vic.visibleWidth * vic.visibleHeight];
top = 0;
bottom = vic.rasterTotalLines-1;
bottom = vic.visibleHeight - 1;
left = 0;
right = vic.rasterWidth-1;
right = vic.visibleWidth - 1;
}
int[] buffer;

View File

@ -7,22 +7,33 @@ namespace BizHawk.Emulation.Computers.Commodore64
{
public class Cia
{
public int alarmTime;
public bool alarmWriteEnabled;
public int cycles;
public bool flagPin;
public bool flagPinInterrupt;
public bool flagPinInterruptEnabled;
public bool[] generatePositiveEdgeOnUnderflow = new bool[2];
public bool interrupt;
public bool[] loadStartValue = new bool[2];
public bool palMode;
public byte[] regs;
public bool serialData;
public bool serialReady;
public int shiftRegisterCycles;
public bool shiftRegisterInterrupt;
public bool shiftRegisterInterruptEnabled;
public bool shiftRegisterIsOutput;
public bool[] stopOnUnderflow = new bool[2];
public int timeOfDay;
public bool timeOfDayAlarmInterrupt;
public bool timeOfDayAlarmInterruptEnabled;
public bool underflowTimerAInterrupt;
public bool underflowTimerAInterruptEnabled;
public bool underflowTimerBInterrupt;
public bool underflowTimerBInterruptEnabled;
public int[] timerConfig = new int[2];
public bool[] timerEnabled = new bool[2];
public bool[] timerInterruptEnabled = new bool[2];
public ushort[] timerLatch = new ushort[2];
public bool[] timerUnderflowMonitor = new bool[2];
public ushort[] timerValue = new ushort[2];
public bool[] underflowTimerInterrupt = new bool[2];
public bool[] underflowTimerInterruptEnabled = new bool[2];
public Func<byte> ReadPortA;
public Func<byte> ReadPortB;
@ -31,12 +42,16 @@ namespace BizHawk.Emulation.Computers.Commodore64
public Cia(Func<byte> funcReadPortA, Func<byte> funcReadPortB, Action<byte, byte> actWritePortA, Action<byte, byte> actWritePortB)
{
regs = new byte[0x10];
ReadPortA = funcReadPortA;
ReadPortB = funcReadPortB;
WritePortA = actWritePortA;
WritePortB = actWritePortB;
HardReset();
}
static public byte DummyReadPort()
{
return 0xFF;
return 0x00;
}
static public void DummyWritePort(byte val, byte direction)
@ -44,33 +59,84 @@ namespace BizHawk.Emulation.Computers.Commodore64
// do nothing
}
public void HardReset()
{
regs = new byte[0x10];
Write(0x0004, 0xFF);
Write(0x0005, 0xFF);
Write(0x0006, 0xFF);
Write(0x0007, 0xFF);
Write(0x000B, 0x01);
}
public void PerformCycle()
{
unchecked
{
cycles++;
for (int i = 0; i < 2; i++)
{
if (timerConfig[i] == 0)
TimerTick(i);
}
}
regs[0x04] = (byte)(timerValue[0] & 0xFF);
regs[0x05] = (byte)(timerValue[0] >> 8);
regs[0x06] = (byte)(timerValue[1] & 0xFF);
regs[0x07] = (byte)(timerValue[1] >> 8);
}
public void PollSerial(ref bool bit)
{
// this has the same effect as raising CNT
for (int i = 0; i < 2; i++)
{
switch (timerConfig[i])
{
case 1:
case 3:
TimerTick(i);
break;
}
}
if (shiftRegisterIsOutput)
{
bit = ((regs[0x0C] & 0x01) != 0x00);
regs[0x0C] >>= 1;
}
else
{
regs[0x0C] >>= 1;
if (bit)
regs[0x0C] |= 0x80;
}
}
public byte Read(ushort addr)
{
byte result = 0;
addr &= 0x0F;
switch (addr & 0x0F)
switch (addr)
{
case 0x00:
result = ReadPortA();
regs[addr] = result;
break;
case 0x01:
result = ReadPortB();
regs[addr] = result;
break;
case 0x0D:
result = regs[addr];
shiftRegisterInterrupt = false;
timeOfDayAlarmInterrupt = false;
underflowTimerAInterrupt = false;
underflowTimerBInterrupt = false;
underflowTimerInterrupt[0] = false;
underflowTimerInterrupt[1] = false;
interrupt = false;
UpdateInterruptReg();
break;
default:
result = regs[addr];
@ -80,47 +146,118 @@ namespace BizHawk.Emulation.Computers.Commodore64
return result;
}
public void TimerTick(int index)
{
if (timerEnabled[index])
{
unchecked
{
timerValue[index]--;
}
if (timerValue[index] == 0xFFFF)
{
if (underflowTimerInterruptEnabled[index])
{
underflowTimerInterrupt[index] = true;
interrupt = true;
}
// timer B can count on timer A's underflows
if (index == 0)
{
switch (timerConfig[1])
{
case 2:
case 3:
TimerTick(1);
break;
}
}
}
}
}
public void UpdateInterruptReg()
{
byte result;
result = (byte)(shiftRegisterInterrupt ? 0x01 : 0x00);
result |= (byte)(timeOfDayAlarmInterrupt ? 0x02 : 0x00);
result |= (byte)(underflowTimerInterrupt[0] ? 0x04 : 0x00);
result |= (byte)(underflowTimerInterrupt[1] ? 0x08 : 0x00);
result |= (byte)(flagPinInterrupt ? 0x10 : 0x00);
result |= (byte)(interrupt ? 0x80 : 0x00);
regs[0x0D] = result;
}
public void Write(ushort addr, byte val)
{
switch (addr & 0x0F)
bool allowWrite = true;
addr &= 0x0F;
switch (addr)
{
case 0x00:
WritePortA(val, regs[0x02]);
allowWrite = false;
break;
case 0x01:
WritePortB(val, regs[0x03]);
break;
case 0x02:
break;
case 0x03:
allowWrite = false;
break;
case 0x04:
timerValue[0] &= 0xFF00;
timerValue[0] |= val;
break;
case 0x05:
timerValue[0] &= 0xFF;
timerValue[0] |= (ushort)(val << 8);
break;
case 0x06:
timerValue[1] &= 0xFF00;
timerValue[1] |= val;
break;
case 0x07:
break;
case 0x08:
break;
case 0x09:
break;
case 0x0A:
break;
case 0x0B:
break;
case 0x0C:
timerValue[1] &= 0xFF;
timerValue[1] |= (ushort)(val << 8);
break;
case 0x0D:
if ((val & 0x01) != 0x00)
timerInterruptEnabled[0] = ((val & 0x80) != 0x00);
if ((val & 0x02) != 0x00)
timerInterruptEnabled[1] = ((val & 0x80) != 0x00);
if ((val & 0x04) != 0x00)
timeOfDayAlarmInterruptEnabled = ((val & 0x80) != 0x00);
if ((val & 0x08) != 0x00)
shiftRegisterInterruptEnabled = ((val & 0x80) != 0x00);
if ((val & 0x10) != 0x00)
flagPinInterruptEnabled = ((val & 0x80) != 0x00);
allowWrite = false;
break;
case 0x0E:
timerEnabled[0] = ((val & 0x01) != 0x00);
timerUnderflowMonitor[0] = ((val & 0x02) != 0x00);
generatePositiveEdgeOnUnderflow[0] = ((val & 0x04) != 0x00);
stopOnUnderflow[0] = ((val & 0x08) != 0x00);
loadStartValue[0] = ((val & 0x10) != 0x00);
timerConfig[0] = ((val & 0x20) >> 5);
shiftRegisterIsOutput = ((val & 0x40) != 0x00);
palMode = ((val & 0x80) != 0x00);
break;
case 0x0F:
timerEnabled[1] = ((val & 0x01) != 0x00);
timerUnderflowMonitor[1] = ((val & 0x02) != 0x00);
generatePositiveEdgeOnUnderflow[1] = ((val & 0x04) != 0x00);
stopOnUnderflow[1] = ((val & 0x08) != 0x00);
loadStartValue[1] = ((val & 0x10) != 0x00);
timerConfig[1] = ((val & 0x60) >> 5);
alarmWriteEnabled = ((val & 0x80) != 0x00);
break;
default:
break;
}
if (allowWrite)
regs[addr] = val;
}
}
}

View File

@ -39,17 +39,19 @@ namespace BizHawk.Emulation.Computers.Commodore64
};
// interrupts
public bool interrupt;
public bool lightPenInterrupt;
public bool interrupt = true;
public bool lightPenInterrupt = true;
public bool lightPenInterruptEnabled;
public bool rasterInterrupt;
public bool rasterInterrupt = true;
public bool rasterInterruptEnabled;
public bool spriteBackgroundInterrupt;
public bool spriteBackgroundInterrupt = true;
public bool spriteBackgroundInterruptEnabled;
public bool spriteSpriteInterrupt;
public bool spriteSpriteInterrupt = true;
public bool spriteSpriteInterruptEnabled;
// memory
public bool characterFetch;
public int characterFetchOffset;
public int characterMemoryOffset;
public int screenMemoryOffset;
@ -61,22 +63,35 @@ namespace BizHawk.Emulation.Computers.Commodore64
public int[] backgroundColor;
public bool backgroundMode;
public bool bitmapMode;
public int borderBottom;
public int borderColor;
public bool borderOn;
public int borderLeft;
public bool borderOnHorizontal;
public bool borderOnVertical;
public int borderRight;
public int borderTop;
public byte[] charBuffer;
public bool extendHeight;
public bool extendWidth;
public int horizontalScroll;
public bool multiColorMode;
public int rasterInterruptLine;
public int rasterLineLeft;
public int rasterOffset;
public int rasterOffsetX;
public int rasterOffsetY;
public int rasterTotalLines;
public int rasterWidth;
public int renderOffset;
public bool screenEnabled;
public int verticalScroll;
public int visibleBottom;
public int visibleHeight;
public int visibleLeft;
public bool visibleRenderX;
public bool visibleRenderY;
public int visibleRight;
public int visibleTop;
public int visibleWidth;
// sprites
@ -104,8 +119,20 @@ namespace BizHawk.Emulation.Computers.Commodore64
case VicIIMode.NTSC:
rasterWidth = 512;
rasterTotalLines = 263;
visibleWidth = 368;
visibleHeight = 235;
rasterLineLeft = 0x19C;
visibleLeft = 0x1E9;
visibleRight = 0x18B;
visibleTop = 0x41;
visibleBottom = 0x13;
visibleRenderX = false;
visibleRenderY = true;
visibleWidth = 418;
visibleHeight = 217;
renderOffset = 0;
borderLeft = 0x018;
borderRight = 0x158;
borderTop = 0x033;
borderBottom = 0x0FA;
break;
case VicIIMode.PAL:
break;
@ -116,6 +143,8 @@ namespace BizHawk.Emulation.Computers.Commodore64
// initialize raster
backgroundColor = new int[4];
charBuffer = new byte[40];
rasterOffsetX = rasterLineLeft;
rasterOffsetY = 0;
// initialize sprites
spriteBackgroundCollision = new bool[8];
@ -135,9 +164,24 @@ namespace BizHawk.Emulation.Computers.Commodore64
bufferSize = buffer.Length;
// initialize registers
HardReset();
}
public void HardReset()
{
// power on state
regs = new byte[0x40];
Write(0x0016, 0xC0);
Write(0x0018, 0x01);
Write(0x0019, 0x71);
Write(0x001A, 0xF0);
for (ushort i = 0x0020; i <= 0x002E; i++)
Write(i, 0xF0);
// unused registers always return FF
for (int i = 0x2F; i <= 0x3F; i++)
regs[i] = 0xFF;
UpdateRegs();
}
@ -149,12 +193,54 @@ namespace BizHawk.Emulation.Computers.Commodore64
public void PerformCycle()
{
for (int i = 0; i < 8; i++)
WritePixel(borderColor);
{
if (rasterOffsetX == visibleLeft)
visibleRenderX = true;
if (rasterOffsetX == visibleRight)
visibleRenderX = false;
if (rasterOffsetX == borderLeft)
borderOnHorizontal = false;
if (rasterOffsetX == borderRight)
borderOnHorizontal = true;
if (rasterInterruptEnabled && (rasterOffsetY == rasterInterruptLine) && (rasterOffsetX == 0))
if (borderOnVertical || borderOnHorizontal)
{
WritePixel(borderColor);
}
else
{
WritePixel(backgroundColor[0]);
}
rasterOffsetX++;
if (rasterOffsetX == rasterWidth)
rasterOffsetX = 0;
if (rasterOffsetX == rasterLineLeft)
{
rasterOffsetY++;
if (rasterOffsetY == visibleTop)
visibleRenderY = true;
if (rasterOffsetY == visibleBottom)
visibleRenderY = false;
if (rasterOffsetY == borderTop)
borderOnVertical = false;
if (rasterOffsetY == borderBottom)
borderOnVertical = true;
if (rasterOffsetY == rasterTotalLines)
{
rasterOffsetY = 0;
renderOffset = 0;
}
if (rasterInterruptEnabled && (rasterOffsetY == rasterInterruptLine))
{
rasterInterrupt = true;
}
}
}
interrupt =
(rasterInterrupt & rasterInterruptEnabled) |
@ -204,7 +290,7 @@ namespace BizHawk.Emulation.Computers.Commodore64
bool allowWrite = true;
addr &= 0x3F;
switch (addr & 0x3F)
switch (addr)
{
case 0x00:
case 0x02:
@ -390,13 +476,11 @@ namespace BizHawk.Emulation.Computers.Commodore64
private void WritePixel(int value)
{
buffer[rasterOffset] = palette[value];
rasterOffset++;
if (rasterOffset >= bufferSize)
rasterOffset = 0;
rasterOffsetX = (rasterOffset & 0x1FF);
rasterOffsetY = (rasterOffset >> 9);
if (visibleRenderX && visibleRenderY)
{
value &= 0x0F;
buffer[renderOffset++] = palette[value];
}
}
}