2012-08-08 23:05:55 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Intellivision
|
|
|
|
|
{
|
2012-09-05 04:42:49 +00:00
|
|
|
|
public sealed class STIC : IVideoProvider
|
2012-08-08 23:05:55 +00:00
|
|
|
|
{
|
2012-08-14 03:58:11 +00:00
|
|
|
|
private bool Sr1, Sr2, Sst, Fgbg = false;
|
2012-08-10 20:40:34 +00:00
|
|
|
|
private ushort[] Register = new ushort[64];
|
2012-09-04 06:26:08 +00:00
|
|
|
|
|
|
|
|
|
public int TotalExecutedCycles;
|
|
|
|
|
public int PendingCycles;
|
2012-09-05 04:42:49 +00:00
|
|
|
|
|
2012-09-06 04:51:17 +00:00
|
|
|
|
public Func<ushort, ushort> ReadMemory;
|
|
|
|
|
public Func<ushort, ushort, bool> WriteMemory;
|
|
|
|
|
|
|
|
|
|
public int[] FrameBuffer = new int[159 * 96];
|
|
|
|
|
|
|
|
|
|
public int[] GetVideoBuffer()
|
|
|
|
|
{
|
|
|
|
|
Background();
|
|
|
|
|
Mobs();
|
|
|
|
|
return FrameBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int VirtualWidth { get { return 159; } }
|
|
|
|
|
public int BufferWidth { get { return 159; } }
|
2012-09-05 04:42:49 +00:00
|
|
|
|
public int VirtualHeight { get { return 192; } }
|
|
|
|
|
public int BufferHeight { get { return 96; } }
|
|
|
|
|
public int BackgroundColor { get { return 0; } }
|
2012-08-13 08:10:15 +00:00
|
|
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
Sr1 = true;
|
|
|
|
|
Sr2 = true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-14 03:58:11 +00:00
|
|
|
|
public bool GetSr1()
|
|
|
|
|
{
|
|
|
|
|
return Sr1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool GetSr2()
|
|
|
|
|
{
|
|
|
|
|
return Sr2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetSst(bool value)
|
|
|
|
|
{
|
|
|
|
|
Sst = value;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 06:26:08 +00:00
|
|
|
|
public int GetPendingCycles()
|
|
|
|
|
{
|
|
|
|
|
return PendingCycles;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddPendingCycles(int cycles)
|
|
|
|
|
{
|
|
|
|
|
PendingCycles += cycles;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-08 23:05:55 +00:00
|
|
|
|
public ushort? ReadSTIC(ushort addr)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xF000)
|
|
|
|
|
{
|
|
|
|
|
case 0x0000:
|
|
|
|
|
if (addr <= 0x003F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x0021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = false;
|
|
|
|
|
return Register[addr];
|
2012-08-08 23:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
else if (addr <= 0x007F)
|
|
|
|
|
// TODO: OK only during VBlank Period 2.
|
2012-08-10 20:40:34 +00:00
|
|
|
|
return Register[addr - 0x0040];
|
2012-08-08 23:05:55 +00:00
|
|
|
|
break;
|
|
|
|
|
case 0x4000:
|
|
|
|
|
if (addr <= 0x403F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x4021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = false;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0x8000:
|
|
|
|
|
if (addr <= 0x803F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x8021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = false;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xC000:
|
|
|
|
|
if (addr <= 0xC03F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0xC021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = false;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool WriteSTIC(ushort addr, ushort value)
|
|
|
|
|
{
|
|
|
|
|
switch (addr & 0xF000)
|
|
|
|
|
{
|
|
|
|
|
case 0x0000:
|
|
|
|
|
if (addr <= 0x003F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x0021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = true;
|
|
|
|
|
Register[addr] = value;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (addr <= 0x007F)
|
|
|
|
|
// Read-only STIC.
|
|
|
|
|
break;
|
|
|
|
|
break;
|
|
|
|
|
case 0x4000:
|
|
|
|
|
if (addr <= 0x403F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x4021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = true;
|
|
|
|
|
Register[addr - 0x4000] = value;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0x8000:
|
|
|
|
|
if (addr <= 0x803F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0x8021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = true;
|
|
|
|
|
Register[addr & 0x003F] = value;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 0xC000:
|
|
|
|
|
if (addr <= 0xC03F)
|
|
|
|
|
{
|
|
|
|
|
// TODO: OK only during VBlank Period 1.
|
|
|
|
|
if (addr == 0xC021)
|
2012-08-10 20:40:34 +00:00
|
|
|
|
Fgbg = true;
|
|
|
|
|
Register[addr - 0xC000] = value;
|
2012-08-08 23:05:55 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-08-14 03:58:11 +00:00
|
|
|
|
|
2012-09-04 06:26:08 +00:00
|
|
|
|
public void Execute(int cycles)
|
2012-08-14 03:58:11 +00:00
|
|
|
|
{
|
2012-09-04 06:26:08 +00:00
|
|
|
|
if (PendingCycles <= 0)
|
|
|
|
|
{
|
|
|
|
|
Sr1 = !Sr1;
|
|
|
|
|
if (Sr1)
|
-Changed the amount of pending cycles to add when the STIC sets SR1.
--This number is fairly arbitrary, and I don't know why it works, but for now, it does.
--The values of INTRM don't match up exactly, but I think this is mostly a logging issue, though I still need to look into this.
-Fixed the Overflow Flag calculation.
--My original formula didn't compare the signs of the operands.
--It always needs to use the original operands, not the 2s complement one.
--As such, a result parameter has been added.
-Fixed the detection of a double swap, shift, and rotate in the related instructions. Ironically, I shifted one too many bits in my detection.
-Masked the result of left shifts and rotates to 0xFFFF so that the flags are calculated properly.
-Made RSWD (un)set the right flags.
-Enabled GSWD, MVI, SARC, CMP@, ADD, SUB@, INCR, RRC, SLR, SLL, RLC, ADDR, SUBR, SLLC, CMPR, and RSWD.
-COMR, NEGR, ADCR, SAR, ANDR, SUB, AND, XOR, and XOR@ remain disabled as I have yet to hit any test cases for them.
At this point, IntelliHawk is executing instructions indefinitely with what seems to be perfect results! I think I'm ready to hook up the screen.
2012-09-04 19:29:02 +00:00
|
|
|
|
AddPendingCycles(14394 - 3791 + 530);
|
2012-09-04 06:26:08 +00:00
|
|
|
|
else
|
|
|
|
|
AddPendingCycles(3791);
|
|
|
|
|
}
|
|
|
|
|
PendingCycles -= cycles;
|
|
|
|
|
TotalExecutedCycles += cycles;
|
2012-08-14 03:58:11 +00:00
|
|
|
|
}
|
2012-09-06 04:51:17 +00:00
|
|
|
|
|
|
|
|
|
public int ColorToRGBA(int color)
|
|
|
|
|
{
|
|
|
|
|
return 0xFFFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Background()
|
|
|
|
|
{
|
|
|
|
|
// The background is a 20x12 grid of "cards".
|
|
|
|
|
for (int card_row = 0; card_row < 12; card_row++)
|
|
|
|
|
{
|
|
|
|
|
for (int card_col = 0; card_col < 20; card_col++)
|
|
|
|
|
{
|
|
|
|
|
// The cards are stored sequentially in the System RAM.
|
|
|
|
|
ushort card = ReadMemory((ushort)(0x0200 +
|
|
|
|
|
(card_row * 20) + card_col));
|
|
|
|
|
// Parse data from the card.
|
|
|
|
|
bool gram = ((card & 0x0800) != 0);
|
|
|
|
|
int card_num = card >> 3;
|
|
|
|
|
int fg = card & 0x0004;
|
|
|
|
|
if (Fgbg)
|
|
|
|
|
{
|
|
|
|
|
int bg = ((card >> 9) & 0x0008) |
|
|
|
|
|
((card >> 11) & 0x0004) |
|
|
|
|
|
((card >> 9) & 0x0002);
|
|
|
|
|
/*
|
|
|
|
|
Only 64 of the GROM's cards can be used in FGBG
|
|
|
|
|
Mode.
|
|
|
|
|
*/
|
|
|
|
|
card_num &= 0x0020;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool advance = ((card & 0x2000) != 0);
|
|
|
|
|
if (gram)
|
|
|
|
|
{
|
|
|
|
|
// GRAM only has 64 cards.
|
|
|
|
|
card_num &= 0x0020;
|
|
|
|
|
fg |= (card >> 9) & 0x0008;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/*
|
|
|
|
|
All of the GROM's 256 cards can be used in Color
|
|
|
|
|
Stack Mode.
|
|
|
|
|
*/
|
|
|
|
|
card_num &= 0x0080;
|
|
|
|
|
}
|
|
|
|
|
// Each picture is 8x8 pixels.
|
|
|
|
|
for (int pict_row = 0; pict_row < 8; pict_row++)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
Each picture is stored sequentially in the GROM / GRAM,
|
|
|
|
|
and so are their rows.
|
|
|
|
|
*/
|
|
|
|
|
int row_mem = (card_num * 8) + (pict_row * 8);
|
|
|
|
|
byte row;
|
|
|
|
|
if (gram)
|
|
|
|
|
row = (byte)ReadMemory((ushort)(0x3800 +
|
|
|
|
|
row_mem));
|
|
|
|
|
else
|
|
|
|
|
row = (byte)ReadMemory((ushort)(0x3000 +
|
|
|
|
|
row_mem));
|
|
|
|
|
for (int pict_col = 0; pict_col < 8; pict_col++)
|
|
|
|
|
{
|
|
|
|
|
// The rightmost column does not get displayed.
|
|
|
|
|
if (card_col == 19 && pict_col == 0)
|
|
|
|
|
continue;
|
|
|
|
|
// If the pixel is on, give it the FG color.
|
|
|
|
|
if ((row & 0x1) != 0)
|
|
|
|
|
/*
|
|
|
|
|
The pixels go right as the bits get less
|
|
|
|
|
significant.
|
|
|
|
|
*/
|
|
|
|
|
FrameBuffer[((card_row * 159 * 8) + (card_col * 8) +
|
|
|
|
|
(pict_row * 159) + (7 - pict_col))] =
|
|
|
|
|
ColorToRGBA(fg);
|
|
|
|
|
row >>= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Mobs()
|
|
|
|
|
{
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
2012-08-08 23:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|