/* REXX */

/*
-----------------------------------------------------------------
  unpacker (v0.1) = public domain : free for any use

  AUTHOR: rugxulo _AT_ gmail

  TESTED: Regina 3.7, BRexx 2.1.9, r4 4.00, ooREXX 4.1.3

  BUG:    Can't use literal '*'+'/' pair in embedded data files.
-----------------------------------------------------------------
*/

/* --- UNPACKER BEGINS --- */

if arg() \= 0 then parse arg onlyfile . ; else onlyfile=''
parse source . . srcfile . ; lineno=0 ; writeln=0

bar = '===' ; prefix='/*' bar ; postfix = bar '*/'
headpost=' begins' postfix ; footpost=' ends' postfix
headlen=length(headpost) ; footlen=length(footpost)

if lines(srcfile)=1 then do while lines(srcfile) \= 0
  call grab
end
else do lines(srcfile)
  call grab
end

exit

grab:
  line=linein(srcfile) ; lineno=lineno+1
  if pos(headpost,line) \= 0 then do
    parse var line ' ' (bar) ' ' outfile (headpost) .
    if onlyfile='' then say outfile
    writeln=1
  end
  else if pos(footpost,line) \= 0 then writeln=0
  if pos(headpost,line)=0 & pos(footpost,line)=0 & writeln then ,
    if onlyfile='' | onlyfile=outfile then ,
      call lineout outfile, line
return

/* --- UNPACKER ENDS --- */

/*
------------------------------------------------------------
*** DATA BEGINS DATA BEGINS DATA BEGINS DATA BEGINS ***

/* these data files = public domain : free for any use */
------------------------------------------------------------

/* === lines.m begins === */
MODULE lines; (* public domain, nenies proprajho, free for any use *)
IMPORT S := Str;

CONST max* = 150;
TYPE
  line* = RECORD
    X*,data*:ARRAY max OF CHAR;
    at*:INTEGER
  END;

PROCEDURE (VAR l:line) getc*(b:INTEGER):CHAR;
BEGIN RETURN l.data[b]
END getc;

PROCEDURE (VAR l:line) putc*(b:INTEGER; c:CHAR);
BEGIN l.data[b] := c
END putc;

PROCEDURE (VAR l:line) len*():INTEGER;
BEGIN RETURN S.Length(l.data)
END len;

PROCEDURE (VAR l:line) find*(s:ARRAY OF CHAR):INTEGER;
BEGIN l.at := S.Pos(s,l.data,0); RETURN l.at
END find;

PROCEDURE (VAR l:line) ins*(s:ARRAY OF CHAR; n:INTEGER);
BEGIN S.Insert(s,n,l.data)
END ins;

PROCEDURE (VAR l:line) del*(ofs,num:INTEGER);
BEGIN S.Delete(l.data,ofs,num)
END del;

PROCEDURE (VAR l:line) found*(s:ARRAY OF CHAR):BOOLEAN;
BEGIN RETURN l.find(s) >= 0
END found;

PROCEDURE (VAR l:line) extract*(i,j:INTEGER);
BEGIN S.Extract(l.data,i,j,l.X)
END extract;

PROCEDURE (VAR l:line) foundstart*(s:ARRAY OF CHAR):BOOLEAN;
BEGIN
  IF l.getc(0) = s[0] THEN l.extract(0,SHORT(LEN(s))-1); RETURN l.X = s
  ELSE RETURN FALSE
  END
END foundstart;

PROCEDURE (VAR l:line) foundend*(s:ARRAY OF CHAR):BOOLEAN;
VAR L1,L2:INTEGER;
BEGIN
  L1 := l.len(); L2 := SHORT(LEN(s));
  IF L1 >= L2 THEN
    l.extract(L1-L2+1,L2-1);
    RETURN l.X = s
  ELSE
    RETURN FALSE
  END
END foundend;

PROCEDURE (VAR l:line) delend*(s:ARRAY OF CHAR);
BEGIN IF l.foundend(s) THEN l.del(l.len()-SHORT(LEN(s))+1,SHORT(LEN(s))-1) END
END delend;

PROCEDURE (VAR l:line) finddel*(s:ARRAY OF CHAR);
BEGIN IF l.found(s) THEN l.del(l.at,SHORT(LEN(s))-1) END
END finddel;

PROCEDURE (VAR l:line) sub*(no,yes:ARRAY OF CHAR);
BEGIN IF l.found(no) THEN l.del(l.at,SHORT(LEN(no))-1); l.ins(yes,l.at) END
END sub;
  
END lines.
/* === lines.m ends === */

/* === text.m begins === */
MODULE text; (* public domain, nenies proprajho, free for any use *)
IMPORT lines,F := Myfiles,O := Out;

CONST linemax* = lines.max;

TYPE
  asmfile* = RECORD (lines.line)
    filein,fileout,incfile:F.File
  END;

VAR inc:BOOLEAN;

PROCEDURE (VAR a:asmfile) readline*():BOOLEAN;
BEGIN
  IF F.Eof(a.filein) THEN
    RETURN FALSE
  ELSE
    IF F.readline(a.filein,a.data) # 0 THEN ; END;
    RETURN TRUE
  END
END readline;

PROCEDURE (VAR a:asmfile) writeline*;
BEGIN F.WriteString(a.fileout,a.data); F.WriteLn(a.fileout)
END writeline;

PROCEDURE (VAR a:asmfile) writeout*(s:ARRAY OF CHAR);
BEGIN F.WriteString(a.fileout,s); F.WriteLn(a.fileout)
END writeout;

PROCEDURE (VAR a:asmfile) writeinc*(s:ARRAY OF CHAR);
BEGIN F.WriteString(a.incfile,s); F.WriteLn(a.incfile)
END writeinc;

PROCEDURE init*(VAR a:asmfile; name:ARRAY OF CHAR);
BEGIN
  a.filein := F.Open("INVADERS.ASM","r");
  IF a.filein=NIL THEN O.String("Not found!"); O.Ln; HALT(1) END;

  a.fileout := F.Open(name,"w")
END init;

PROCEDURE incinit*(VAR a:asmfile; incname:ARRAY OF CHAR);
BEGIN a.incfile := F.Open(incname,"w"); inc := TRUE
END incinit;

PROCEDURE done*(VAR a:asmfile);
BEGIN
  F.Close(a.fileout); F.Close(a.filein);
  IF inc THEN F.Close(a.incfile) END
END done;

BEGIN inc := FALSE
END text.
/* === text.m ends === */

/* === ctype.m begins === */
MODULE ctype; (* public domain, nenies proprajho, free for any use *)

PROCEDURE isdigit*(k:CHAR):BOOLEAN;
BEGIN RETURN (k >= "0") & (k <= "9")
END isdigit;

PROCEDURE isupper*(k:CHAR):BOOLEAN;
BEGIN RETURN (k >= "A") & (k <= "Z")
END isupper;

PROCEDURE islower*(k:CHAR):BOOLEAN;
BEGIN RETURN (k >= "a") & (k <= "z")
END islower;

PROCEDURE isalpha*(k:CHAR):BOOLEAN;
BEGIN RETURN ((k >= "a") & (k <= "z")) OR ((k >= "A") & (k <= "Z"))
END isalpha;

PROCEDURE isalnum*(k:CHAR):BOOLEAN;
BEGIN RETURN ((k >= "0") & (k <= "9")) OR
  (((k >= "a") & (k <= "z")) OR ((k >= "A") & (k <= "Z")))
END isalnum;

END ctype.
/* === ctype.m ends === */

/* === fixes.m begins === */
MODULE fixes; (* public domain, nenies proprajho, free for any use *)
IMPORT text,C := ctype;

TYPE
  asmfixer* = RECORD (text.asmfile) END;

PROCEDURE (VAR a:asmfixer) fixproc*;
BEGIN
  IF a.found(" PROC ") & a.found(" ") THEN
    a.putc(a.at,":"); a.del(a.at+1,a.len()-a.at)
  END
END fixproc;

PROCEDURE (VAR a:asmfixer) incdec(qualify:BOOLEAN);
VAR p,p2:INTEGER;
BEGIN
  IF a.found("INC ") OR a.found("DEC ") THEN
    p := a.at; INC(p,4);
    WHILE a.getc(p)=" " DO INC(p) END;
    IF a.getc(p) # "[" THEN
      p2 := p;

      REPEAT
        INC(p2)
      UNTIL (p2=a.len()) OR (a.getc(p2)=" ") OR
            ~(C.isalnum(a.getc(p2)));

      IF a.getc(p2)=" " THEN DEC(p2) END;

      IF (p2-p > 2) THEN
        a.extract(p,p2-p);
        a.ins("]",a.len()); a.ins("[",p);
        IF qualify THEN
          a.ins("s_",p); a.ins(a.X,p+2)
        END
      END
    END
  END
END incdec;

PROCEDURE (VAR a:asmfixer) fixop1(qualify:BOOLEAN);
VAR p,p2:INTEGER;
BEGIN
  p2 := a.find(",");
  IF (p2 >= 0) & (a.getc(p2-1) # "]") THEN
    DEC(p2); p := p2;

    REPEAT
      DEC(p)
    UNTIL (a.getc(p)=" ") OR ~((C.isalnum(a.getc(p))) OR (a.getc(p)="_"));

    INC(p);

    IF (p2-p+1 > 2) & (a.getc(p) # "[") & C.isupper(a.getc(p)) THEN
      a.extract(p,p2-p+1);
      a.ins("]",a.find(",")); a.ins("[",p);
      IF qualify THEN
        a.ins("s_",p); a.ins(a.X,p+2)
      END
    END
  END
END fixop1;

PROCEDURE (VAR a:asmfixer) fixop2(qualify:BOOLEAN);
VAR p,p2:INTEGER;
BEGIN
  p := a.find(","); p2 := a.find(";");
  IF (p >= 0) & ((p2 < 0) OR (p2 > p)) & (a.getc(p) # "[") THEN
    INC(p); p2 := p;

    WHILE (p2 < a.len()-1) & (a.getc(p2) # " ") & (C.isalnum(a.getc(p2))
          OR (a.getc(p2)="_")) DO
            INC(p2)
    END;

    IF a.getc(p2)=" " THEN DEC(p2) END;

    IF (p2 > p) & (p2-p > 2) & C.isupper(a.getc(p)) THEN
      a.extract(p,p2-p+1);
      a.ins("]",p2+1); a.ins("[",p);
      IF qualify THEN
        a.ins("s_",p); a.ins(a.X,p+2)
      END
    END
  END
END fixop2;

PROCEDURE (VAR a:asmfixer) fixop*(qualify:BOOLEAN);
  PROCEDURE fixplusbx;
  VAR p:INTEGER;
  BEGIN
    p := a.find("["); a.extract(p+1,a.find("+")-p-1);
    a.ins(a.X,p); a.ins("s_",p)
  END fixplusbx;
BEGIN
  IF qualify & a.found("+BX") THEN fixplusbx END;
  a.fixop1(qualify); a.fixop2(qualify); a.incdec(qualify)
END fixop;

PROCEDURE (VAR a:asmfixer) unbrakdig*;
BEGIN
  IF a.found("]") &
    (a.at >= 4) & (a.getc(a.at-2)="[") & C.isdigit(a.getc(a.at-1)) THEN
      a.putc(a.at-2,"_"); a.del(a.at,1)
  END
END unbrakdig;

PROCEDURE (VAR a:asmfixer) brakdig*(p:INTEGER):BOOLEAN;
BEGIN
  RETURN (p >= 2) & (a.getc(p-2)="_") & C.isdigit(a.getc(p-1))
END brakdig;

PROCEDURE (VAR a:asmfixer) fixbrakdig*(left,right:BOOLEAN);
BEGIN
  IF left & a.found("[") & a.brakdig(a.at) THEN a.del(a.at-2,2) END;
  IF right & a.found("]") & a.brakdig(a.at) THEN a.putc(a.at-2,"+") END
END fixbrakdig;

END fixes.
/* === fixes.m ends === */

/* === jmps.m begins === */
MODULE jmps; (* public domain, nenies proprajho, free for any use *)
IMPORT text;
CONST
  noshort="#$%&,.134578>DGJMOPQRSTUVWXYZ][_^\`abcdefy"; len=LEN(noshort)-1;
VAR
  jumpnum:INTEGER;
  shorten*:PROCEDURE (VAR t:text.asmfile; jmpmsg:ARRAY OF CHAR);
  jmpstr:ARRAY LEN(noshort) OF CHAR; jumps:ARRAY ORD("~")+1 OF BOOLEAN;

(*
PROCEDURE oldway(VAR t:text.asmfile; jmpmsg:ARRAY OF CHAR);
BEGIN
  INC(jumpnum);
  t.X := " "; t.X[0] := CHR(jumpnum+ORD(" "));
  IF Str.Pos(t.X,noshort,0) = -1 THEN
    t.ins(jmpmsg,t.at+3)
  END
END oldway;
*)

PROCEDURE newway(VAR t:text.asmfile; jmpmsg:ARRAY OF CHAR);
BEGIN
  INC(jumpnum);
  IF jumps[jumpnum] THEN t.ins(jmpmsg,t.at+3) END
END newway;

PROCEDURE initnew;
VAR n:INTEGER;
BEGIN
  jmpstr := noshort;
  FOR n := 0 TO ORD("~") DO jumps[n] := TRUE END;
  FOR n := 0 TO len-1 DO jumps[ORD(jmpstr[n])-ORD(" ")] := FALSE END
END initnew;

BEGIN jumpnum := 0; initnew; shorten := newway
END jmps.
/* === jmps.m ends === */

/* === cseg.m begins === */
MODULE cseg; (* public domain, nenies proprajho, free for any use *)
IMPORT text;
VAR override:BOOLEAN;
CONST beginning="RemoveNewInt9"; ending="CLC";

PROCEDURE insert*(VAR t:text.asmfile);
BEGIN
  IF t.foundstart(beginning) THEN override := TRUE END;
  IF override & t.found("MOV ") &
    (t.found(",Word") OR t.found("StoreAX") OR
     t.foundend(",0") OR t.foundend(",1")) THEN
       t.writeout(" CS:")
  END;
  IF t.foundend(ending) THEN override := FALSE END
END insert;

PROCEDURE insertbracket*(VAR t:text.asmfile);
BEGIN
  IF t.foundstart(beginning) THEN override := TRUE END;
  IF override & ~t.found("[0") THEN t.sub("[","[cs:") END;
  IF t.foundend(ending) THEN override := FALSE END;
END insertbracket;

BEGIN override := FALSE
END cseg.
/* === cseg.m ends === */

/* === nasm.m begins === */
MODULE nasm; (* public domain, nenies proprajho, free for any use *)
IMPORT text,fixes,cseg,S := Str;

TYPE fix* = RECORD (fixes.asmfixer) END;

PROCEDURE (VAR n:fix) adjust;
VAR p,p2:INTEGER;

  PROCEDURE writeinc;
  VAR s:ARRAY text.linemax OF CHAR;
  BEGIN
    p := n.find(" "); p2 := p; WHILE n.getc(p2)=" " DO INC(p2) END;
    s := "%define s_"; n.extract(0,p);
    S.Append(n.X,s); S.Append(" ?",s); s[S.Length(s)-1] := n.getc(p2+1);
    n.writeinc(s)
  END writeinc;

BEGIN
  IF (n.len()=0) OR (n.getc(0)=";") OR n.found("LEA ") THEN
    n.writeline; RETURN
  END;

  n.sub(" DD "," DW 0,");
  IF (n.found(" DB ") OR n.found(" DW ")) & (n.getc(0) # " ") THEN
    writeinc;
    n.writeline; RETURN
  END;

  IF n.found("CODE_SEG") OR n.found("END") THEN RETURN END;

  n.finddel("[0]");

  IF ~n.found(",O") (* ,O[fF][^ ]* *) THEN
    n.sub("ES:[","[ES:"); n.finddel("40:"); n.finddel("Word Ptr ");
    n.fixproc; n.unbrakdig; n.fixop(TRUE); n.fixbrakdig(TRUE,TRUE);

    cseg.insertbracket(n)
  END;

  n.writeline
END adjust;

PROCEDURE (VAR n:fix) init*;
CONST incname="inv-nasm.inc";
BEGIN
  text.init(n,"inv-nasm.asm"); text.incinit(n,incname);
  n.writeout("%idefine offset"); n.writeout("%define LEA MOV");
  n.writeout("%define B byte");  n.writeout("%define W word");
  n.X := "%include '"; S.Append(incname,n.X); S.Append("'",n.X);
  n.writeout(n.X);
  WHILE n.readline() DO n.adjust END
END init;

PROCEDURE (VAR n:fix) done*;
BEGIN text.done(n)
END done;

END nasm.
/* === nasm.m ends === */

/* === fasm.m begins === */
MODULE fasm; (* public domain, nenies proprajho, free for any use *)
IMPORT text,fixes,cseg;

TYPE fix* = RECORD (fixes.asmfixer) END;

PROCEDURE (VAR f:fix) adjust;
BEGIN
  IF (f.len()=0) OR (f.getc(0)=";") OR f.found(" DB ") OR f.found(" DW ") OR
      f.found("LEA ") THEN
        f.writeline; RETURN
  END;

  IF f.found("CODE_SEG") OR f.found("END") THEN RETURN END;

  f.finddel("[0]");

  IF ~f.found(",O") (* ,[oO][fF][^]* *) THEN
    f.sub(" DD "," DW 0,"); f.sub("ES:[","[ES:");
    f.finddel("40:"); f.finddel("Word Ptr ");
    f.fixproc; f.unbrakdig;
    f.fixop(FALSE); f.fixbrakdig(FALSE,TRUE);

    cseg.insertbracket(f)
  END;

  f.writeline
END adjust;

PROCEDURE (VAR f:fix) init*;
BEGIN
  text.init(f,"inv-fasm.asm");
  f.writeout("OFFSET equ"); f.writeout("Offset equ");
  f.writeout("LEA equ MOV");
  WHILE f.readline() DO f.adjust END
END init;

PROCEDURE (VAR f:fix) done*;
BEGIN text.done(f)
END done;

END fasm.
/* === fasm.m ends === */

/* === wasm.m begins === */
MODULE wasm; (* public domain, nenies proprajho, free for any use *)
IMPORT text,fixes,S := Str;

TYPE fix* = RECORD (fixes.asmfixer) END;

PROCEDURE (VAR w:fix) adjust;
VAR p,p2:INTEGER;

  PROCEDURE writeinc;
  VAR s:ARRAY text.linemax OF CHAR;
  BEGIN
    p := w.find(" "); p2 := p; WHILE w.getc(p2)=" " DO INC(p2) END;
    s := "s_ equ  "; w.extract(0,p); S.Insert(w.X,2,s);
    s[S.Length(s)-1] := w.getc(p2+1);
    w.writeinc(s)
  END writeinc;

BEGIN
  IF (w.len()=0) OR (w.getc(0)=";") OR w.found(":C") OR w.found("S:[")
    OR w.found("Ptr") THEN
      w.writeline; RETURN
  END;

  IF w.found("LEA ") THEN
    w.sub("LEA ","MOV "); w.ins("OFFSET ",w.find(",")+1)
  END;
  w.sub("40:","DS:");

  IF w.found(",Of") OR w.found(",OF") OR w.found("DS:") THEN
    w.writeline; RETURN
  END;

  w.sub(" DD "," DW 0,");
  IF (w.found(" DB ") OR w.found(" DW ")) & (w.getc(0) # " ") THEN
    writeinc;
    w.writeline; RETURN
  END;

  w.unbrakdig; w.fixop(TRUE);

  w.fixbrakdig(TRUE,FALSE);
  IF w.found("]") & w.brakdig(w.at) THEN
    w.del(w.at-2,1); w.ins("][",w.at-2)
  END;

  w.writeline
END adjust;

PROCEDURE (VAR w:fix) init*;
CONST incname="inv-wat.inc";
BEGIN
  text.init(w,"inv-wat.asm"); text.incinit(w,incname);
  w.writeout("B equ byte ptr");  w.writeout("W equ word ptr");
  w.X := "include "; S.Append(incname,w.X); w.writeout(w.X);
  WHILE w.readline() DO w.adjust END
END init;

PROCEDURE (VAR w:fix) done*;
BEGIN text.done(w)
END done;

END wasm.
/* === wasm.m ends === */

/* === a86.m begins === */
MODULE a86; (* public domain, nenies proprajho, free for any use *)
IMPORT text,jmps,cseg,S := Str;

TYPE fix* = RECORD (text.asmfile) END;

PROCEDURE (VAR a:fix) adjust;

  PROCEDURE writeinc;
  VAR s:ARRAY text.linemax OF CHAR; p,p2:INTEGER;
  BEGIN
    IF (a.getc(0) # " ") & (a.found(" DB ") OR a.found(" DW ")) THEN
      p := a.find(" "); p2 := p; WHILE a.getc(p2)=" " DO INC(p2) END;
      s := "EXTRN : "; s[S.Length(s)-1] := a.getc(p2+1);
      a.extract(0,p); S.Insert(a.X,6,s);
      a.writeinc(s)
    END;
  END writeinc;

BEGIN
  IF (a.len() # 0) & (a.getc(0) # ";") THEN
    writeinc;
    a.delend("[0]"); a.finddel("40:");
    IF a.found("JMP ") THEN jmps.shorten(a," SHORT ") END;
    cseg.insert(a)
  END;

  a.writeline
END adjust;

PROCEDURE (VAR a:fix) init*;
CONST incname="inv.inc";
BEGIN
  text.init(a,"inv.a86"); text.incinit(a,incname);
  a.X := "include "; S.Append(incname,a.X); a.writeout(a.X);
  WHILE a.readline() DO a.adjust END
END init;

PROCEDURE (VAR a:fix) done*;
BEGIN text.done(a)
END done;

END a86.
/* === a86.m ends === */

/* === jwasm.m begins === */
MODULE jwasm; (* public domain, nenies proprajho, free for any use *)
IMPORT text;

TYPE fix* = RECORD (text.asmfile) END;

PROCEDURE (VAR j:fix) adjust;
BEGIN
  IF (j.len() # 0) & (j.getc(0) # ";") THEN
    j.sub("40:","DS:");

    IF j.found("LEA ") THEN j.sub(",",",OFFSET "); j.sub("LEA ","MOV ") END;

    IF j.foundstart("Exit:") OR j.foundstart("StartGame:") OR
       j.foundstart("RedrawBunkers:") THEN
         j.sub(":","::") (* default v6 mode needs it, "-Zm" doesn't *)
    END
  END;

  j.writeline
END adjust;

PROCEDURE (VAR j:fix) init*;
BEGIN
  text.init(j,"inv-jwas.asm");
  WHILE j.readline() DO j.adjust END
END init;

PROCEDURE (VAR j:fix) done*;
BEGIN text.done(j)
END done;

END jwasm.
/* === jwasm.m ends === */

/* === wolf.m begins === */
MODULE wolf; (* public domain, nenies proprajho, free for any use *)
IMPORT text,jmps,cseg,C := ctype;

TYPE fix* = RECORD (text.asmfile) END;

PROCEDURE (VAR W:fix) adjust;
BEGIN
  IF (W.len()=0) OR (W.getc(0)=";") THEN W.writeline; RETURN END;
  IF W.found("CODE_SEG") OR W.found("END ") THEN RETURN END;

  IF W.found(" DB ") THEN WHILE W.found('"') DO W.sub('"',"'") END END;
  W.sub(" DD "," DW 0,");

  W.finddel(" Ptr"); W.finddel("40:");
  IF (W.found("SHL") OR W.found("SHR")) THEN W.finddel(",1") END;

  IF W.found("[") & (W.getc(W.at+2)="]") & C.isdigit(W.getc(W.at+1)) THEN
    W.putc(W.at,"+"); W.del(W.at+2,1)
  END;

  IF W.found("ENDP") THEN W.del(0,W.at-1) END;
  IF C.isupper(W.getc(0)) & W.found(":") THEN W.putc(W.at," ") END;

  IF W.found("CMP ") OR W.found("MOV ") OR W.found("INC ") THEN
    IF W.found("BombY") OR W.found("BombT") THEN W.sub("[","byte[") END
  END;

  IF W.found("ES:[") THEN W.sub("ES:[","["); W.writeout(" ES:") END;
  IF W.found("LEA ") THEN W.sub("LEA ","MOV "); W.sub(",",",OFFSET ") END;

  IF W.found("JMP ") THEN jmps.shorten(W,"S ") END;

  cseg.insert(W);

  W.writeline
END adjust;

PROCEDURE (VAR W:fix) init*;
BEGIN
  text.init(W,"inv-wolf.asm");
  WHILE W.readline() DO W.adjust END
END init;

PROCEDURE (VAR W:fix) done*;
BEGIN text.done(W)
END done;

END wolf.
/* === wolf.m ends === */

/* === lazy.m begins === */
MODULE lazy; (* public domain, nenies proprajho, free for any use *)
IMPORT text,fixes,S := Str;

TYPE fix* = RECORD (fixes.asmfixer) END;

PROCEDURE (VAR L:fix) adjust;
VAR p,p2:INTEGER;
CONST optional=TRUE; (* only shuts up warnings *)

  PROCEDURE swapwords;
  VAR word1,word2:ARRAY text.linemax OF CHAR;
  BEGIN
    p := L.find(" "); L.extract(0,p); COPY(L.X,word1);
    p2 := p; WHILE L.getc(p2)=" " DO INC(p2) END;
    L.extract(p2,L.len()); COPY(L.X,word2);

    IF S.Pos(" ",word2,0) = -1 THEN
      COPY(word2,L.data); S.Append(" ",L.data); S.Append(word1,L.data)
    ELSE
      S.Insert(word1,S.Pos(" ",word2,0)+1,word2); COPY(word2,L.data)
    END
  END swapwords;

  PROCEDURE fixleabrak;
  BEGIN
    p := L.find(",")+1; L.ins("[",p); p2 := p;
    WHILE (p2 # L.len()) & (L.getc(p2) # " ") & (L.getc(p2) # ";") DO
      INC(p2)
    END;
    L.ins("]",p2)
  END fixleabrak;

BEGIN
  IF (L.len()=0) OR (L.getc(0)=";") OR L.found(" DB ") OR L.found(" DW ") OR
    L.found("CS:") THEN
      L.writeline; RETURN
  END;

  IF L.found(" SEGMENT") OR L.found(" ENDS") OR
     L.found(" PROC ") OR L.found(" ENDP") THEN
       swapwords
  END;

  L.finddel("[0]");
  IF optional THEN L.finddel("40:"); L.sub("ES:[","[ES:") END;

  IF ~(L.found(",OF") OR L.found(",Of")) THEN
    L.sub(" DD "," DW 0,"); L.finddel("Word Ptr ");

    L.unbrakdig;

    IF L.found("LEA ") THEN
      IF optional THEN fixleabrak END
    ELSE
      L.fixop(FALSE)
    END;

    L.fixbrakdig(FALSE,TRUE)
  END;

  L.writeline
END adjust;

PROCEDURE (VAR L:fix) init*;
BEGIN
  text.init(L,"inv-lazy.asm");
  L.writeout("ideal"); L.writeout("ifdef @lzasm");
  L.writeout("option procalign:1"); L.writeout("endif");
  WHILE L.readline() DO L.adjust END
END init;

PROCEDURE (VAR L:fix) done*;
BEGIN text.done(L)
END done;

END lazy.
/* === lazy.m ends === */

/* === oct.m begins === */
MODULE oct; (* public domain, nenies proprajho, free for any use *)
IMPORT text,fixes,cseg,C := ctype,S := Str;

TYPE fix* = RECORD (fixes.asmfixer) END;

PROCEDURE (VAR o:fix) adjust;
VAR p,p2:INTEGER;

  PROCEDURE lowerline(n:INTEGER);
  VAR z:INTEGER;
    PROCEDURE downcase(k:CHAR):CHAR;
    BEGIN
      IF C.isupper(k) THEN RETURN CHR(ORD(k)+ORD(" "))
      ELSE RETURN k
      END
    END downcase;
  BEGIN FOR z := 0 TO n DO o.putc(z,downcase(o.getc(z))) END
  END lowerline;

  PROCEDURE writeinc;
  VAR s:ARRAY text.linemax OF CHAR;
  BEGIN
    p := o.find(" "); p2 := p; WHILE o.getc(p2)=" " DO INC(p2) END;
    s := "eq s_"; o.extract(0,p);
    S.Append(o.X,s); S.Append(" ?",s); s[S.Length(s)-1] := o.getc(p2+1);
    o.writeinc(s)
  END writeinc;

  PROCEDURE fixlabels;
  BEGIN
    IF o.getc(0) # " " THEN
      IF o.found(":") THEN o.del(o.at,1); o.ins("#",0) END
    END;
  END fixlabels;

  PROCEDURE optimize;

    PROCEDURE fixzero;
      PROCEDURE trim(s:ARRAY OF CHAR);
      BEGIN IF o.found(s) THEN o.del(o.at+4,o.len()-(o.at+4)) END;
      END trim;
    BEGIN
      trim("bx,00h"); trim("di,0 ");
      p := o.find("mov ");
      IF o.foundend("ax,0") THEN o.ins("db 0B8h,0,0 ;",p)
      ELSIF o.foundend("bx,0") THEN o.ins("db 0BBh,0,0 ;",p)
      ELSIF o.foundend("di,0") THEN o.ins("db 0BFh,0,0 ;",p)
      END
    END fixzero;

    PROCEDURE fixmov(reg:ARRAY 3+1 OF CHAR);
    VAR seg:CHAR;
    CONST segcs="c"; seges="e";
      PROCEDURE fixseg():CHAR;
      VAR k:CHAR;
      BEGIN k := " ";
        IF o.found("cs+") OR o.found("es+") THEN
          p2 := S.Pos("s+",o.X,0);
          k := o.X[p2-1];
          S.Delete(o.X,p2-1,3)
        END;
        RETURN k
      END fixseg;
    BEGIN
      IF o.found(reg) THEN
        p := o.at;
        IF reg[2]="," THEN
          p := o.find(",")+1; p2 := p;
          WHILE (p2 < o.len()) & (o.getc(p2) # " ") DO INC(p2) END;
          IF o.getc(p2) = " " THEN DEC(p2) END
        ELSIF reg[0]="," THEN
          p2 := o.find(",")-1; p := p2;
          WHILE (p >= 0) & (o.getc(p) # " ") DO DEC(p) END;
          IF o.getc(p) = " " THEN INC(p) END
        END;

        IF ~C.isdigit(o.getc(p)) THEN
          o.extract(p,p2-p+1);
          p := S.Pos("[",o.X,0);
          IF p >= 0 THEN
            S.Delete(o.X,0,p+1); S.Delete(o.X,S.Length(o.X)-1,1)
          END;

          seg := fixseg();

          IF S.Length(o.X) > 2 THEN
            p := o.find("mov ");
            o.ins(" ;",p); o.ins(")",p); o.ins(o.X,p);
            IF reg = ",al" THEN o.ins("movalto(",p)
            ELSIF reg = "al," THEN o.ins("movtoal(",p)
            ELSIF reg = ",ax" THEN o.ins("movaxto(",p)
            ELSIF reg = "ax," THEN o.ins("movtoax(",p)
            END;

            IF seg = segcs THEN o.ins("segcs ",0)
            ELSIF seg = seges THEN o.ins("seges ",0)
            END
          END
        END
      END
    END fixmov;

  BEGIN
    IF o.found("mov ") & ~o.found("+bx") THEN
      fixmov("al,"); fixmov(",al"); fixmov("ax,"); fixmov(",ax");
      fixzero
    END
  END optimize;

BEGIN
  IF (o.len()=0) OR (o.getc(0)=";") THEN o.writeline; RETURN
  ELSIF (o.found("CODE_SEG") OR o.found("END")) THEN RETURN
  END;

  o.sub(" DD "," DW 0,");
  IF (o.found(" DB ") OR o.found(" DW ")) & (o.getc(0) # " ") THEN
    WHILE o.found('"') DO o.sub('"',"'") END;
    IF o.found("'") THEN lowerline(o.find(" D")+2)
    ELSE lowerline(o.len()-1)
    END;
    writeinc; o.ins("#",0);
    o.writeline; RETURN
  END;

  o.finddel("[0]"); o.finddel("40:"); o.finddel("Word Ptr ");
  o.sub("ES:[","[es+");

  IF o.found("LEA ") OR (o.found(",OF") OR o.found(",Of")) THEN
    fixlabels;
    o.sub("LEA ","mov "); o.sub(",OFFSET ",","); o.sub(",Offset ",",");
    lowerline(o.len()-1);
    o.writeline; RETURN
  END;

  o.unbrakdig; o.fixop(TRUE); o.fixbrakdig(TRUE,TRUE);

  cseg.insertbracket(o); o.sub("[cs:","[cs+");

  o.fixproc; fixlabels; lowerline(o.len()-1); optimize;

  o.writeline
END adjust;

PROCEDURE (VAR o:fix) init*;
CONST incname="inv-oct.inc"; LANG="LANG OCTASM,0.1";
BEGIN
  text.init(o,"inv-oct.asm"); text.incinit(o,incname);
  o.writeout(LANG); o.writeout("use16 file_out \inv-oct.com");
  o.writeout("define eq define");
  o.writeout("eq movtoal() db 0A0h dw #1");
  o.writeout("eq movalto() db 0A2h dw #1");
  o.writeout("eq movtoax() db 0A1h dw #1");
  o.writeout("eq movaxto() db 0A3h dw #1");
  o.writeout("eq segcs db 2Eh"); o.writeout("eq seges db 26h");
  o.X := "\"; S.Append(incname,o.X); o.writeout(o.X);
  o.writeinc(LANG);

  WHILE o.readline() DO o.adjust END
END init;

PROCEDURE (VAR o:fix) done*;
BEGIN text.done(o)
END done;

END oct.
/* === oct.m ends === */

/* === Args.ob2 begins === */
MODULE Args; (* XDS *)

IMPORT TextIO,ProgramArgs;

VAR argc-:INTEGER;

PROCEDURE GetArg*(i:INTEGER; VAR str:ARRAY OF CHAR);
BEGIN IF argc > i THEN TextIO.ReadToken(ProgramArgs.ArgChan(),str) END
END GetArg;

BEGIN
  IF ProgramArgs.IsArgPresent() THEN argc := 2
  ELSE argc := 1
  END
END Args.
/* === Args.ob2 ends === */

/* === inv.m begins === */
(*$MAIN+ *)
MODULE inv; (* public domain, nenies proprajho, free for any use *)
IMPORT Args,O := Out,nasm,fasm,wasm,wolf,a86,jwasm,lazy,oct;
VAR
  which:ARRAY 128 OF CHAR;
  n:nasm.fix; f:fasm.fix; j:jwasm.fix; W:wolf.fix; a:a86.fix; o:oct.fix;
  w:wasm.fix; L:lazy.fix;
CONST
  usage="inv.exe [nasm | fasm | wolf | oct | a86 | jwasm | wasm | lazy]";
  masmsum="9A476DE7"; nasmsum="FFF22EF9";

PROCEDURE cksum(crc:ARRAY 9 OF CHAR);
BEGIN O.String(which); O.String(" CRC32 = "); O.String(crc); O.Ln
END cksum;

BEGIN
  IF Args.argc=1 THEN
    O.String("Usage:  "); O.String(usage); O.Ln; HALT(255)
  ELSIF Args.argc=2 THEN
    Args.GetArg(1,which);
    IF which = "nasm" THEN
      n.init; n.done; cksum(nasmsum)
    ELSIF which = "fasm" THEN
      f.init; f.done; cksum(nasmsum)
    ELSIF which = "wolf" THEN
      W.init; W.done; cksum(nasmsum)
    ELSIF (which="oct") OR (which="octasm") THEN
      o.init; o.done; cksum(nasmsum)
    ELSIF (which="a86") OR (which="a386") THEN
      a.init; a.done; cksum("CDFE86FA")
    ELSIF which = "jwasm" THEN
      j.init; j.done; cksum(masmsum)
    ELSIF (which="wasm") OR (which="watcom") OR (which="wat") THEN
      w.init; w.done; cksum(masmsum)
    ELSIF (which="lazy") OR (which="lzasm") THEN
      L.init; L.done; cksum(masmsum)
    END
  END
END inv.
/* === inv.m ends === */

/* === inv3.m begins === */
(*$MAIN+ *)
MODULE inv3; (* public domain, nenies proprajho, free for any use *)
IMPORT Args,Out,wolf,a86,jwasm;
VAR which:ARRAY 128 OF CHAR; a:a86.fix; j:jwasm.fix; W:wolf.fix;
BEGIN
  IF Args.argc=1 THEN
    Out.String("Usage:  inv3.exe [wolf | a86 | jwasm]");
    Out.Ln; HALT(255)
  ELSIF Args.argc=2 THEN
    Args.GetArg(1,which);
    IF which = "wolf" THEN
      W.init; W.done
    ELSIF which = "a86" THEN
      a.init; a.done
    ELSIF which = "jwasm" THEN
      j.init; j.done
    END
  END
END inv3.
/* === inv3.m ends === */

/* === inv1.m begins === */
(*$MAIN+ *)
MODULE inv1; (* public domain, nenies proprajho, free for any use *)
IMPORT nasm;
VAR n:nasm.fix;
BEGIN n.init; n.done
END inv1.
/* === inv1.m ends === */

/* === invoopob.bat begins === */
@echo off
if "%1"=="test" goto test
for %%a in (obc oxford) do if "%1"=="%%a" goto oxford
for %%a in (xds excelsior) do if "%1"=="%%a" goto excelsior
echo.
echo USAGE: %0 o2  (where "o2" is one of:  obc xds)
echo.
goto end
:excelsior
for %%a in (*.m) do ren %%a *.ob2
for %%a in (inv1 inv3 inv) do xc.exe =m %%a
for %%a in (inv*.exe) do xstrip -q -n %%a
goto end
:oxford
shift
for %%a in (*.ob2) do ren %%a *.m
echo on
@if not "%HDPMI%"=="" goto inv
obc %1 %2 Str.m Myfiles.m li*.m ct*.m t*.m fi*.m jm*.m cs*.m n*.m fa*.m wa*.m wo*.m a86.m jw*.m la*.m o*.m inv.m -o inv.exe
@goto inv3
:inv
obc %1 %2 Str.m Myfiles.m lines.m ctype.m text.m fixes.m jmps.m cseg.m nasm.m fasm.m wasm.m wolf.m a86.m jwasm.m lazy.m oct.m inv.m -o inv.exe
:inv3
obc %1 %2 Str.k Myfiles.k lines.k ctype.k text.k jmps.k cseg.k wolf.k a86.k jwasm.k inv3.m -o inv3.exe
:inv1
obc %1 %2 Str.k Myfiles.k lines.k ctype.k text.k fixes.k cseg.k nasm.k inv1.m -o inv1.exe
@echo off
if exist inv*.exe dir /os inv*.exe | find /i "exe"
goto end
:test
for %%a in (nasm fasm wolf oct a86 jwasm wasm lazy) do if exist INVADERS.ASM inv.exe %%a
if exist inv-nasm.asm nasm -o inv-nasm.com inv-nasm.asm -O3
if exist inv-fasm.asm fasm inv-fasm.asm
if exist inv-wolf.asm wasm.com inv-wolf
if exist inv-oct.asm octasm \inv-oct.asm
if exist inv.a86 a86 +ESP0 *.a86 inv-a86
if exist inv-jwas.asm jwasmr -q -bin -Fo=inv-jwas.com inv-jwas.asm
if exist inv-wat.asm wasmr /q inv-wat
if exist inv-wat.obj warplink /c inv-wat
if exist inv-lazy.asm lzasmx /t inv-lazy
if exist inv-lazy.obj warplink /c inv-lazy
if not exist inv-*.com goto end
dir inv-*.com | find /i "com"
crc32 inv-*.com
:end
/* === invoopob.bat ends === */

# --- extract.awk begins ---
#!/usr/bin/awk -f

/[b]egins ===/{
  fname=$3 ; print fname
  while (getline > 0) {
    if ($0 !~ / [e]nds ===/) {
      print > fname
    }
    else {
      close(fname)
      break
    }
  }
}
# --- extract.awk ends ---

------------------------------------------------------------
*** DATA ENDS DATA ENDS DATA ENDS DATA ENDS ***
------------------------------------------------------------
*/

/* EOF */
