I wrote a binder for my AtomVM. Here is the binder code in Delphi:
program bindercl;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
fvm, fsource, fexe: string;
fname, fpath: string;
procedure bind;
var
fin, fout: file of Byte;
bread: Byte;
vmsize, size: Longint;
i: integer;
faname, fsname, foutname: string;
tag: string;
begin
//get the atomvm path
faname := fvm;
//get the source path
fsname := fsource;
//get the output file name
foutname := fexe;
//copy over the runtime
if (Length(faname) > 0) and (Length(foutname) > 0 ) and (Length(fsname) > 0 ) then begin
try
//open the vm
AssignFile(fin, faname);
Reset(fin);
//get the file size
size := FileSize(fin);
vmsize := size;
//create the out file.
AssignFile(fout, foutname);
Rewrite(fout);
writeln('Writing VM...');
while not eof(fin) do begin
Read(fin, bread);
Write(fout, bread);
end;
CloseFile(fin);
//open the source
AssignFile(fin, fsname);
Reset(fin);
//get the file size
size := FileSize(fin);
//write out the source
writeln('Writing Source...');
while not eof(fin) do begin
Read(fin, bread);
Write(fout, bread);
end;
//Set the tag
writeln('Writing Tag...');
tag := 'atabind' + IntToStr(vmsize);
//pad the tag to 20 spaces
tag := tag + StringOfChar(' ', 20 - Length(tag));
//write out the tag
for i := 1 to 20 do begin
bread := Byte(tag[i]);
Write(fout, bread);
end;
Beep;
writeln('Done!');
finally
CloseFile(fin);
CloseFile(fout);
end;
end;
end;
begin
//get the command line parameters
if ParamCount < 3 then begin
Beep;
writeln('ERROR: Not enough paramaters.');
writeln('Useage: bindercl [path]/atomvm.exe [path]/source.ata [path]/outfile.exe');
end
else begin
//get the comamnd line
fvm := ParamStr(1);
fsource := ParamStr(2);
fexe := ParamStr(3);
//validate the path and file names.
if not FileExists(fvm) then begin
Beep;
writeln('Cannot find file ',fvm);
end
else begin
if not FileExists(fsource) then begin
Beep;
writeln('Cannot find file ',fvm);
end
else begin
bind;
end;
end;
end;
end.
What I do here is copy over the VM exe from a stub file (an exe that is marked as a dat file). I then append the source file to the end of the exe file. I then write a 20 byte tag that starts with "atabind" the magic word, and the size of the VM file. You need to size of the VM file when reading the source file.
Here is where I read in the source from the bind.
//Get the exe name.
exename := ParamStr(0);
//Check for bind tag at end of file. Should be 'atabind' + vm filesize + padding (20 chars total).
try
AssignFile(fin, exename);
FileMode := fmOpenRead;
Reset(fin);
size := FileSize(fin);
//calc the tag start position
offset := size - 20;
Seek(fin, offset);
//get the tag info
while not eof(fin) do begin
Read(fin, bread);
tag := tag + Char(bread);
end;
//Do we have a tag?
if Pos('atabind', tag) > 0 then begin
//get the offset of the source code
tmp := RightStr(tag, 13);
tmp := Trim(tmp);
offset := StrToInt(tmp);
//Make sure we have a valid offset
if offset > 0 then begin
Reset(fin);
//Seek to source code position
Seek(fin, offset);
//load source code. Lines delimited by linefeed (10)
for i := offset to size - 20 do begin
Read(fin, bread);
lin := lin + Char(bread);
//look for line delimiter
if bread = 10 then begin
//trim any spaces
wlin := Trim(lin);
//parse the current line of source
ok := LoadLine(wlin);
if ok = false then begin
break;
end;
//clear input line
lin := '';
end;
end;
end;
end
Here I first look at the last 20 bytes of the file. If it has the magic word, atabind then I load the offset of the source file in the exe which is the size of the VM I stored earlier. I then seek to that position in the exe file, and start reading in the source lines until I reach the last 20 bytes of the file. Each line is delimited by chr(10) so I know I have a full line when I read a chr(10). I store each line in a array and then process the source file the same as if it was loaded via the command line.