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 ReadMemory; public Func 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 public 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 { get { return 277; } } public int BufferWidth { get { return 176; } } public int VirtualHeight { get { return 208; } } public int BufferHeight { get { return 208; } } public int BackgroundColor { get { return 0; } } public void Reset() { Sr1 = true; Sr2 = true; for (int i=0;i<64;i++) { write_reg(i, 0, false); } } 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; } public 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 + 8) - (8 - 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 + 8) + 1 - (8 - 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 + 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 + 8) - (8 - 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 + 8) + 1 - (8 - 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 + 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 } }