c64 - more CIA timer regs, VIC border and background rendering
This commit is contained in:
parent
81f33754e0
commit
59bb49ae20
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
regs[i] = 0xFF;
|
||||
|
||||
UpdateRegs();
|
||||
}
|
||||
|
||||
|
@ -149,13 +193,55 @@ namespace BizHawk.Emulation.Computers.Commodore64
|
|||
public void PerformCycle()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
WritePixel(borderColor);
|
||||
|
||||
if (rasterInterruptEnabled && (rasterOffsetY == rasterInterruptLine) && (rasterOffsetX == 0))
|
||||
{
|
||||
rasterInterrupt = true;
|
||||
if (rasterOffsetX == visibleLeft)
|
||||
visibleRenderX = true;
|
||||
if (rasterOffsetX == visibleRight)
|
||||
visibleRenderX = false;
|
||||
if (rasterOffsetX == borderLeft)
|
||||
borderOnHorizontal = false;
|
||||
if (rasterOffsetX == borderRight)
|
||||
borderOnHorizontal = true;
|
||||
|
||||
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) |
|
||||
(spriteSpriteInterrupt & spriteSpriteInterruptEnabled) |
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue