BizHawk/BizHawk.Emulation.Cores/Consoles/Intellivision/STIC.cs

897 lines
25 KiB
C#

using System;
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Intellivision
{
public sealed class STIC : IVideoProvider
{
public bool Sr1, Sr2, Sst, Fgbg = false;
public bool active_display, in_vb_1, in_vb_2 = false;
private ushort[] Register = new ushort[64];
public ushort ColorSP = 0x0028;
public byte[] mobs = new byte[8];
public byte[] y_mobs = new byte[8];
public int TotalExecutedCycles;
public int PendingCycles;
public Func<ushort, bool, ushort> ReadMemory;
public Func<ushort, ushort, bool, bool> WriteMemory;
private static int BORDER_OFFSET=176*8;
public int[] BGBuffer = new int[159 * 96];
public int[] FrameBuffer = new int[176 * 208];
public ushort[,] Collision = new ushort[168,210];
public void SyncState(Serializer ser)
{
ser.BeginSection("STIC");
ser.Sync("Sr1", ref Sr1);
ser.Sync("Sr2", ref Sr2);
ser.Sync("Sst", ref Sst);
ser.Sync("active_display", ref active_display);
ser.Sync("in_vb_1", ref in_vb_1);
ser.Sync("in_vb_2", ref in_vb_2);
ser.Sync("Fgbg", ref Fgbg);
ser.Sync("Toal_executed_cycles", ref TotalExecutedCycles);
ser.Sync("Pending_Cycles", ref PendingCycles);
ser.Sync("Registers", ref Register, false);
Update_Border();
ser.EndSection();
}
public int[] GetVideoBuffer()
{
return FrameBuffer;
}
// gets called when a new border color is chosen
private void Update_Border()
{
for (int i = 0; i < 176; i++)
{
for (int j = 0; j < 8; j++)
{
FrameBuffer[i + j * 176]= ColorToRGBA(Register[0x2C] & 0xF);
FrameBuffer[i + j * 176 + 176*200] = ColorToRGBA(Register[0x2C] & 0xF);
}
}
for (int j = 8; j < (208 - 8); j++)
{
for (int i = 0; i < 8; i++)
{
FrameBuffer[i + j * 176] = ColorToRGBA(Register[0x2C] & 0xF);
FrameBuffer[i + 168 + j * 176] = ColorToRGBA(Register[0x2C] & 0xF);
}
}
}
public int VirtualWidth => 277;
public int BufferWidth => 176;
public int VirtualHeight => 208;
public int BufferHeight => 208;
public int BackgroundColor => 0;
public int VsyncNumerator
{
[FeatureNotImplemented]
get
{
return NullVideo.DefaultVsyncNum;
}
}
public int VsyncDenominator
{
[FeatureNotImplemented]
get
{
return NullVideo.DefaultVsyncDen;
}
}
public void Reset()
{
Sr1 = true;
Sr2 = true;
for (int i = 0; i < 64; i++)
{
Register[i] = 0;
write_reg(i, 0, false);
}
ColorSP = 0x0028;
}
public bool GetSr1()
{
return Sr1;
}
public bool GetSr2()
{
return Sr2;
}
public void ToggleSr2()
{
Sr2 = !Sr2;
}
public void SetSst(bool value)
{
Sst = value;
}
// mask off appropriate STIC bits and write to register
private void write_reg(int reg, ushort value, bool poke)
{
if (reg < 0x8)
{
value = (ushort)((value & 0x7FF) | 0x3800);
}
else if (reg < 0x10)
{
value = (ushort)((value & 0xFFF) | 0x3000);
}
else if (reg < 0x18)
{
value = (ushort)(value & 0x3FFF);
}
else if (reg < 0x20)
{
value = (ushort)((value & 0x3FF) | 0x3C00);
}
else if (reg < 0x28)
{
value = (ushort)(0x3FFF);
}
else if (reg < 0x2D)
{
value = (ushort)((value & 0xF) | 0x3FF0);
}
else if (reg < 0x30)
{
value = (ushort)(0x3FFF);
}
else if (reg < 0x33)
{
if (reg == 0x32)
{
value = (ushort)((value & 0x3) | 0x3FFC);
}
else
{
value = (ushort)((value & 0x7) | 0x3FF8);
}
}
else if (reg < 0x40)
{
value = (ushort)(0x3FFF);
}
Register[reg] = value;
if (reg == 0x21 && !poke)
{
Fgbg = true;
}
if (reg == 0x20 && !poke)
{
active_display = true;
}
if (reg==0x2C && !poke)
{
Update_Border();
}
}
public ushort? ReadSTIC(ushort addr, bool peek)
{
switch (addr & 0xF000)
{
case 0x0000:
if (addr <= 0x003F && (in_vb_1 | !active_display))
{
if (addr == 0x0021 && !peek)
{
Fgbg = false;
}
return Register[addr];
}
else if (addr >= 0x0040 && addr <= 0x007F && (in_vb_2 | !active_display))
{
return Register[addr - 0x0040];
}
break;
case 0x4000:
if ((addr <= 0x403F) && (in_vb_1 | !active_display))
{
if (addr == 0x4021 && !peek)
{
Fgbg = false;
}
}
break;
case 0x8000:
if ((addr <= 0x803F) && (in_vb_1 | !active_display))
{
if (addr == 0x8021 && !peek)
{
Fgbg = false;
}
}
break;
case 0xC000:
if ((addr <= 0xC03F) && (in_vb_1 | !active_display))
{
if (addr == 0xC021 && !peek)
{
Fgbg = false;
}
}
break;
}
return null;
}
public bool WriteSTIC(ushort addr, ushort value, bool poke)
{
switch (addr & 0xF000)
{
case 0x0000:
if (addr <= 0x003F && (in_vb_1 | !active_display))
{
write_reg(addr, value, poke);
return true;
}
break;
case 0x4000:
if (addr <= 0x403F && (in_vb_1 | !active_display))
{
write_reg(addr-0x4000, value, poke);
return true;
}
break;
case 0x8000:
if (addr <= 0x803F && (in_vb_1 | !active_display))
{
write_reg(addr-0x8000, value, poke);
return true;
}
break;
case 0xC000:
if (addr <= 0xC03F && (in_vb_1 | !active_display))
{
write_reg(addr-0xC000, value, poke);
return true;
}
break;
}
return false;
}
private int ColorToRGBA(int color)
{
switch (color)
{
case 0:
return 0x000000;
case 1:
return 0x002DFF;
case 2:
return 0xFF3D10;
case 3:
return 0xC9CFAB;
case 4:
return 0x386B3F;
case 5:
return 0x00A756;
case 6:
return 0xFAEA50;
case 7:
return 0xFFFCFF;
case 8:
return 0xBDACC8;
case 9:
return 0x24B8FF;
case 10:
return 0xFFB41F;
case 11:
return 0x546E00;
case 12:
return 0xFF4E57;
case 13:
return 0xA496FF;
case 14:
return 0x75CC80;
case 15:
return 0xB51A58;
}
throw new ArgumentException("Specified color does not exist.");
}
public void Background(int input_row)
{
// here we will also need to apply the 'delay' register values.
// this shifts the displayed portion of the screen relative to the BG
// The background is a 20x12 grid of "cards".
int bg = 0;
for (int card_row = input_row; card_row < (input_row+1); card_row++)
{
for (int card_col = 0; card_col < 20; card_col++)
{
int buffer_offset = (card_row * 159 * 8) + (card_col * 8);
// The cards are stored sequentially in the System RAM.
ushort card = ReadMemory((ushort)(0x0200 + (card_row * 20) + card_col), false);
// Parse data from the card.
bool gram = ((card & 0x0800) != 0);
int card_num = card >> 3;
int fg = card & 0x0007;
if (Fgbg)
{
bg = ((card >> 9) & 0x0008) | ((card >> 11) & 0x0004) | ((card >> 9) & 0x0003);
// Only 64 of the GROM's cards can be used in FGBG Mode.
card_num &= 0x003F;
}
else
{
bool advance = ((card & 0x2000) != 0);
bool squares = ((card & 0x1000) != 0);
if (gram)
{
// GRAM only has 64 cards.
card_num &= 0x003F;
// The foreground color has an additional bit when not in Colored Squares mode.
if (squares)
fg |= 0x0008;
}
else
{
// All of the GROM's 256 cards can be used in Color Stack Mode.
card_num &= 0x00FF;
}
if (!gram && squares)
{
// Colored Squares Mode.
int[] colors = new int[4];
int[] square_col = new int[4];
colors[0] = fg;
colors[1] = (card >> 3) & 0x0007;
colors[2] = (card >> 6) & 0x0007;
colors[3] = ((card >> 11) & 0x0004) | ((card >> 9) & 0x0003);
for (int z = 0; z < 4; z++)
{
if (colors[z] == 7)
{
colors[z] = Register[ColorSP] & 0x000F;
square_col[z] = 0;
}
else
{
square_col[z] = 1;
}
}
for (int squares_row = 0; squares_row < 8; squares_row++)
{
for (int squares_col = 0; squares_col < 8; squares_col++)
{
// The rightmost column does not get displayed.
if (card_col == 19 && squares_col == 7)
{
continue;
}
int color;
int pixel = buffer_offset + (squares_row * 159) + squares_col;
// Determine the color of the quadrant the pixel is in.
if (squares_col < 4)
{
if (squares_row < 4)
{
color = 0;
}
else
{
color = 2;
}
}
else
{
if (squares_row < 4)
{
color = 1;
}
else
{
color = 3;
}
}
BGBuffer[pixel] = ColorToRGBA(colors[color]);
// also if the pixel is on set it in the collision matrix
// note that the collision field is attached to the lower right corner of the BG
// so we add 8 to x and 16 to y here
// also notice the extra condition attached to colored squares mode
if ((card_col * 8 + squares_col + 8) < 167 && square_col[color]==1)
{
Collision[card_col * 8 + squares_col + 8, (card_row * 8 + squares_row) * 2 + 16] = 1 << 8;
Collision[card_col * 8 + squares_col + 8, (card_row * 8 + squares_row) * 2 + 16 + 1] = 1 << 8;
}
}
}
continue;
}
else
{
if (advance)
{
// Cycle through the Color Stack registers.
ColorSP++;
if (ColorSP > 0x002B)
{
ColorSP = 0x0028;
}
}
bg = Register[ColorSP] & 0x000F;
}
}
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;
byte row;
if (gram)
{
row = (byte)ReadMemory((ushort)(0x3800 + row_mem), false);
}
else
{
row = (byte)ReadMemory((ushort)(0x3000 + row_mem), false);
}
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;
}
int pixel = buffer_offset + (pict_row * 159) + (7 - pict_col);
// If the pixel is on, give it the FG color.
if ((row & 0x1) != 0)
{
// The pixels go right as the bits get less significant.
BGBuffer[pixel] = ColorToRGBA(fg);
// also if the pixel is on set it in the collision matrix
// note that the collision field is attached to the lower right corner of the BG
// so we add 8 to x and 16 to y here
if ((card_col * 8 + (7 - pict_col) + 8) < 167)
{
Collision[card_col * 8 + (7 - pict_col) + 8, (card_row * 8 + pict_row) * 2 + 16] = 1 << 8;
Collision[card_col * 8 + (7 - pict_col) + 8, (card_row * 8 + pict_row) * 2 + 16 + 1] = 1 << 8;
}
}
else
{
BGBuffer[pixel] = ColorToRGBA(bg);
}
row >>= 1;
}
}
}
}
// now that we have the cards in BGbuffer, we can double vertical resolution to get Frame buffer
// there is a trick here in that we move the displayed area of the screen relative to the BG buffer
// this is done using the delay registers
int x_delay = Register[0x30] & 0x7;
int y_delay = Register[0x31] & 0x7;
int x_border = (Register[0x32] & 0x0001) * 8;
int y_border = ((Register[0x32] >> 1) & 0x0001) * 8;
int min_x = x_border == 0 ? x_delay : x_border;
int min_y = y_border == 0 ? y_delay : y_border;
for (int j = input_row * 8; j < (input_row * 8) + 8; j++)
{
for (int i = 0; i < 159; i++)
{
if (i >= min_x && j >= min_y)
{
FrameBuffer[(j * 2) * 176 + (i+8) + BORDER_OFFSET] = BGBuffer[(j - y_delay) * 159 + i - x_delay];
FrameBuffer[(j * 2 + 1) * 176 + (i+8) + BORDER_OFFSET] = BGBuffer[(j - y_delay) * 159 + i - x_delay];
}
else
{
FrameBuffer[(j * 2) * 176 + (i + 8) + BORDER_OFFSET] = ColorToRGBA(bg);
FrameBuffer[(j * 2 + 1) * 176 + (i + 8) + BORDER_OFFSET] = ColorToRGBA(bg);
}
}
}
}
// see for more details: http://spatula-city.org/~im14u2c/intv/jzintv-1.0-beta3/doc/programming/stic.txt
/*
The STIC provides 3 registers for controlling each MOB, and a 4th register
for reading its collision (or "interaction") status. The registers are
laid out as follows:
X Register: Address = $0000 + MOB #
13 12 11 10 9 8 7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | X |VISB|INTR| X Coordinate |
| | | |SIZE| | | (0 to 255) |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
Y Register: Address = $0008 + MOB #
13 12 11 10 9 8 7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| ?? | ?? | Y | X | Y | Y |YRES| Y Coordinate |
| | |FLIP|FLIP|SIZ4|SIZ2| | (0 to 127) |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
A Register: Address = $0010 + MOB #
13 12 11 10 9 8 7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
|PRIO| FG |GRAM| GRAM/GROM Card # (0 to 255) | FG Color |
| |bit3|GROM| (bits 9, 10 ignored for GRAM) | Bits 0-2 |
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
C Register: Address = $0018 + MOB #
13 12 11 10 9 8 7 6 5 4 3 2 1 0
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
| ?? | ?? | ?? | ?? |COLL|COLL|COLL|COLL|COLL|COLL|COLL|COLL|COLL|COLL|
| | | | |BORD| BG |MOB7|MOB6|MOB5|MOB4|MOB3|MOB2|MOB1|MOB0|
+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
*/
public void Mobs()
{
ushort x;
ushort y;
ushort attr;
byte row;
int x_delay = Register[0x30] & 0x7;
int y_delay = Register[0x31] & 0x7;
int cur_x, cur_y;
// we go from 7 to zero because visibility of lower numbered MOBs have higher priority
for (int i = 7; i >= 0 ; i--)
{
x = Register[i];
y = Register[i + 8];
attr = Register[i + 16];
byte card = (byte)(attr >> 3);
bool gram = attr.Bit(11);
byte loc_color = (byte)(attr & 7);
bool color_3 = attr.Bit(12);
if (color_3 && gram)
{
loc_color += 8;
}
bool priority = attr.Bit(13);
byte loc_x = (byte)(x & 0xFF);
byte loc_y = (byte)(y & 0x7F);
bool vis = x.Bit(9);
bool x_flip = y.Bit(10);
bool y_flip = y.Bit(11);
ushort yres = y.Bit(7) ? (ushort)2 : (ushort)1;
ushort ysiz2 = y.Bit(8) ? (ushort)2 : (ushort)1;
ushort ysiz4 = y.Bit(9) ? (ushort)4 : (ushort)1;
bool intr = x.Bit(8);
ushort x_size = x.Bit(10) ? (ushort)2 : (ushort)1;
ushort y_size = (ushort)(ysiz2 * ysiz4);
// setting yres implicitly uses an even card first
if (yres > 1)
card &= 0xFE;
// in GRAM mode only take the first 6 bits of the card number
if (gram)
card &= 0x3F;
//pull the data from the card into the mobs array
for (int j=0;j<8;j++)
{
if (gram)
{
row = (byte)ReadMemory((ushort)(0x3800 + 8 * card + j), false);
}
else
{
row = (byte)ReadMemory((ushort)(0x3000 + 8 * card + j), false);
}
mobs[j] = row;
}
// assign the y_mob, used to double vertical resolution
if (yres > 1)
{
for (int j = 0; j < 8; j++)
{
if (gram)
{
row = (byte)ReadMemory((ushort)(0x3800 + 8 * (card + 1) + j), false);
}
else
{
row = (byte)ReadMemory((ushort)(0x3000 + 8 * (card + 1) + j), false);
}
y_mobs[j] = row;
}
}
// flip mobs accordingly
if (x_flip)
{
for (int j = 0; j < 8; j++)
{
byte temp_0 = (byte)((mobs[j] & 1) << 7);
byte temp_1 = (byte)((mobs[j] & 2) << 5);
byte temp_2 = (byte)((mobs[j] & 4) << 3);
byte temp_3 = (byte)((mobs[j] & 8) << 1);
byte temp_4 = (byte)((mobs[j] & 16) >> 1);
byte temp_5 = (byte)((mobs[j] & 32) >> 3);
byte temp_6 = (byte)((mobs[j] & 64) >> 5);
byte temp_7 = (byte)((mobs[j] & 128) >> 7);
mobs[j] = (byte)(temp_0 + temp_1 + temp_2 + temp_3 + temp_4 + temp_5 + temp_6 + temp_7);
}
if (yres > 1)
{
for (int j = 0; j < 8; j++)
{
byte temp_0 = (byte)((y_mobs[j] & 1) << 7);
byte temp_1 = (byte)((y_mobs[j] & 2) << 5);
byte temp_2 = (byte)((y_mobs[j] & 4) << 3);
byte temp_3 = (byte)((y_mobs[j] & 8) << 1);
byte temp_4 = (byte)((y_mobs[j] & 16) >> 1);
byte temp_5 = (byte)((y_mobs[j] & 32) >> 3);
byte temp_6 = (byte)((y_mobs[j] & 64) >> 5);
byte temp_7 = (byte)((y_mobs[j] & 128) >> 7);
y_mobs[j] = (byte)(temp_0 + temp_1 + temp_2 + temp_3 + temp_4 + temp_5 + temp_6 + temp_7);
}
}
}
if (y_flip)
{
byte temp_0 = mobs[0];
byte temp_1 = mobs[1];
byte temp_2 = mobs[2];
byte temp_3 = mobs[3];
byte temp_4 = mobs[4];
byte temp_5 = mobs[5];
byte temp_6 = mobs[6];
byte temp_7 = mobs[7];
if (yres == 1)
{
mobs[0] = mobs[7];
mobs[1] = mobs[6];
mobs[2] = mobs[5];
mobs[3] = mobs[4];
mobs[4] = temp_3;
mobs[5] = temp_2;
mobs[6] = temp_1;
mobs[7] = temp_0;
}
else
{
mobs[0] = y_mobs[7];
mobs[1] = y_mobs[6];
mobs[2] = y_mobs[5];
mobs[3] = y_mobs[4];
mobs[4] = y_mobs[3];
mobs[5] = y_mobs[2];
mobs[6] = y_mobs[1];
mobs[7] = y_mobs[0];
y_mobs[0] = temp_7;
y_mobs[1] = temp_6;
y_mobs[2] = temp_5;
y_mobs[3] = temp_4;
y_mobs[4] = temp_3;
y_mobs[5] = temp_2;
y_mobs[6] = temp_1;
y_mobs[7] = temp_0;
}
}
// draw the mob and check for collision
for (int j = 0; j < 8; j++)
{
for (int k = 0; k < 8; k++)
{
bool pixel = mobs[j].Bit(7 - k);
cur_x = loc_x + k * x_size;
for (int m = 0; m < y_size; m++)
{
cur_y = j * y_size + m;
if ((cur_x) < (167 - x_delay) && (loc_y * 2 + cur_y) < (208 - y_delay * 2) && pixel && vis && cur_x >= (8 - x_delay) && (loc_y * 2 + cur_y) >= (16 - y_delay * 2))
{
if (!(priority && (Collision[cur_x, loc_y * 2 + cur_y]&0x100)>0))
FrameBuffer[(loc_y * 2 + cur_y - (16 - y_delay * 2)) * 176 + cur_x + x_delay + BORDER_OFFSET] = ColorToRGBA(loc_color);
}
// a MOB does not need to be visible for it to be interracting
// special case: a mob with x position 0 is counted as off
if (intr && pixel && cur_x <= 167 && (loc_y * 2 + cur_y) < 210 && loc_x != 0)
{
Collision[cur_x, loc_y * 2 + cur_y] |= (ushort)(1 << i);
}
if (x_size == 2)
{
if ((cur_x + 1) < (167 - x_delay) && (loc_y * 2 + cur_y) < (208 - y_delay * 2) && pixel && vis && (cur_x + 1) >= (8 - x_delay) && (loc_y * 2 + cur_y) >= (16 - y_delay * 2))
{
if (!(priority && (Collision[cur_x + 1, loc_y * 2 + cur_y] & 0x100) > 0))
FrameBuffer[(loc_y * 2 + cur_y - (16 - y_delay * 2)) * 176 + cur_x + x_delay + 1 + BORDER_OFFSET] = ColorToRGBA(loc_color);
}
//a MOB does not need to be visible for it to be interracting
//special case: a mob with x position 0 is counted as off
if (intr && pixel && (cur_x + 1) <= 167 && (loc_y * 2 + cur_y) < 210 && loc_x != 0)
{
Collision[cur_x + 1, loc_y * 2 + cur_y] |= (ushort)(1 << i);
}
}
}
}
}
// Now repeat the process if the mob is double sized
if (yres>1)
{
for (int j = 0; j < 8; j++)
{
for (int k = 0; k < 8; k++)
{
bool pixel = y_mobs[j].Bit(7 - k);
cur_x = loc_x + k * x_size;
for (int m = 0; m < y_size; m++)
{
cur_y = j * y_size + m;
if ((cur_x) < (167 - x_delay) && ((loc_y + 4 * y_size) * 2 + cur_y) < (208 - y_delay * 2) && pixel && vis && cur_x >= (8 - x_delay) && ((loc_y + 4 * y_size) * 2 + cur_y) >= (16 - y_delay * 2))
{
if (!(priority && (Collision[cur_x, (loc_y + 4 * y_size) * 2 + cur_y] & 0x100) > 0))
FrameBuffer[((loc_y + 4 * y_size) * 2 + cur_y - (16 - y_delay * 2)) * 176 + cur_x + x_delay + BORDER_OFFSET] = ColorToRGBA(loc_color);
}
// a MOB does not need to be visible for it to be interracting
// special case: a mob with x position 0 is counted as off
if (intr && pixel && cur_x <= 167 && ((loc_y + 4 * y_size) * 2 + cur_y) < 210 && loc_x != 0)
{
Collision[cur_x, (loc_y + 4 * y_size) * 2 + cur_y] |= (ushort)(1 << i);
}
if (x_size == 2)
{
if ((cur_x + 1) < (167 - x_delay) && ((loc_y + 4 * y_size) * 2 + cur_y) < (208 - y_delay * 2) && pixel && vis && (cur_x + 1) >= (8 - x_delay) && ((loc_y + 4 * y_size) * 2 + cur_y) >= (16 - y_delay * 2))
{
if (!(priority && (Collision[cur_x + 1, (loc_y + 4 * y_size) * 2 + cur_y] & 0x100) > 0))
FrameBuffer[((loc_y + 4 * y_size) * 2 + cur_y - (16 - y_delay * 2)) * 176 + cur_x + x_delay + 1 + BORDER_OFFSET] = ColorToRGBA(loc_color);
}
// a MOB does not need to be visible for it to be interracting
// special case: a mob with x position 0 is counted as off
if (intr && pixel && (cur_x + 1) <= 167 && ((loc_y + 4 * y_size) * 2 + cur_y) < 210 && loc_x != 0)
{
Collision[cur_x + 1, (loc_y + 4 * y_size) * 2 + cur_y] |= (ushort)(1 << i);
}
}
}
}
}
}
}
// by now we have collision information for all 8 mobs and the BG
// so we can store data in the collision registers here
int x_border = Register[0x32].Bit(0) ? 15 - x_delay : 7 - x_delay;
int y_border = Register[0x32].Bit(1) ? 30 - y_delay * 2 : 14 - y_delay * 2;
int x_border_2 = Register[0x32].Bit(0) ? 8 : 0;
int y_border_2 = Register[0x32].Bit(1) ? 16 : 0;
for (int i = 0; i < 168; i++)
{
for (int j = 0; j < 210; j++)
{
// while we are here we can set collision detection bits for the border region
if (i == x_border || i == (167 - x_delay))
{
Collision[i, j] |= (1 << 9);
}
if (j == y_border || j == y_border + 1 || j == (208 - y_delay * 2) || j == (208 - y_delay * 2 + 1))
{
Collision[i, j] |= (1 << 9);
}
// and also make sure the border region is all the border color
if ((i-x_delay)>=0 && (i-x_delay) <= 159 && (j-y_delay*2) >= 0 && (j-y_delay*2) < 192)
{
if ((i-x_delay) < x_border_2)
FrameBuffer[(j - y_delay*2) * 176 + ((i + 8) - x_delay) + BORDER_OFFSET] = ColorToRGBA(Register[0x2C] & 0xF);
if ((j - y_delay*2) < y_border_2)
FrameBuffer[(j - y_delay*2) * 176 + ((i + 8) - x_delay) + BORDER_OFFSET] = ColorToRGBA(Register[0x2C] & 0xF);
if ((i-x_delay)==159)
{
FrameBuffer[(j - y_delay*2) * 176 + ((i + 8) - x_delay) + BORDER_OFFSET] = ColorToRGBA(Register[0x2C] & 0xF);
}
}
// the extra condition here is to ignore only border/BG collsion bit set
if (Collision[i, j] != 0 && Collision[i,j] != (1 << 9) && Collision[i,j] != (1 << 8))
{
for (int k = 0; k < 8; k++)
{
for (int m = 0; m < 10; m++)
{
if (k != m) // mobs never self interact
{
Register[k + 24] |= (ushort)((Collision[i, j].Bit(k) && Collision[i, j].Bit(m)) ? 1 << m : 0);
}
}
}
}
// after we check for collision, we can clear that value for the next frame.
Collision[i, j] = 0;
}
}
}
// end of Mobs function, we now have collision and graphics data for the mobs
}
}