void DrawTerrainColumn(int index, Vector3 corner)
            {
                // local tile map (we are rendering tile A)
                //  TileF   TileD   TileC
                //  TileE   TileA   TileB
                //          TileG   TileH

                // get this tile
                Tile TileA = Memory[Row.This + ((index + 0) & 15)];

                // check if object must be rendered with this tile
                if (TileA.HoldsObject)
                {
                    Row.ObjectCount++;
                }

                // check if tile is empty
                if (TileA.IsEmpty)
                {
                    return; // nothing to draw
                }
                // get base tile color
                int colorIndex = TileA.ColorIndex;

                // locate tile corners
                //       d +-------+ c
                //        /       /|
                //       /       / |          x2 = x1 + Tile.WIDTH_X
                // y1-- +-------+  |          y2 = y1 + Tile.DEFAULT_HEIGHT_Y
                //      |a     b| g+ --z2     z2 = z1 + Tile.DEPTH_Z
                //      |       | /
                //      |e     f|/
                // y2-- +-------+ --z1
                //      |       |
                //      x1      x2
                float x1 = corner.X;
                float x2 = x1 + Tile.WIDTH_X;
                float z1 = corner.Z;
                float z2 = corner.Z + Tile.DEPTH_Z;

                // determine tile height offset
                Tile        TileB  = Memory[Row.This + ((index + 1) & 15)];
                Tile        TileC  = Memory[Row.Next + ((index + 1) & 15)];
                Tile        TileD  = Memory[Row.Next + ((index + 0) & 15)];
                TILE_HEIGHT height = GetTileHeight(TileA, TileB, TileC, TileD);

                // draw left or right side of cube
                if (x1 > 0)
                {
                    // left side of the cube
                    TILE_HEIGHT side;

                    if (index == 1)
                    {
                        // leftmost tile
                        side.b = side.c = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y;
                    }
                    else
                    {
                        Tile TileE = Memory[Row.This + ((index - 1) & 15)];
                        Tile TileF = Memory[Row.Next + ((index - 1) & 15)];
                        side = GetTileHeight(TileE, TileA, TileD, TileF);
                    }

                    // draw if tile to the left is lower than current tile
                    if (height.a < side.b || height.d < side.c)
                    {
                        Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD);
                        Vertices[1] = Vector3.Transform(new Vector3(x1, height.d, z2), Parent.D3DTS_WORLD);
                        Vertices[2] = Vector3.Transform(new Vector3(x1, side.c, z2), Parent.D3DTS_WORLD);
                        Vertices[3] = Vector3.Transform(new Vector3(x1, side.b, z1), Parent.D3DTS_WORLD);
                        DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 1));
                    }
                }
                else if (x2 < 0)
                {
                    // right side of the cube
                    TILE_HEIGHT side;

                    if (index == 15)
                    {
                        // rightmost tile
                        side.a = side.d = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y;
                    }
                    else
                    {
                        side = GetTileHeight(TileB, TileB, TileC, TileC);
                    }

                    // draw if tile to the right is lower than current tile
                    if (height.b < side.a || height.c < side.d)
                    {
                        Vertices[0] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD);
                        Vertices[1] = Vector3.Transform(new Vector3(x2, side.a, z1), Parent.D3DTS_WORLD);
                        Vertices[2] = Vector3.Transform(new Vector3(x2, side.d, z2), Parent.D3DTS_WORLD);
                        Vertices[3] = Vector3.Transform(new Vector3(x2, height.c, z2), Parent.D3DTS_WORLD);
                        DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 1));
                    }
                }

                // if tile is flat
                if (TileA.IsFlat)
                {
                    // Draw the front of the cube if:
                    //   - This is the last row to render
                    // OR
                    //   - Tile in front is lower than current tile (or empty)
                    TILE_HEIGHT side;
                    if (Row.Count == 0)
                    {
                        side.c = side.d = -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y;
                    }
                    else
                    {
                        Tile TileG = Memory[Row.Prev + ((index + 0) & 15)];
                        Tile TileH = Memory[Row.Prev + ((index + 10) & 15)];
                        side = GetTileHeight(TileG, TileH, TileB, TileA);
                    }
                    if (height.a < side.d || height.b < side.c)
                    {
                        Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD);
                        Vertices[1] = Vector3.Transform(new Vector3(x1, side.d, z1), Parent.D3DTS_WORLD);
                        Vertices[2] = Vector3.Transform(new Vector3(x2, side.c, z1), Parent.D3DTS_WORLD);
                        Vertices[3] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD);
                        DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex - 2));
                    }
                }

                // draw the top of the cube
                Vertices[0] = Vector3.Transform(new Vector3(x1, height.a, z1), Parent.D3DTS_WORLD);
                Vertices[1] = Vector3.Transform(new Vector3(x2, height.b, z1), Parent.D3DTS_WORLD);
                Vertices[2] = Vector3.Transform(new Vector3(x2, height.c, z2), Parent.D3DTS_WORLD);
                Vertices[3] = Vector3.Transform(new Vector3(x1, height.d, z2), Parent.D3DTS_WORLD);
                DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex));

                // special case for rendering solid sloped tiles
                // this is done for maximum compatibility with real machine
                if (!TileA.IsFlat && renderMode == Mathbox.RenderMode.Polygon)
                {
                    DisplayListManager.AddPrimitive(Mathbox.RenderMode.Vector, Vertices, 4, Parent.GetColor(colorIndex));
                }

                // draw the bottom of the cube
                Vertices[0] = Vector3.Transform(new Vector3(x1, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z1), Parent.D3DTS_WORLD);
                Vertices[1] = Vector3.Transform(new Vector3(x1, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z2), Parent.D3DTS_WORLD);
                Vertices[2] = Vector3.Transform(new Vector3(x2, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z2), Parent.D3DTS_WORLD);
                Vertices[3] = Vector3.Transform(new Vector3(x2, -Parent.ViewPosition.Y + Tile.DEFAULT_HEIGHT_Y, z1), Parent.D3DTS_WORLD);
                DisplayListManager.AddPrimitive(renderMode, Vertices, 4, Parent.GetColor(colorIndex));
            }
            /// <summary>
            /// Parses the list of objects to render
            /// </summary>
            /// <param name="address">base address of object list</param>
            public void ParseObjectList(UInt16 address)
            {
                // address+0  Object position.X
                // address+1  Object position.Y
                // address+2  Object position.Z
                // address+3  Object Instruction
                // address+4  Address of view matrix
                //    address+5  Address of rotation matrix (only if specified by instruction)
                //    address+6  Address of surface list (only if specified by instruction)
                // address +5 or + 7 first child object
#if false
                Debug.WriteLine($"{Memory[address+0].HexString()} {Memory[address + 1].HexString()} {Memory[address + 2].HexString()} {Memory[address + 3].HexString()} {Memory[address + 4].HexString()} {Memory[address + 5].HexString()} {Memory[address + 6].HexString()}");
#endif

                for (; ;)
                {
                    // check for end of list
                    if (address == 0 || address >= 0x8000)
                    {
                        return;
                    }

                    // Control word encoding
                    // 0x8000 = stop flag
                    // 0x4000 = don't load camera matrix
                    // 0x1000 = ?
                    // 0x0800 = don't load base address or rotation matrix
                    // 0x0400 = x/y/z value is relative offset, not absolute position
                    Mathbox.ObjectInstruction opcode = Memory[address + 3];

                    // load camera matrix
                    if (!opcode.SkipObjectRotation)
                    {
                        Parent.LoadViewMatrix(Memory[address + 4]);
                    }

                    // Get new primitive list address and rotation matrix if they exist
                    int childAddr = address + 5;
                    if (!opcode.UsePreviousObjectPointsAndFaces)
                    {
                        PrimitiveListAddress = Memory[address + 6];
                        if (PrimitiveListAddress >= 0x8000)
                        {
                            return;
                        }
                        Parent.LoadRotationMatrix(Memory[address + 5]);
                        childAddr += 2;
                    }

                    // Don't render invalid objects
                    if (PrimitiveListAddress >= 0x8000)
                    {
                        return;
                    }

                    // Determine position of object
                    Vector3 pt = Parent.GetVectorAt(address);
                    if (opcode.ObjectPositionIsRelative)
                    {
                        // relative position
                        Parent.WorldPosition += Vector3.Transform(pt, Parent.ViewRotation);
                    }
                    else
                    {
                        // absolute position
                        pt -= Parent.ViewPosition;
                        Parent.WorldPosition = Vector3.Transform(pt, Parent.ViewRotation);
                    }

                    Parent.SetWorldMatrix(ref Parent.WorldPosition, ref Parent.WorldRotation);

#if WIDESCREEN_STARS
                    if (PrimitiveListAddress >= 0x4AE8 && PrimitiveListAddress <= 0x4B44)
                    {
                        for (int n = 0; n < Stars.Length; n++)
                        {
                            Vertices[n] = Vector3.Transform(Stars[n], Parent.D3DTS_WORLD);
                        }

                        DisplayListManager.AddPrimitive(Mathbox.RenderMode.Dot, Vertices, Stars.Length, Parent.GetColor(7));
                        return;
                    }
#endif

                    // parese the surfaces in this object
                    ParsePrimitiveList(PrimitiveListAddress);

                    // parse all child objects
                    for (; ;)
                    {
                        UInt16 child = Memory[childAddr++];
                        if (child == 0 || child >= 0x8000)
                        {
                            return;
                        }
                        if (child == 0x0002)
                        {
                            address += 8;
                            break;
                        }

                        ParseObjectList(child);
                    }
                }
            }