/asm89.pas
program Asm89;
uses Compiler, Omf, Util, SymTable;

const VERSION: string = '0.1';

var infile: string;
    outfile: string;
    cob: CompiledObject;

procedure WriteObjectFile(path: string; obj: CompiledObject);
var rec: OmfRecord;
    f: OmfFile;
    se: PSymbolEntry;
    v: Word;
begin
    OpenOmf(path, f);

    NewRecord(rec, THEADR);
    if obj.name = '' then
        RecAddName(rec, path)
    else
        RecAddName(rec, obj.name);
    WriteRecord(f, rec);

    NewRecord(rec, COMENT);
    RecAddCommentStr(rec, 0, 0, 'asm89 v' + VERSION);
    WriteRecord(f, rec);

    NewRecord(rec, LNAMES);
    RecAddByte(rec, 0); { The first name is empty, apparently used for
                          the overlay index. }
    RecAddName(rec, 'DATA');
    RecAddName(rec, 'DGROUP');
    RecAddName(rec, obj.segment_name);
    WriteRecord(f, rec);

    NewRecord(rec, SEGDEF);
    { alignment, combine, segment length 5, name index, class index, overlay index }
    RecAddRelocatableSegment(rec, AlignParagraph, CombinePublic, obj.buffer.data_len, 4, 2, 1);
    WriteRecord(f, rec);

    NewRecord(rec, GRPDEF);
    { group name index, segment definition }
    RecAddGroupDef(rec, 3, 1);
    WriteRecord(f, rec);

    NewRecord(rec, LEDATA);
    RecAddEnumeratedData(rec, 1, 0, obj.buffer.data^, obj.buffer.data_len);
    WriteRecord(f, rec);

    NewRecord(rec, PUBDEF);
    RecAddPubDef(rec, 1, 1);
    se := obj.public_symbols.head;
    while se <> nil do begin
        if NOT FindSymbol(obj.symbols, se^.name, v) then begin
            Writeln('Public symbol `', se^.name, '` not defined');
            Halt(1);
        end;
        { group index, segment index, name, offset, type }
        RecAddPubDefName(rec, se^.name, v, 0);
        se := se^.next;
    end;
    WriteRecord(f, rec);

    NewRecord(rec, MODEND);
    RecAddByte(rec, 0);
    WriteRecord(f, rec);

    Writeln('''', outfile, ''' written, ', FileSize(f.f), ' bytes');
    CloseOmf(f);
end;

procedure PrintUsage;
begin
    Writeln('Usage: asm89 <input.asm> [output.obj]');
    Writeln('');
    Writeln('The output object file is optional. If it is not specified, the output file');
    Writeln('Is the input file with the .obj extension');
end;

procedure FatalError(const message: string; const print_usage: boolean);
begin
    Writeln(message);
    Writeln('');
    if print_usage then PrintUsage;
    Halt(1);
end;

begin
    Writeln('asm89 compiler v', VERSION);
    Writeln('');
    if ParamCount < 1 then
        FatalError('You must specify an input file', true);
    infile := ParamStr(1);
    if ParamCount < 2 then begin
        outfile := ChangeExtension(ParamStr(1), 'obj');
    end
    else
        outfile := ParamStr(2);

    Writeln('Assembling ''', infile, ''' to ''', outfile, '''');

    Compile(infile, cob);
    Writeln('Assembly complete');
    WriteObjectFile(outfile, cob);
end.