Contributor: ARNE DE BRUIJN

{
 EH> I was wondering if there was some way that I could convert a Pascal
 EH> exe to some sys file that the computer loads/runs when booting.

You can use this, the only problem is that the units are not initialized (the
optional code before the last end. in a unit is not executed), and so system
(WriteLn/ReadLn) and crt (WriteLn/ReadLn) don't work.

===
{ DEVCLINE.PAS: Example of a device driver in TP, Arne de Bruijn, 19960302. }
{ Released to the Public Domain. }
{ This example shows the 'commandline' of the device driver }
{ (everything after DEVICE=), and removes itself from memory. }
type
 TReqHead=record                   { Structure passed to us by DOS }
  ReqLen:byte;
  SubUnit:byte;
  Cmd:byte;
  Status:word;
  Reserved:array[0..7] of byte;
  MediaDesc:byte;
  Address:pointer;
  case byte of
   0:(DevLine:pointer; DriveName:byte);
  255:
   (Count:word; Sector:word);
 end;

var
 DevStack:array[0..4094] of byte;  { Own stack, DOS's isn't that big }
 EndOfStack:byte;
 ReqHead:^TReqHead;

procedure DevStrat; far; forward;
procedure DevIntr; far; forward;

procedure Header; assembler;
{ The trick: put the device header as the very first procedure your source, }
{ so TP places it at the start of the .exe }
asm
 dd -1                 { Next device in chain (updated by MS-DOS) }
 dw 0                  { Device attribute, now block device }
 dw offset DevStrat    { Offset of strategy routine }
 dw offset DevIntr     { Offset of interrupt routine }
 db 0,0,0,0,0,0,0,0    { For block: 1 byte no of subunits, 7 bytes reserved }
end;

procedure DevStrat; assembler;
{ Strategy routine, save ES:BX for later use }
asm
 push ax
 push ds
 mov ax,seg @Data
 mov ds,ax
 mov word ptr [ReqHead],bx
 mov word ptr [ReqHead+2],es
 pop ds
 pop ax
end;

procedure WriteStr(S:string); assembler;
{ Units not initalized, can't use some System procs (WriteLn, etc.) }
asm
 cld
 mov bx,ds
 lds si,S
 lodsb
 mov cl,al
 xor ch,ch
 jcxz @NoStr
@PrtStr:
 lodsb
 mov ah,2
 mov dl,al
 int 21h
 loop @PrtStr
@NoStr:
 mov ds,bx
end;

procedure TPIntr;
{ Called by asm proc, ReqHead contains pointer to request header, }
{ Local stack in datasegment used (now 4k) }
type
 AByte=array[0..65534] of byte;
var
 S:string[50];
 I,IntNo:byte;
begin
 if ReqHead^.Cmd=0 then            { Initialization? }
  begin
   S[0]:=#50;                      { Max len of string }
   Move(ReqHead^.DevLine^,S[1],50);{ Copy from DOS buffer }
   I:=pos(#10,S);                  { Search for #10 }
   if I>0 then                     { Found? }
    begin
     byte(S[0]):=I-1;              { That's the len for now }
     I:=pos(#13,S);                { Also a #13? }
     if I>0 then byte(S[0]):=I-1;  { That must be the length }
    end;
   WriteStr('Cmdline:"'+S+'"'#13#10);  { Display 'command line' }
   { Remove device driver from memory }
   ReqHead^.MediaDesc:=0;          { Number of components }
   ReqHead^.Address:=ptr(cseg,0);  { First free address }
   ReqHead^.Status:=$100;          { Status OK }
  end
 else
  ReqHead^.Status:=$9003;          { Status unknown cmd }
end;

procedure DevIntr; assembler;
asm
 push ax
 push bx
 push cx
 push dx
 push si
 push di
 push ds
 push es
 mov ax,seg @Data
 mov ds,ax
 mov bx,ss
 mov cx,sp
 mov ss,ax                  { Set up local stack }
 mov sp,offset EndOfStack+1
 push bx
 push cx
 call TPIntr
 pop cx                     { Restore old stack pointer }
 pop bx
 mov ss,bx
 mov sp,cx
 pop es
 pop ds
 pop di
 pop si
 pop dx
 pop cx
 pop bx
 pop ax
end;

begin
 ReqHead:=@Header; {To include it in linking (smartlinker skips it otherwise)}
 { This is executed when run from the commandline }
 WriteStr('Must be loaded from CONFIG.SYS with DEVICE=DEVCLINE.EXE'#13#10);
end.