{ 
	Include->Util - Data Manipulation Utlities.
	
	@author(Kieron Morris <kjm@kieronmorris.me>)
}
unit util;

{$ASMMODE intel}

interface

uses
    bios_data_area, tracer;

function  INTE : boolean;
procedure CLI();
procedure STI();
procedure GPF();

function hi(b : uint8) : uint8;
function lo(b : uint8) : uint8;
function switchendian(b : uint8) : uint8;
function switchendian16(b : uint16) : uint16;
function switchendian32(b : uint32) : uint32;
function getWord(i : uint32; hi : boolean) : uint16;
function getByte(i : uint32; index : uint8) : uint8;

procedure outb(port : uint16; val : uint8);
procedure outw(port : uint16; val : uint16);
procedure outl(port : uint16; val : uint32);
function inb(port : uint16) : uint8;
function inw(port : uint16) : uint16;
function inl(port : uint16) : uint32;
procedure io_wait;

procedure memset(location : uint32; value : uint8; size : uint32);
procedure memcpy(source : uint32; dest : uint32; size : uint32);

procedure printmemory(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean);
procedure printmemoryWND(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean; WND : HWND);

procedure halt_and_catch_fire();
procedure halt_and_dont_catch_fire();
procedure BSOD(fault : pchar; info : pchar);
procedure psleep(t : uint16);
procedure sleep(seconds : uint32);

function get16bitcounter : uint16;
function get32bitcounter : uint32;
function get64bitcounter : uint64;
function getTSC : uint64;

function div6432(dividend : uint64; divisor : uint32) : uint64;

function BCDToUint8(bcd : uint8) : uint8;

function HexCharToDecimal(hex : char) : uint8;

procedure resetSystem();

function getESP : uint32;

function RolDWord(AValue : uint32; Dist : uint8) : uint32;

function RorDWord(AValue : uint32; Dist : uint8) : uint32;

function MsSinceSystemBoot : uint64;

var
    endptr : uint32; external name '__end';
    stack  : uint32; external name 'KERNEL_STACK';

implementation

uses
    console, RTC, cpu, serial, strings;

function MsSinceSystemBoot : uint64;
begin
    MsSinceSystemBoot:= div6432(getTSC, (CPUID.ClockSpeed.Hz div 1000));
end;

function div6432(dividend : uint64; divisor : uint32) : uint64;
var
    d0, d4 : uint32;
    r0, r4 : uint32;

begin
    d4:= dividend SHR 32;
    d0:= dividend AND $FFFFFFFF;
    asm
        PUSHAD
        xor edx, edx
        mov eax, d4
        div divisor
        mov r4, eax
        mov eax, d0
        div divisor
        mov r0, eax
        POPAD
    end;
    div6432:= (r0 SHL 32) OR r4;
end;

function switchendian16(b : uint16) : uint16;
begin
    switchendian16:= ((b AND $FF00) SHR 8) OR ((b AND $00FF) SHL 8);
end;

function switchendian32(b : uint32) : uint32;
begin
    switchendian32:= ((b AND $FF000000) SHR 24) OR 
                     ((b AND $00FF0000) SHR 8) OR 
                     ((b AND $0000FF00) SHL 8) OR 
                     ((b AND $000000FF) SHL 24);
end;

function getESP : uint32;
begin
    asm
        MOV getESP, ESP
    end;
end;

function HexCharToDecimal(hex : char) : uint8;
begin
    case hex of
        '0':HexCharToDecimal:=0;
        '1':HexCharToDecimal:=1;
        '2':HexCharToDecimal:=2;
        '3':HexCharToDecimal:=3;
        '4':HexCharToDecimal:=4;
        '5':HexCharToDecimal:=5;
        '6':HexCharToDecimal:=6;
        '7':HexCharToDecimal:=7;
        '8':HexCharToDecimal:=8;
        '9':HexCharToDecimal:=9;
        'a':HexCharToDecimal:=10;
        'A':HexCharToDecimal:=10;
        'b':HexCharToDecimal:=11;
        'B':HexCharToDecimal:=11;
        'c':HexCharToDecimal:=12;
        'C':HexCharToDecimal:=12;
        'd':HexCharToDecimal:=13;
        'D':HexCharToDecimal:=13;
        'e':HexCharToDecimal:=14;
        'E':HexCharToDecimal:=14;
        'f':HexCharToDecimal:=15;
        'F':HexCharToDecimal:=15;
        else HexCharToDecimal:= 0;
    end;
end;

procedure sleep1;
var
   DateTimeStart, DateTimeEnd : TDateTime;


begin
    DateTimeStart:= getDateTime;
    DateTimeEnd:= DateTimeStart;
    while DateTimeStart.seconds = DateTimeEnd.seconds do begin
        DateTimeEnd:= getDateTime;
    end;
end;

procedure sleep(seconds : uint32);
var
    i : uint32;

begin
    for i:=1 to seconds do begin
        sleep1;
    end;
end;

function INTE : boolean;
var
    flags : uint32;
begin
    asm
        PUSH EAX
        PUSHF
        POP EAX
        MOV flags, EAX
        POP EAX
    end;
    INTE:= (flags AND (1 SHL 9)) > 0;
end;

procedure io_wait;
var
    port : uint8;
    val  : uint8;
begin
    port:= $80;
    val:= 0;
    asm
        PUSH EAX
        PUSH EDX
        MOV DX, port
        MOV AL, val
        OUT DX, AL
        POP EDX
        POP EAX   
    end;  
end;

procedure printmemoryWND(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean; WND : HWND);
var
    buf : puint8;
    i   : uint32;

begin   
    buf:= puint8(source);
    for i:=0 to length-1 do begin
        if offset_row and (i = 0) then begin
            console.writehexWND(source + (i), WND);
            console.writestringWND(': ', WND);
        end; 
        console.writehexpairWND(buf[i], WND);
        if ((i+1) MOD col) = 0 then begin
            console.writestringlnWND(' ', WND);  
            if offset_row then begin
                console.writehexWND(source + (i + 1), WND);
                console.writestringWND(': ', WND);
            end;  
        end else begin
            console.writestringWND(delim, WND);
        end;
    end;
    console.writestringlnWND(' ', WND);   
end;

procedure printmemory(source : uint32; length : uint32; col : uint32; delim : PChar; offset_row : boolean);
begin
    printmemoryWND(source, length, col, delim, offset_row, 0);
end;

function hi(b : uint8) : uint8; [public, alias: 'util_hi'];
begin
     hi:= (b AND $F0) SHR 4;
end;

function lo(b : uint8) : uint8; [public, alias: 'util_lo'];
begin
     lo:= b AND $0F;
end;

procedure CLI(); assembler; nostackframe;
asm
    CLI
end;

procedure STI(); assembler; nostackframe;
asm
    STI
end;

procedure GPF(); assembler;
asm
    INT 13
end;

function switchendian(b : uint8) : uint8; [public, alias: 'util_switchendian'];
begin
     switchendian:= (lo(b) SHL 4) OR hi(b);
end;

procedure psleep(t : uint16);
var
    t1, t2 : uint16;

begin
    t1:= BDA^.Ticks;
    t2:= BDA^.Ticks;
    while t2-t1 < t do begin
        t2:= BDA^.Ticks;
        if t2 < t1 then break;
    end;
end;

procedure outl(port : uint16; val : uint32); [public, alias: 'util_outl'];
begin
     //serial.sendString('[outl]');
     //serial.sendHex(port);
     //serial.sendHex(val);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          MOV EAX, val
          OUT DX, EAX
          POP EDX
          POP EAX
     end;
     io_wait;
end;

procedure outw(port : uint16; val : uint16); [public, alias: 'util_outw'];
begin
     //serial.sendString('[outw]');
     //serial.sendHex(port);
     //serial.sendHex(val);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          MOV AX, val
          OUT DX, AX
          POP EDX
          POP EAX
     end;
     io_wait;
end;

procedure outb(port : uint16; val : uint8); [public, alias: 'util_outb'];
begin
     //serial.sendString('[outb]');
     //serial.sendHex(port);
     //serial.sendHex(val);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          MOV AL, val
          OUT DX, AL
          POP EDX
          POP EAX
     end;
     io_wait;
end;

procedure halt_and_catch_fire(); [public, alias: 'util_halt_and_catch_fire'];
begin
     asm
          cli
          hlt
     end;
end;

procedure halt_and_dont_catch_fire(); [public, alias: 'util_halt_and_dont_catch_fire'];
begin
    while true do begin
    end;
end;

function inl(port : uint16) : uint32; [public, alias: 'util_inl'];
begin
     //serial.sendString('[inl]');
     //serial.sendHex(port);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          IN EAX, DX
          MOV inl, EAX
          POP EDX
          POP EAX
     end;
     io_wait;
end;

function inw(port : uint16) : uint16; [public, alias: 'util_inw'];
begin
     //serial.sendString('[inw]');
     //serial.sendHex(port);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          IN AX, DX
          MOV inw, AX
          POP EDX
          POP EAX
     end;
     io_wait;
end;

function inb(port : uint16) : uint8; [public, alias: 'util_inb'];
begin
     //serial.sendString('[inb]');
     //serial.sendHex(port);
     asm
          PUSH EAX
          PUSH EDX
          MOV DX, port
          IN AL, DX
          MOV inb, AL
          POP EDX
          POP EAX
     end;
     io_wait;
end;

procedure memset(location : uint32; value : uint8; size : uint32);
var
    loc : puint8;
    i   : uint32;

begin
    //push_trace('util.memset');
    for i:=0 to size-1 do begin
        loc:= puint8(location + i);
        loc^:= value;
    end;
    //pop_trace;
end;

procedure memcpy(source : uint32; dest : uint32; size : uint32);
var
    src, dst : puint8;
    i : uint32;

begin
    //push_trace('util.memcpy');
    for i:=0 to size-1 do begin
        src:= puint8(source + i);
        dst:= puint8(dest + i);
        dst^:= src^;
    end;
    //pop_trace;
end;

function getWord(i : uint32; hi : boolean) : uint16;
begin
    if hi then begin
        getWord:= (i AND $FFFF0000) SHR 16;
    end else begin
        getWord:= (i AND $0000FFFF);
    end;    
end;

function getByte(i : uint32; index : uint8) : uint8;
var
    mask : uint32;

begin
    mask:= ($FF SHL (8*index));
    getByte:= (i AND mask) SHR (8*index);
end;

function BCDToUint8(bcd : uint8) : uint8;
begin
    BCDToUint8:= ((bcd SHR 4) * 10) + (bcd AND $0F);
end;

procedure resetSystem();
var
    good : uint8;

begin
    CLI;
    good:= $02;
    while (good AND $02) > 0 do good:= inb($64);
    outb($64, $FE);
    halt_and_catch_fire;
end;

function get16bitcounter : uint16;
begin
    get16bitcounter:= bios_data_area.Counters.c16;
end;

function get32bitcounter : uint32;
begin
    get32bitcounter:= bios_data_area.Counters.c32;
end;

function get64bitcounter : uint64;
begin
    get64bitcounter:= bios_data_area.Counters.c64;
end;

function getTSC : uint64;
var
    hi, lo : uint32;

begin
    asm
        PUSH EAX
        PUSH EDX
        RDTSC
        MOV hi, EDX
        MOV lo, EAX
        POP EDX
        POP EAX
    end;
    getTSC:= (hi SHL 32) OR lo;
end;

function RolDWord(AValue : uint32; Dist : uint8) : uint32;
var
    result : uint32;
    i      : uint8;    

begin
    result:= AValue;
    asm
        PUSH EAX
    end;
    for i:=0 to Dist-1 do begin
        asm
            MOV EAX, result
            ROL EAX, 1
            MOV result, EAX
        end;
    end;
    asm
        POP EAX
    end;
    RolDWord:= result;
end;

function RorDWord(AValue : uint32; Dist : uint8) : uint32;
var
    result : uint32;
    i      : uint8;    

begin
    result:= AValue;
    asm
        PUSH EAX
    end;
    for i:=0 to Dist-1 do begin
        asm
            MOV EAX, result
            ROR EAX, 1
            MOV result, EAX
        end;
    end;
    asm
        POP EAX
    end;
    RorDWord:= result;
end;

procedure BSOD(fault : pchar; info : pchar);
var
    trace : pchar;
    i     : uint32;
    z     : uint32;

begin
    console.disable_cursor;
    console.mouseEnabled(false);
    console.forceQuitAll;
    if not BSOD_ENABLE then exit;
    console.setdefaultattribute(console.combinecolors($FFFF, $F800));
    console.clear;
    console.writestringln(' ');
    console.writestringln(' ');
    console.writestring('             ');
    console.setdefaultattribute(console.combinecolors($0000, $FFFF));  
    console.writestring('                                                    ');
    console.setdefaultattribute(console.combinecolors($FFFF, $F800));
    console.writestringln(' ');
    console.writestring('             ');
    console.setdefaultattribute(console.combinecolors($0000, $FFFF)); 
    console.writestring('             ASURO DID A WHOOPSIE!  :(              ');
    console.setdefaultattribute(console.combinecolors($FFFF, $F800));
    console.writestringln(' ');
    console.writestring('             ');
    console.setdefaultattribute(console.combinecolors($0000, $FFFF)); 
    console.writestring('                                                    ');
    console.setdefaultattribute(console.combinecolors($FFFF, $F800));
    console.writestringln(' ');
    console.writestringln(' ');
    console.writestringln(' ');
    console.writestringln('    Asuro encountered an error and your computer is now a teapot.');
    console.writestringln(' ');
    console.writestringln('    Your data is almost certainly safe.');
    console.writestringln(' ');
    console.writestringln('    Details of the fault (for those boring enough to read) are as follows: ');
    console.writestringln(' ');
    console.writestring('    Fault ID:   ');
    console.writestringln(fault);
    console.writestring('    Fault Info: ');
    console.writestringln(info);
    console.writestringln(' ');
    console.writestring('    Call Stack: ');
    trace:= tracer.get_last_trace;
    if trace <> nil then begin
        console.writestring('[-0] ');
        console.writestringln(trace);
        for i:=1 to tracer.get_trace_count-1 do begin
            trace:= tracer.get_trace_N(i);
            if trace <> nil then begin
                console.writestring('                [');
                console.writestring('-');
                console.writeint(i);
                console.writestring('] ');
                console.writestringln(trace);
            end;
        end;
    end else begin
        console.writestringln('Unknown.');
    end;
    console.redrawWindows;
    halt_and_catch_fire();
end;

end.
