{ ************************************************
  * Asuro
  * Unit: console
  * Description: Basic Console Output
  ************************************************
  * Author: K Morris
  * Contributors: 
  ************************************************ }

unit console;

interface

uses
     util, 
     bios_data_area,
     multiboot,
     fonts,
     tracer;

type
    TColor = ( Black   = $0,
               Blue    = $1,
               Green   = $2,
               Aqua    = $3,
               Red     = $4,
               Purple  = $5,
               Yellow  = $6,
               White   = $7,
               Gray    = $8,
               lBlue   = $9,
               lGreen  = $A,
               lAqua   = $B,
               lRed    = $C,
               lPurple = $D,
               lYellow = $E,
               lWhite  = $F );

procedure init();
procedure clear();
procedure setdefaultattribute(attribute : uint32);

procedure disable_cursor;

procedure writechar(character : char);
procedure writecharln(character : char);
procedure writecharex(character : char; attributes: uint32);
procedure writecharlnex(character : char; attributes: uint32);

procedure Output(identifier : PChar; str : PChar);
procedure Outputln(identifier : PChar; str : PChar);

procedure writestring(str: PChar);
procedure writestringln(str: PChar);
procedure writestringex(str: PChar; attributes: uint32);
procedure writestringlnex(str: PChar; attributes: uint32);

procedure writeint(i: Integer);
procedure writeintln(i: Integer);
procedure writeintex(i: Integer; attributes: uint32);
procedure writeintlnex(i: Integer; attributes: uint32);

procedure writeword(i: DWORD);
procedure writewordln(i: DWORD);
procedure writewordex(i: DWORD; attributes: uint32);
procedure writewordlnex(i: DWORD; attributes: uint32);

procedure writehexpair(b : uint8);
procedure writehex(i: DWORD);
procedure writehexln(i: DWORD);
procedure writehexex(i : DWORD; attributes: uint32);
procedure writehexlnex(i: DWORD; attributes: uint32);

procedure writebin8(b : uint8);
procedure writebin8ln(b : uint8);
procedure writebin8ex(b : uint8; attributes: uint32);
procedure writebin8lnex(b : uint8; attributes: uint32);

procedure writebin16(b : uint16);
procedure writebin16ln(b : uint16);
procedure writebin16ex(b : uint16; attributes: uint32);
procedure writebin16lnex(b : uint16; attributes: uint32);

procedure writebin32(b : uint32);
procedure writebin32ln(b : uint32);
procedure writebin32ex(b : uint32; attributes: uint32);
procedure writebin32lnex(b : uint32; attributes: uint32);

procedure backspace;

function combinecolors(Foreground, Background : uint16) : uint32;

procedure _increment_x();
procedure _increment_y();
procedure _safeincrement_y();
procedure _safeincrement_x();
procedure _newline();

procedure outputChar(c : char; x : uint8; y : uint8; fgcolor : uint16; bgcolor : uint16);
procedure outputCharToScreenSpace(c : char; x : uint32; y : uint32; fgcolor : uint16);
procedure outputCharTransparent(c : char; x : uint8; y : uint8; fgcolor : uint16);

function  getPixel(x : uint32; y : uint32) : uint16;
procedure drawPixel(x : uint32; y : uint32; color : uint16);
function  getPixel32(x : uint32; y : uint32) : uint32;
procedure drawPixel32(x : uint32; y : uint32; pixel : uint32);
function  getPixel64(x : uint32; y : uint32) : uint64;
procedure drawPixel64(x : uint32; y : uint32; pixel : uint64);

implementation

uses
    lmemorymanager;

const
    MAX_WINDOWS = 10;

type
    TConsoleProperties = record
      Default_Attribute : uint32;
    end;

    TCharacter = bitpacked record
      Character  : Char;
      attributes : uint32;
      visible    : boolean;
    end;
    PCharacter = ^TCharacter;

    TVideoMemory = Array[0..1999] of TCharacter;
    PVideoMemory = ^TVideoMemory;

    T2DVideoMemory = Array[0..63] of Array[0..159] of TCharacter;
    P2DVideoMemory = ^T2DVideoMemory;

    TCoord = record
      X : Byte;
      Y : Byte;
    end;

    TWindow = record
        visible     : boolean;
        buffer      : T2DVideoMemory;
        row_dirty   : Array[0..63] of Boolean;
        invalid     : boolean;
        WND_X       : uint32;
        WND_Y       : uint32;
        WND_W       : uint32;
        WND_H       : uint32;
    end;

    TWindows = Array[0..MAX_WINDOWS-1] of TWindow;

    TWindowManager = record
        Windows     : TWindows;
        MouseX      : uint32;
        MouseY      : uint32;
    end;

var
   Console_Properties : TConsoleProperties;
   Console_Matrix     : T2DVideoMemory;
   Console_Cursor     : TCoord;
   WindowManager      : TWindowManager;
   Ready              : Boolean = false;

procedure redrawWindows;
var
    x, y, w : uint32;
    row_dirty : Arrray[0..63] of Boolean;

begin
    for w:=0 to 63 do begin
        row_dirty[w]:= false;
    end;
    for y:=0 to 63 do begin
        for w:=0 to MAX_WINDOWS-1 do begin
            if WindowManager.Windows[w].row_dirty[y] OR row_dirty[y] then begin 
                row_dirty[y]:= true;
                if WindowManager.Windows[w].visible then begin
                    for x:=0 to 159 do begin
                        if WindowManager.Windows[w].buffer[x][y].visible then begin
                            if Console_Matrix[x][y].Character <> WindowManager.Windows[w].buffer[x][y].character then begin
                                Console_Matrix[x][y].Character:= WindowManager.Windows[w].buffer[x][y].character;
                                Console_Matrix[x][y].attributes:= WindowManager.Windows[w].buffer[x][y].attributes;
                            end;
                        end;
                    end;
                end;
            end;
        end;
    end;
end;

function getPixel(x : uint32; y : uint32) : uint16;
var
    dest : puint16;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    getPixel:= dest^;
end;

procedure drawPixel(x : uint32; y : uint32; color : uint16);
var
    dest : puint16;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    dest^:= color;
end;

function getPixel32(x : uint32; y : uint32) : uint32;
var
    dest : puint16;
    dest32 : puint32;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    dest32:= puint32(dest);
    getPixel32:= dest32[0];
end;

procedure drawPixel32(x : uint32; y : uint32; pixel : uint32);
var
    dest : puint16;
    dest32 : puint32;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    dest32:= puint32(dest);
    dest32[0]:= pixel;
end;

function getPixel64(x : uint32; y : uint32) : uint64;
var
    dest : puint16;
    dest64 : puint64;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    dest64:= puint64(dest);
    getPixel64:= dest64^;
end;

procedure drawPixel64(x : uint32; y : uint32; pixel : uint64);
var
    dest : puint16;
    dest64 : puint64;

begin
    if not ready then exit;
    dest:= puint16(multibootinfo^.framebuffer_addr);
    dest:= dest + (y * 1280) + x;
    dest64:= puint64(dest);
    dest64^:= pixel;
end;

procedure outputCharToScreenSpace(c : char; x : uint32; y : uint32; fgcolor : uint16);
var
    dest : puint16;
    dest32 : puint32;
    fgcolor32, bgcolor32 : uint32;
    mask : puint32;
    i : uint32;

begin
    if not ready then exit;
    fgcolor32:= fgcolor OR (fgcolor SHL 16);
    mask:= puint32(@Std_Mask[uint32(c) * (16 * 8)]);
    dest:= puint16(multibootinfo^.framebuffer_addr);
    //dest:= puint16(windowmanager.getBuffer(0));
    dest:= dest + (y * 1280) + x;
    dest32:= puint32(dest);
    for i:=0 to 15 do begin
        dest32[(i*640)+0]:= (dest32[(i*640)+0] AND NOT(mask[(i*4)+0])) OR (fgcolor32 AND mask[(i*4)+0]);
        dest32[(i*640)+1]:= (dest32[(i*640)+1] AND NOT(mask[(i*4)+1])) OR (fgcolor32 AND mask[(i*4)+1]);
        dest32[(i*640)+2]:= (dest32[(i*640)+2] AND NOT(mask[(i*4)+2])) OR (fgcolor32 AND mask[(i*4)+2]);
        dest32[(i*640)+3]:= (dest32[(i*640)+3] AND NOT(mask[(i*4)+3])) OR (fgcolor32 AND mask[(i*4)+3]);
    end;
    //windowmanager.redraw;
end;

procedure outputCharTransparent(c : char; x : uint8; y : uint8; fgcolor : uint16);
var
    dest : puint16;
    dest32 : puint32;
    fgcolor32, bgcolor32 : uint32;
    mask : puint32;
    i : uint32;

begin
    if not ready then exit;
    fgcolor32:= fgcolor OR (fgcolor SHL 16);
    mask:= puint32(@Std_Mask[uint32(c) * (16 * 8)]);
    dest:= puint16(multibootinfo^.framebuffer_addr);
    //dest:= puint16(windowmanager.getBuffer(0));
    dest:= dest + (y*(1280 * 16)) + (x * 8);
    dest32:= puint32(dest);
    for i:=0 to 15 do begin
        dest32[(i*640)+0]:= (dest32[(i*640)+0] AND NOT(mask[(i*4)+0])) OR (fgcolor32 AND mask[(i*4)+0]);
        dest32[(i*640)+1]:= (dest32[(i*640)+1] AND NOT(mask[(i*4)+1])) OR (fgcolor32 AND mask[(i*4)+1]);
        dest32[(i*640)+2]:= (dest32[(i*640)+2] AND NOT(mask[(i*4)+2])) OR (fgcolor32 AND mask[(i*4)+2]);
        dest32[(i*640)+3]:= (dest32[(i*640)+3] AND NOT(mask[(i*4)+3])) OR (fgcolor32 AND mask[(i*4)+3]);
    end;
    //windowmanager.redraw;
end;

procedure outputChar(c : char; x : uint8; y : uint8; fgcolor : uint16; bgcolor : uint16);
var
    dest : puint16;
    dest32 : puint32;
    fgcolor32, bgcolor32 : uint32;
    mask : puint32;
    i : uint32;

begin
    if not ready then exit;
    fgcolor32:= fgcolor OR (fgcolor SHL 16);
    bgcolor32:= bgcolor OR (bgcolor SHL 16);
    mask:= puint32(@Std_Mask[uint32(c) * (16 * 8)]);
    dest:= puint16(multibootinfo^.framebuffer_addr);
    //dest:= puint16(windowmanager.getBuffer(0));
    dest:= dest + (y*(1280 * 16)) + (x * 8);
    dest32:= puint32(dest);
    for i:=0 to 15 do begin
        dest32[(i*640)+0]:= (bgcolor32 AND NOT(mask[(i*4)+0])) OR (fgcolor32 AND mask[(i*4)+0]);
        dest32[(i*640)+1]:= (bgcolor32 AND NOT(mask[(i*4)+1])) OR (fgcolor32 AND mask[(i*4)+1]);
        dest32[(i*640)+2]:= (bgcolor32 AND NOT(mask[(i*4)+2])) OR (fgcolor32 AND mask[(i*4)+2]);
        dest32[(i*640)+3]:= (bgcolor32 AND NOT(mask[(i*4)+3])) OR (fgcolor32 AND mask[(i*4)+3]);
    end;
    //windowmanager.redraw;
end;

procedure disable_cursor;
begin
     outb($3D4, $0A);
     outb($3D5, $20);
end;

procedure init(); [public, alias: 'console_init'];
var
   fb: puint32;

Begin
     fb:= puint32(uint32(multibootinfo^.framebuffer_addr));
     kpalloc(uint32(fb));
     Console_Properties.Default_Attribute:= console.combinecolors($FFFF, $0000);
     console.clear();
     Ready:= True;
end;

procedure clear(); [public, alias: 'console_clear'];
var
   x,y: Byte;

begin
     for y:=0 to 63 do begin
         for x:=0 to 159 do begin
	        Console_Matrix[y][x].Character:= ' ';
	        Console_Matrix[y][x].Attributes:= Console_Properties.Default_Attribute;
            OutputChar(Console_Matrix[y][x].Character, x, y, Console_Matrix[y][x].Attributes SHR 16, Console_Matrix[y][x].Attributes AND $FFFF);
	    end;
     end;
     Console_Cursor.X:= 0;
     Console_Cursor.Y:= 0;
end;

procedure writebin8ex(b : uint8; attributes: uint32);
var
   Mask : PMask;
   i    : uint8;

begin
     Mask:= PMask(@b);
     for i:=0 to 7 do begin
        If Mask^[7-i] then writecharex('1', attributes) else writecharex('0', attributes);
     end;
end;

procedure writebin16ex(b : uint16; attributes: uint32);
var
   Mask : PMask;
   i,j  : uint8;

begin
     for j:=1 downto 0 do begin
        Mask:= PMask(uint32(@b) + (1 * j));
        for i:=0 to 7 do begin
                If Mask^[7-i] then writecharex('1', attributes) else writecharex('0', attributes);
        end;
     end;
end;

procedure writebin32ex(b : uint32; attributes: uint32);
var
   Mask : PMask;
   i,j  : uint8;

begin
     for j:=3 downto 0 do begin
        Mask:= PMask(uint32(@b) + (1 * j));
        for i:=0 to 7 do begin
                If Mask^[7-i] then writecharex('1', attributes) else writecharex('0', attributes);
        end;
     end;
end;

procedure writebin8(b : uint8);
begin
     writebin8ex(b, Console_Properties.Default_Attribute);
end;

procedure writebin16(b : uint16);
begin
     writebin16ex(b, Console_Properties.Default_Attribute);
end;

procedure writebin32(b : uint32);
begin
     writebin32ex(b, Console_Properties.Default_Attribute);
end;

procedure writebin8lnex(b : uint8; attributes: uint32);
begin
     writebin8ex(b, attributes);
     console._safeincrement_y();
end;

procedure writebin16lnex(b : uint16; attributes: uint32);
begin
     writebin16ex(b, attributes);
     console._safeincrement_y();
end;

procedure writebin32lnex(b : uint32; attributes: uint32);
begin
     writebin32ex(b, attributes);
     console._safeincrement_y();
end;

procedure writebin8ln(b : uint8);
begin
     writebin8lnex(b, Console_Properties.Default_Attribute);
end;

procedure writebin16ln(b : uint16);
begin
     writebin16lnex(b, Console_Properties.Default_Attribute);
end;

procedure writebin32ln(b : uint32);
begin
     writebin32lnex(b, Console_Properties.Default_Attribute);
end;

procedure setdefaultattribute(attribute: uint32); [public, alias: 'console_setdefaultattribute'];
begin
     Console_Properties.Default_Attribute:= attribute;
end;

procedure writechar(character: char); [public, alias: 'console_writechar'];
begin
     console.writecharex(character, Console_Properties.Default_Attribute);
end;

procedure writestring(str: PChar); [public, alias: 'console_writestring'];
begin
     console.writestringex(str, Console_Properties.Default_Attribute);
end;

procedure writeint(i: Integer); [public, alias: 'console_writeint'];
begin
     console.writeintex(i, Console_Properties.Default_Attribute);
end;

procedure writeword(i: DWORD); [public, alias: 'console_writeword'];
begin
     console.writewordex(i, Console_Properties.Default_Attribute);
end;

procedure writecharln(character: char); [public, alias: 'console_writecharln'];
begin
     console.writecharlnex(character, Console_Properties.Default_Attribute);
end;

procedure writestringln(str: PChar); [public, alias: 'console_writestringln'];
begin
     console.writestringlnex(str, Console_Properties.Default_Attribute);
end;

procedure writeintln(i: Integer); [public, alias: 'console_writeintln'];
begin
     console.writeintlnex(i, Console_Properties.Default_Attribute);
end;

procedure writewordln(i: DWORD); [public, alias: 'console_writewordln'];
begin
     console.writewordlnex(i, Console_Properties.Default_Attribute);
end;

procedure writecharex(character: char; attributes: uint32); [public, alias: 'console_writecharex'];
begin
     outputChar(character, Console_Cursor.X, Console_Cursor.Y, attributes SHR 16, attributes AND $FFFF);
     Console_Matrix[Console_Cursor.Y][Console_Cursor.X].Character:= character;
     Console_Matrix[Console_Cursor.Y][Console_Cursor.X].Attributes:= attributes;
     console._safeincrement_x();
end;

procedure writehexpair(b : uint8);
var
    bn : Array[0..1] of uint8;
    i  : uint8;

begin
    bn[0]:= b SHR 4;
    bn[1]:= b AND $0F;
    for i:=0 to 1 do begin
        case bn[i] of
            0:writestring('0');
            1:writestring('1');
            2:writestring('2');
            3:writestring('3');
            4:writestring('4');
            5:writestring('5');
            6:writestring('6');
            7:writestring('7');
            8:writestring('8');
            9:writestring('9');
            10:writestring('A');
            11:writestring('B');
            12:writestring('C');
            13:writestring('D');
            14:writestring('E');
            15:writestring('F');
        end;
    end;
end;

procedure writehexex(i : dword; attributes: uint32); [public, alias: 'console_writehexex'];
var
   Hex : Array[0..7] of Byte;
   Res : DWORD;
   Rem : DWORD;
   c   : Integer;
   
begin
     for c:=0 to 7 do begin
          Hex[c]:= 0;
     end;
     c:=0;
     Res:= i;
     Rem:= Res mod 16;
     while Res > 0 do begin
          Hex[c]:= Rem;
          Res:= Res div 16;
          Rem:= Res mod 16;
          c:=c+1;
     end;
     writestringex('0x', attributes);
     for c:=7 downto 0 do begin
          if Hex[c] <> 255 then begin
               case Hex[c] of
                    0:writecharex('0', attributes);
                    1:writecharex('1', attributes);
                    2:writecharex('2', attributes);
                    3:writecharex('3', attributes);
                    4:writecharex('4', attributes);
                    5:writecharex('5', attributes);
                    6:writecharex('6', attributes);
                    7:writecharex('7', attributes);
                    8:writecharex('8', attributes);
                    9:writecharex('9', attributes);
                    10:writecharex('A', attributes);
                    11:writecharex('B', attributes);
                    12:writecharex('C', attributes);
                    13:writecharex('D', attributes);
                    14:writecharex('E', attributes);
                    15:writecharex('F', attributes);
                    else writecharex('?', attributes);
               end;
          end;
     end;
end;

procedure writehex(i : dword); [public, alias: 'console_writehex'];
begin
     console.writehexex(i, Console_Properties.Default_Attribute);
end;

procedure writehexlnex(i : dword; attributes: uint32);
begin
     console.writehexex(i, attributes);
     console._safeincrement_y();
end;

procedure writehexln(i : dword);
begin
     writehexlnex(i, Console_Properties.Default_Attribute);
end;

procedure Output(identifier : PChar; str : PChar);
begin
     writestring('[');
     writestring(identifier);
     writestring('] ');
     writestring(str);
end;

procedure Outputln(identifier : PChar; str : PChar);
begin
     Output(identifier, str);
     writestringln(' ');
end;

procedure writestringex(str: PChar; attributes: uint32); [public, alias: 'console_writestringex'];
var
   i : integer;

begin
     i:= 0;
     while (str[i] <> #0) do begin
           console.writecharex(str[i], attributes);
           i:=i+1;
     end;
end;

procedure writeintex(i: Integer; attributes: uint32); [public, alias: 'console_writeintex'];
var
        buffer: array [0..11] of Char;
        str: PChar;
        digit: DWORD;
        minus: Boolean;
begin
        str := @buffer[11];
        str^ := #0;
        if (i < 0) then begin
                digit := -i;
                minus := True;
        end else begin
                digit := i;
                minus := False;
        end;
        repeat
                Dec(str);
                str^ := Char((digit mod 10) + Byte('0'));
                digit := digit div 10;
        until (digit = 0);
        if (minus) then begin
                Dec(str);
                str^ := '-';
        end;
        console.writestringex(str, attributes);
end;
 
procedure writewordex(i: DWORD; attributes: uint32); [public, alias: 'console_writedwordex'];
var
        buffer: array [0..11] of Char;
        str: PChar;
        digit: DWORD;
begin
        for digit := 0 to 10 do buffer[digit] := '0';
        str := @buffer[11];
        str^ := #0;
        digit := i;
        repeat
                Dec(str);
                str^ := Char((digit mod 10) + Byte('0'));
                digit := digit div 10;
        until (digit = 0);
        console.writestringex(@Buffer[0], attributes);
end;

procedure writecharlnex(character: char; attributes: uint32); [public, alias: 'console_writecharlnex'];
begin
     console.writecharex(character, attributes);
     console._safeincrement_y();
end;

procedure writestringlnex(str: PChar; attributes: uint32); [public, alias: 'console_writestringlnex'];
begin
     console.writestringex(str, attributes);
     console._safeincrement_y();
end;

procedure writeintlnex(i: Integer; attributes: uint32); [public, alias: 'console_writeintlnex'];
begin
     console.writeintex(i, attributes);
     console._safeincrement_y();
end;

procedure writewordlnex(i: DWORD; attributes: uint32); [public, alias: 'console_writewordlnex'];
begin
     console.writewordex(i, attributes);
     console._safeincrement_y();
end;

function combinecolors(Foreground, Background: uint16): uint32; 
begin
     combinecolors:= (uint32(Foreground) SHL 16) OR Background;
end;

procedure _update_cursor(); [public, alias: '_console_update_cursor'];
var
   pos : word;
   b   : byte;
   
begin
     pos:= (Console_Cursor.Y * 80) + Console_Cursor.X;
     outb($3D4, $0F);
     b:= pos and $00FF;
     outb($3D5, b);
     outb($3D4, $0E);
     b:= pos shr 8;
     outb($3D5, b);
end;

procedure backspace;
begin
     Dec(Console_Cursor.X);
     writechar(' ');
     Dec(Console_Cursor.X);
     _update_cursor();
end;

procedure _increment_x(); [public, alias: '_console_increment_x'];
begin
     Console_Cursor.X:= Console_Cursor.X+1;
     If Console_Cursor.X > 159 then Console_Cursor.X:= 0;
     console._update_cursor;
end;

procedure _increment_y(); [public, alias: '_console_increment_y'];
begin
     Console_Cursor.Y:= Console_Cursor.Y+1;
     If Console_Cursor.Y > 63 then begin
             console._newline();
             Console_Cursor.Y:= 63;
     end;
     console._update_cursor;
end;

procedure _safeincrement_x(); [public, alias: '_console_safeincrement_x'];
begin
     Console_Cursor.X:= Console_Cursor.X+1;
     If Console_Cursor.X > 159 then begin
        console._safeincrement_y();
     end;
     console._update_cursor;
end;

procedure _safeincrement_y(); [public, alias: '_console_safeincrement_y'];
begin
     Console_Cursor.Y:= Console_Cursor.Y+1;
     If Console_Cursor.Y > 63 then begin
             console._newline();
             Console_Cursor.Y:= 63;
     end;
     Console_Cursor.X:= 0;
     console._update_cursor;
end;

procedure _newline(); [public, alias: '_console_newline'];
var
   x, y : byte;

begin
     for x:=0 to 159 do begin
         for y:=0 to 63 do begin
             Console_Matrix[y][x]:= Console_Matrix[y+1][x];
         end;
     end;
     for x:=0 to 159 do begin
         Console_Matrix[63][x].Character:= ' ';
         Console_Matrix[63][x].Attributes:= $FFFF0000;
     end;
     for y:=0 to 63 do begin
        for x:=0 to 159 do begin
            OutputChar(Console_Matrix[y][x].Character, x, y, Console_Matrix[y][x].Attributes SHR 16, Console_Matrix[y][x].Attributes AND $FFFF);
        end;
     end;
     console._update_cursor;
end;
 
end.
