{ ************************************************ * Asuro * Unit: Terminal * Description: Interactive shell for the user ************************************************ * Author: K Morris * Contributors: ************************************************ } unit terminal; interface uses bios_data_area, console, keyboard, util, lmemorymanager, strings, tracer, asuro, serial; type PParamList = ^TParamList; TParamList = record Param : pchar; Next : PParamList; end; PHistory = ^THistory; THistory = record Command : pchar; Next : PHistory; end; TCommandBuffer = array[0..1023] of byte; TCommandMethod = procedure(params : PParamList); TCommand = record registered : boolean; hidden : boolean; command : pchar; method : TCommandMethod; description : pchar; end; var buffer : TCommandBuffer; History : PHistory; bIndex : uint32 = 0; Commands : array[0..65534] of TCommand; Working_Directory : PChar = '/'; procedure run; procedure init; procedure registerCommand(command : pchar; method : TCommandMethod; description : pchar); procedure registerCommandEx(command : pchar; method : TCommandMethod; description : pchar; hide : boolean); function getParams(buf : TCommandBuffer) : PParamList; function paramCount(params : PParamList) : uint32; function getParam(index : uint32; params : PParamList) : pchar; procedure setWorkingDirectory(str : pchar); function getWorkingDirectory : pchar; function getTerminalHWND : uint32; implementation uses RTC; var TERMINAL_HWND : HWND = 0; function getTerminalHWND : uint32; begin getTerminalHWND:= TERMINAL_HWND; end; function getWorkingDirectory : pchar; begin getWorkingDirectory:= Working_Directory; end; procedure setWorkingDirectory(str : pchar); begin if str <> nil then begin Working_Directory:= stringCopy(str); end; end; function paramCount(params : PParamList) : uint32; var current : PParamList; i : uint32; begin //push_trace('terminal.paramCount'); current:= params; i:= 0; while current^.param <> nil do begin inc(i); current:= current^.next; end; paramCount:= i-1; //pop_trace; end; function getParams(buf : TCommandBuffer) : PParamList; var start, finish : uint32; size : uint32; ptr : uint32; root : PParamList; current : PParamList; begin //push_trace('terminal.getParams'); root:= PParamList(kalloc(sizeof(TParamList))); current:= root; current^.next:= nil; current^.Param:= nil; start:= 0; finish:= 0; while buf[start] <> 0 do begin while (char(buf[finish]) <> ' ') and (buf[finish] <> 0) do begin inc(finish); end; size:= finish - start; if size > 0 then begin ptr:= uint32(@buf[start]); current^.Param:= pchar(kalloc(size+2)); memset(uint32(current^.Param), 0, size+2); memcpy(uint32(ptr), uint32(current^.Param), size); current^.next:= PParamList(kalloc(sizeof(TParamList))); current:= current^.next; current^.next:= nil; current^.Param:= nil; end; start:=finish+1; inc(finish); end; getParams:= root; //pop_trace; end; function getParam(index : uint32; params : PParamList) : pchar; var result : pchar; search : PParamList; i : uint32; begin //push_trace('terminal.getParam'); result:= nil; search:= params; for i:=0 to index do begin search:= search^.next; end; result:= search^.param; getParam:= result; //pop_trace; end; procedure freeParams(params : PParamList); var p : PParamList; next : PParamList; begin //push_trace('terminal.freeParams'); p:= params; next:= p^.next; while p^.next <> nil do begin if p^.param <> nil then kfree(void(p^.param)); kfree(void(p)); p:= next; next:= p^.next; end; //pop_trace; end; procedure testParams(params : PParamList); begin while params^.Param <> nil do begin writestringlnWND(params^.Param, TERMINAL_HWND); params:= params^.next; end; end; procedure echo(params : PParamList); var current : PParamList; begin current:= params^.next; while current^.param <> nil do begin console.writestringWND(current^.param, TERMINAL_HWND); console.writestringWND(' ', TERMINAL_HWND); current:= current^.next; end; console.writestringlnWND('', TERMINAL_HWND); end; procedure clear(params : PParamList); begin console.clearWND(TERMINAL_HWND); end; procedure version(params : PParamList); begin console.writestringWND(' Asuro Version: ', TERMINAL_HWND); console.writestringlnWND(asuro.VERSION, TERMINAL_HWND); console.writestringWND(' Compiled on: ', TERMINAL_HWND); console.writestringWND(asuro.COMPILE_DATE, TERMINAL_HWND); console.writestringWND(' ', TERMINAL_HWND); console.writestringlnWND(asuro.COMPILE_TIME, TERMINAL_HWND); console.writestringlnWND(' Compiled With: ', TERMINAL_HWND); console.writestringWND(' NASM - Version: ', TERMINAL_HWND); console.writestringlnWND(asuro.NASM_VERSION, TERMINAL_HWND); console.writestringWND(' FPC - Version: ', TERMINAL_HWND); console.writestringlnWND(asuro.FPC_VERSION, TERMINAL_HWND); console.writestringWND(' MAKE - Version: ', TERMINAL_HWND); console.writestringlnWND(asuro.MAKE_VERSION, TERMINAL_HWND); console.writestringWND(' ', TERMINAL_HWND); console.writeintWND(asuro.LINE_COUNT, TERMINAL_HWND); console.writestringWND(' lines, across ', TERMINAL_HWND); console.writeintWND(asuro.FILE_COUNT, TERMINAL_HWND); console.writestringlnWND(' files.', TERMINAL_HWND); console.writestringWND(' Baked Drivers: ', TERMINAL_HWND); console.writeintlnWND(asuro.DRIVER_COUNT, TERMINAL_HWND); end; procedure help(params : PParamList); var i, j : uint32; longestCommand : uint32; len : uint32; begin longestCommand:= 0; for i:=0 to 65534 do begin if (Commands[i].Registered) and not(Commands[i].hidden) then begin if stringSize(Commands[i].command) > longestCommand then longestCommand:= stringSize(Commands[i].command); end; end; console.writestringlnWND('Registered Commands: ', TERMINAL_HWND); for i:=0 to 65534 do begin if (Commands[i].Registered) and not(Commands[i].hidden) then begin console.writestringWND(' ', TERMINAL_HWND); console.writestringWND(Commands[i].command, TERMINAL_HWND); len:= longestCommand - StringSize(Commands[i].command); for j:=0 to len do begin writeStringWND(' ', TERMINAL_HWND); end; console.writestringWND('- ', TERMINAL_HWND); console.writestringlnWND(Commands[i].description, TERMINAL_HWND); end; end; end; procedure cockwomble(params : PParamList); var x, y : uint8; o : uint16; i : uint32; begin i:= 1; while true do begin for y:=0 to 63 do begin for x:=0 to 159 do begin o:= uint16(y * i + x * i + i + (BDA^.Ticks SHR 3)); outputChar(' ', x, y, $FFFF, o); end; end; i:= uint32(i) + 1; end; end; procedure test(params : PParamList); begin if paramCount(params) > 0 then begin console.writeintlnWND(stringToInt(getParam(0, params)), TERMINAL_HWND); end else begin console.writestringlnWND('Invalid number of params', TERMINAL_HWND); end; end; procedure printTime(Params : PParamList); var DateTime : TDateTime; begin DateTime:= getDateTime; if DateTime.Day < 10 then writeStringWND('0', TERMINAL_HWND); writeIntWND(DateTime.Day, TERMINAL_HWND); writeStringWND('/', TERMINAL_HWND); if DateTime.Month < 10 then writeStringWND('0', TERMINAL_HWND); writeIntWND(DateTime.Month, TERMINAL_HWND); writeStringWND('/', TERMINAL_HWND); writeIntWND(DateTime.Century, TERMINAL_HWND); writeIntWND(DateTime.Year, TERMINAL_HWND); writeStringWND(' ', TERMINAL_HWND); if DateTime.Hours < 10 then writeStringWND('0', TERMINAL_HWND); writeIntWND(DateTime.Hours, TERMINAL_HWND); writeStringWND(':', TERMINAL_HWND); if DateTime.Minutes < 10 then writeStringWND('0', TERMINAL_HWND); writeIntWND(DateTime.Minutes, TERMINAL_HWND); writeStringWND(':', TERMINAL_HWND); if DateTime.Seconds < 10 then writeStringWND('0', TERMINAL_HWND); writeIntlnWND(DateTime.Seconds, TERMINAL_HWND); writeStringWND('Weekday: ', TERMINAL_HWND); writeStringlnWND(WeekdayToString(DateTime.Weekday), TERMINAL_HWND); end; procedure registerCommand(command : pchar; method : TCommandMethod; description : pchar); var index : uint32; begin index:= 0; while Commands[index].registered = true do inc(index); Commands[index].registered:= true; Commands[index].Command:= command; Commands[index].hidden:= false; Commands[index].method:= method; Commands[index].description:= description; end; procedure registerCommandEx(command : pchar; method : TCommandMethod; description : pchar; hide : boolean); var index : uint32; begin index:= 0; while Commands[index].registered = true do inc(index); Commands[index].registered:= true; Commands[index].Command:= command; Commands[index].hidden:= hide; Commands[index].method:= method; Commands[index].description:= description; end; procedure process_command; var fallthrough : boolean; params : PParamList; i : uint32; next : PParamList; uppera, upperb : pchar; begin push_trace('terminal.process_command'); { Start a new line. } console.writecharlnWND(' ', TERMINAL_HWND); { Enable fallthrough/Unrecognized command } fallthrough:= true; { Get all params and check params[0] (the command) to see if it's registered } params:= getParams(buffer); if params^.param <> nil then begin uppera:= stringToUpper(params^.param); for i:=0 to 65534 do begin if Commands[i].registered then begin upperb:= stringToUpper(Commands[i].command); if stringEquals(uppera, upperb) then begin Commands[i].method(params); fallthrough:= false; end; kfree(void(upperb)); end; end; kfree(void(uppera)); end; { Free the params } freeParams(params); { Display message if command is unknown AKA fallthrough is active } if fallthrough then begin console.writestringlnWND('Unknown Command.', TERMINAL_HWND); end; { Reset the terminal ready for the next command } console.writestringWND('Asuro#', TERMINAL_HWND); console.writestringWND(Working_Directory, TERMINAL_HWND); console.writestringWND('> ', TERMINAL_HWND); bIndex:= 0; memset(uint32(@buffer[0]), 0, 1024); pop_trace; end; procedure key_event(info : TKeyInfo); begin if TERMINAL_HWND <> 0 then begin //writeintlnWND(info.key_code, TERMINAL_HWND); if (info.key_code >= 32) and (info.key_code <= 126) then begin if bIndex < 1024 then begin buffer[bIndex]:= info.key_code; inc(bIndex); console.writecharWND(char(info.key_code), TERMINAL_HWND); end; end; if info.key_code = 8 then begin //backspace if bIndex > 0 then begin console.backspaceWND(TERMINAL_HWND); dec(bIndex); buffer[bIndex]:= 0; end; end; if info.key_code = 13 then begin //return process_command; end; end; end; procedure change_dir(Params : PParamList); begin if paramCount(Params) > 0 then begin setWorkingDirectory(getParam(0, Params)); end; end; procedure SendSerial(Params : PParamList); var success : boolean; begin success:= true; success:= success AND Serial.Send(COM1, uint8('H'), 1000); success:= success AND Serial.Send(COM1, uint8('E'), 1000); success:= success AND Serial.Send(COM1, uint8('L'), 1000); success:= success AND Serial.Send(COM1, uint8('L'), 1000); success:= success AND Serial.Send(COM1, uint8('O'), 1000); success:= success AND Serial.Send(COM1, uint8('W'), 1000); success:= success AND Serial.Send(COM1, uint8('O'), 1000); success:= success AND Serial.Send(COM1, uint8('R'), 1000); success:= success AND Serial.Send(COM1, uint8('L'), 1000); success:= success AND Serial.Send(COM1, uint8('D'), 1000); success:= success AND Serial.Send(COM1, 10, 1000); success:= success AND Serial.Send(COM1, 13, 1000); if success then begin console.writestringlnWND('Send Success!', TERMINAL_HWND); end else begin console.writestringlnWND('Send Failed!', TERMINAL_HWND); end; end; procedure Reboot(Params : PParamList); begin resetSystem; end; procedure teapot(Params : PParamList); begin console.writestringlnWND('Teapot?', getTerminalHWND); end; procedure init; begin console.writestringln('TERMINAL: INIT BEGIN.'); memset(uint32(@Commands[0]), 0, 65535*sizeof(TCommand)); memset(uint32(@buffer[0]), 0, 1024); registerCommand('VERSION', @version, 'Display the running version of Asuro.'); registerCommand('CLEAR', @clear, 'Clear the Screen.'); registerCommand('HELP', @help, 'Lists all registered commands and their description.'); registerCommand('ECHO', @echo, 'Echo''s text to the terminal.'); registerCommand('TIME', @printTime, 'Print the current time.'); registerCommandEx('SERIAL', @SendSerial, 'Send ''helloworld'' through COM1.', true); registerCommand('REBOOT', @Reboot, 'Reboot the system.'); registerCommandEx('LOLWUT', @teapot, '?', true); console.writestringln('TERMINAL: INIT END.'); end; procedure OnClose(); begin TERMINAL_HWND:= 0; end; procedure run; begin if TERMINAL_HWND = 0 then begin TERMINAL_HWND:= newWindow(20, 10, 90, 30, 'ASURO TERMINAL'); console.registerEventHandler(TERMINAL_HWND, EVENT_KEY_PRESSED, void(@key_event)); console.registerEventHandler(TERMINAL_HWND, EVENT_CLOSE, void(@OnClose)); console.clearWND(TERMINAL_HWND); console.writestringWND('Asuro#', TERMINAL_HWND); console.writestringWND(Working_Directory, TERMINAL_HWND); console.writestringWND('> ', TERMINAL_HWND); end; end; end.