#define VGAP_IDX                0x3C4
#define VGAP_DATA               0x3C5
#define VGAR_MAP_MASK   0x02

U8      rev[256],               //The VGA bits are backward
        image[640 * 480 / 8];           //We need read-modify write.
                                                                //0xA0000 alias memory can't be read.
 
U0 MGInit()
{
        I64 i, j;

        MemSet(image, 0, sizeof(image));
        MemSet(rev, 0, sizeof(rev));
        for (i = 0; i < 256; i++)
                for (j = 0; j < 8; j++)
                        if (Bt(&i, j))
                                Bts(&rev[i], 7 - j);
}
 
U0 MGUpdate()
{//Copy image to VGA memory
//For better performance we could only write what's changed.
        //0xA0000 alias is slower than normal RAM.
        OutU8(VGAP_IDX, VGAR_MAP_MASK);
        OutU8(VGAP_DATA, 0xF);//All color planes at once -- Black and White
        MemCopy(text.vga_alias, image,sizeof(image)); //Alias of 0xA0000
}
 
U0 MGPlot(I64 x,I64 y)
{
        if (0 <= x < 640 && 0 <= y < 480)
                Bts(image,y * 640 + x ^ 7);
}
 
U0 MGHLine(I64 x1, I64 x2, I64 y)
{//Warning!  No clipping
//For performance, we do as many whole-bytes as possible.
        U8 *ptr;
        I64 i, w, leading, trailing, whole_bytes;

        if (x2 < x1)
                SwapI64(&x1, &x2);
        ptr = image + y * 640 / 8 + x1 >> 3;
        w = x2 - x1 + 1;
        leading = 8 - x1 & 7;
        trailing = (x2 + 1) & 7;
        if (leading + trailing > w)
                *ptr |= rev[(0xFF00 >> leading & (0x00FF << trailing) >> 8)];
        else
        {
                whole_bytes = (w - leading - trailing) >> 3;
                if (leading)
                        *ptr++ |= rev[(0xFF00 >> leading) & 0xFF];
                for (i = 0; i < whole_bytes; i++)
                        *ptr++ = 0xFF;
                if (trailing)
                        *ptr++ |= rev[(0x00FF << trailing) >> 8];
        }
}
 
U0 MGLine(I64 x1,I64 y1,I64 x2,I64 y2)
{//Warning!  No clipping
        I64 dx=x2-x1,dy=y2-y1;

        x1 <<= 32; x2 <<= 32;
        y1 <<= 32; y2 <<= 32;
        if (AbsI64(dx) > AbsI64(dy))
        {
                dy = dy << 32 / AbsI64(dx);
                dx = SignI64(dx) << 32;
                while (x1 != x2)
                {
                        MGPlot(x1.i32[1], y1.i32[1]);
                        x1 += dx; y1 += dy;
                }
        }
        else
        {
                dx = dx << 32 / AbsI64(dy);
                dy = SignI64(dy) << 32;
                while (y1 != y2)
                {
                        MGPlot(x1.i32[1], y1.i32[1]);
                        x1 += dx;
                        y1 += dy;
                }
        }
        MGPlot(x1.i32[1], y1.i32[1]);
}
 
U0 MGCircle(I64 x, I64 y, F64 r)
{
        F64 s, c, x1, y1, x2, y2;
        I64 len;

        if (r < 0)
                return;
        x1 = r;
        y1 = 0;
        c = Cos(1 / r);
        s = Sin(1 / r);
        len = 2 * r * pi;
        MGPlot(x + x1, y + y1);
        while (len-- >= 0)
        {

                //m1@a1 * m2@a2         = m1*m2@(arg1+arg2)

                //(x1+y1i)*(x2+y2i) = x1*x2+(x1*y1+x2*y2)i-y1*y2

                // meti=mCos(t)+imSin(t)

                x2 = x1;
                y2 = y1;
                x1 = c * x2 - s * y2;
                y1 = s * x2 + c * y2;
                MGPlot(x+x1,y+y1);
        }
}
 
 
U0 MiniGrLibDemo()
{
        I64 i;
        MGInit;

        for (i = 0; i < 100; i++)
                MGHLine(200 + i, 400 + i, 300 + i);

        for (i = 0; i < 500; i += 10)
                MGLine(i, 0, 0, 480 - i);

        for (i = 0; i < 300; i += 4)
                MGCircle(200, 100 + i, i);
        MGUpdate;
        Busy(1500000);
/*
We are returning graphics to normal operations under ZealOS.
It is not normal to by-pass the ZealOS graphcis routines.
The ZealOS graphics don't know VGA has changed.
This bit tells ZealOS to update whole screen.
*/
        //<CTRL-ALT-v> will flush screen VGA cache.
        LFBFlush;
}
 
MiniGrLibDemo;