MODULE OSG; (* NW 19.12.94 / 20.10.07 / OSGX 9.5.2017*) IMPORT SYSTEM, Files, Texts, Oberon, OSS; CONST MemSize = 8192; (* class / mode*) Head* = 0; Const* = 1; Var* = 2; Par* = 3; Fld* = 4; Typ* = 5; SProc* = 6; SFunc* = 7; Proc* = 8; NoTyp* = 9; Reg = 10; RegI = 11; Cond = 12; SB = 13; SP = 14; LNK = 15; (*reserved registers*) (* form *) Boolean* = 0; Integer* = 1; Array* = 2; Record* = 3; (*frequently used opcodes*) U = 2000H; Mov = 0; Lsl = 1; Asr = 2; Ror= 3; And = 4; Ann = 5; Ior = 6; Xor = 7; Add = 8; Sub = 9; Cmp = 9; Mul = 10; Div = 11; Ldw = 0; Stw = 2; BR = 0; BLR = 1; BC = 2; BL = 3; MI = 0; PL = 8; EQ = 1; NE = 9; LT = 5; GE = 13; LE = 6; GT = 14; TYPE Object* = POINTER TO ObjDesc; Type* = POINTER TO TypeDesc; Item* = RECORD mode*, lev*: INTEGER; type*: Type; a*, b, r: LONGINT END ; ObjDesc*= RECORD class*, lev*: INTEGER; next*, dsc*: Object; type*: Type; name*: OSS.Ident; val*, nofpar*: LONGINT; comd*: BOOLEAN END ; TypeDesc* = RECORD form*: INTEGER; dsc*: Object; base*: Type; size*, len*, nofpar*: LONGINT END ; VAR boolType*, intType*: Type; curlev*, pc*: INTEGER; curSB: INTEGER; entry, fixlist, fixorgD: LONGINT; RH: LONGINT; (*register stack pointer*) W: Texts.Writer; relmap: ARRAY 6 OF INTEGER; code*: ARRAY MemSize OF LONGINT; mnemo0, mnemo1: ARRAY 16, 4 OF CHAR; (*for decoder*) PROCEDURE Put0(op, a, b, c: LONGINT); BEGIN (*emit format-0 instruction*) code[pc] := ((a*10H + b) * 10H + op) * 10000H + c; INC(pc) END Put0; PROCEDURE Put1(op, a, b, im: LONGINT); BEGIN (*emit format-1 instruction*) IF im < 0 THEN INC(op, 1000H) END ; (*set v-bit*) code[pc] := (((a+40H) * 10H + b) * 10H + op) * 10000H + (im MOD 10000H); INC(pc) END Put1; PROCEDURE Put2(op, a, b, off: LONGINT); BEGIN (*emit load/store instruction*) code[pc] := (((op+8) * 10H + a) * 10H + b) * 100000H + (off MOD 10000H); INC(pc) END Put2; PROCEDURE Put3(op, cond, off: LONGINT); BEGIN (*emit branch instruction*) code[pc] := ((op+12) * 10H + cond) * 1000000H + (off MOD 1000000H); INC(pc) END Put3; PROCEDURE incR; BEGIN IF RH < SB THEN INC(RH) ELSE OSS.Mark("register stack overflow") END END incR; PROCEDURE CheckRegs*; BEGIN IF RH # 0 THEN (* Texts.WriteString(W, "RegStack!"); Texts.WriteInt(W, R, 4); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) *) OSS.Mark("Reg Stack"); RH := 0 END END CheckRegs; PROCEDURE SetCC(VAR x: Item; n: LONGINT); BEGIN x.mode := Cond; x.a := 0; x.b := 0; x.r := n END SetCC; PROCEDURE TestRange(x: LONGINT); BEGIN (*16-bit entity*) IF (x > 0FFFFH) OR (x < -10000H) THEN OSS.Mark("value too large") END END TestRange; PROCEDURE negated(cond: LONGINT): LONGINT; BEGIN IF cond < 8 THEN cond := cond+8 ELSE cond := cond-8 END ; RETURN cond END negated; PROCEDURE invalSB; BEGIN curSB := 1 END invalSB; PROCEDURE fix(at, with: LONGINT); BEGIN code[at] := code[at] DIV 1000000H * 1000000H + (with MOD 1000000H) END fix; PROCEDURE FixLink*(L: LONGINT); VAR L1: LONGINT; BEGIN WHILE L # 0 DO IF L < MemSize THEN L1 := code[L] MOD 40000H; fix(L, pc-L-1); L := L1 END END END FixLink; PROCEDURE GetSB; BEGIN IF curSB = 1 THEN Put2(Ldw, SB, 0, pc-fixorgD); fixorgD := pc-1; curSB := 0 END END GetSB; PROCEDURE load(VAR x: Item); BEGIN IF x.mode # Reg THEN IF x.mode = Var THEN IF x.r > 0 THEN (*local*) Put2(Ldw, RH, SP, x.a) ELSE GetSB; Put2(Ldw, RH, SB, x.a) END ; x.r := RH; incR ELSIF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); Put2(Ldw, RH, RH, 0); x.r := RH; incR ELSIF x.mode = Const THEN IF (x.a >= 10000H) OR (x.a < -10000H) THEN OSS.Mark("const too large") END ; Put1(Mov, RH, 0, x.a); x.r := RH; incR ELSIF x.mode = RegI THEN Put2(Ldw, x.r, x.r, x.a) ELSIF x.mode = Cond THEN Put3(2, negated(x.r), 2); FixLink(x.b); Put1(Mov, RH, 0, 1); Put3(2, 7, 1); FixLink(x.a); Put1(Mov, RH, 0, 0); x.r := RH; incR END ; x.mode := Reg END END load; PROCEDURE loadAdr(VAR x: Item); BEGIN IF x.mode = Var THEN IF x.r > 0 THEN (*local*) Put1(Add, RH, SP, x.a); x.r := RH ELSE GetSB; Put1(Add, RH, SB, x.a) END ; incR ELSIF x.mode = Par THEN Put2(Ldw, RH, SP, x.a); Put1(Add, RH, RH, x.b); x.r := RH; incR ELSIF (x.mode = RegI) & (x.a # 0) THEN Put1(Add, x.r, x.r, x.a) ELSE OSS.Mark("address error") END ; x.mode := Reg END loadAdr; PROCEDURE loadCond(VAR x: Item); BEGIN IF x.type.form = Boolean THEN IF x.mode = Const THEN x.r := 15 - x.a*8 ELSE load(x); Put1(Cmp, x.r, x.r, 0); x.r := NE; DEC(RH) END ; x.mode := Cond; x.a := 0; x.b := 0 ELSE OSS.Mark("not Boolean") END END loadCond; PROCEDURE merged(L0, L1: LONGINT): LONGINT; VAR L2, L3: LONGINT; BEGIN IF L0 # 0 THEN L3 := L0; REPEAT L2 := L3; L3 := code[L2] MOD 40000H UNTIL L3 = 0; code[L2] := code[L2] + L1; L1 := L0 END ; RETURN L1 END merged; (*-----------------------------------------------*) PROCEDURE IncLevel*(n: INTEGER); BEGIN curlev := curlev + n END IncLevel; PROCEDURE MakeConstItem*(VAR x: Item; typ: Type; val: LONGINT); BEGIN x.mode := Const; x.type := typ; x.a := val END MakeConstItem; PROCEDURE MakeItem*(VAR x: Item; y: Object; curlev: LONGINT); VAR r: LONGINT; BEGIN x.mode := y.class; x.type := y.type; x.a := y.val; x.r := y.lev; IF y.class = Par THEN x.b := 0 END ; IF (y.lev > 0) & (y.lev # curlev) & (y.class # Const) THEN OSS.Mark("level error") END END MakeItem; PROCEDURE Field*(VAR x: Item; y: Object); (* x := x.y *) BEGIN IF (x.mode = Var) OR (x.mode = RegI) THEN x.a := x.a + y.val ELSIF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); x.mode := RegI; x.r := RH; x.a := y.val; incR END END Field; PROCEDURE Index*(VAR x, y: Item); (* x := x[y] *) VAR s: LONGINT; BEGIN IF y.mode = Const THEN IF (y.a < 0) OR (y.a >= x.type.len) THEN OSS.Mark("bad index") END ; IF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); x.mode := RegI; x.a := 0 END ; x.a := x.a + y.a * x.type.base.size ELSE s := x.type.base.size; IF y.mode # Reg THEN load(y) END ; IF s = 4 THEN Put1(Lsl, y.r, y.r, 2) ELSE Put1(Mul, y.r, y.r, s) END ; IF x.mode = Var THEN IF x.r > 0 THEN Put0(Add, y.r, SP, y.r) ELSE GetSB; Put0(Add, y.r, SB, y.r) END ; x.mode := RegI; x.r := y.r ELSIF x.mode = Par THEN Put2(Ldw, RH, SP, x.a); Put0(Add, y.r, RH, y.r); x.mode := RegI; x.r := y.r ELSIF x.mode = RegI THEN Put0(Add, x.r, x.r, y.r); DEC(RH) END END END Index; (* Code generation for Boolean operators *) PROCEDURE Not*(VAR x: Item); (* x := ~x *) VAR t: LONGINT; BEGIN IF x.mode # Cond THEN loadCond(x) END ; x.r := negated(x.r); t := x.a; x.a := x.b; x.b := t END Not; PROCEDURE And1*(VAR x: Item); (* x := x & *) BEGIN IF x.mode # Cond THEN loadCond(x) END ; Put3(BC, negated(x.r), x.a); x.a := pc-1; FixLink(x.b); x.b := 0 END And1; PROCEDURE And2*(VAR x, y: Item); BEGIN IF y.mode # Cond THEN loadCond(y) END ; x.a := merged(y.a, x.a); x.b := y.b; x.r := y.r END And2; PROCEDURE Or1*(VAR x: Item); (* x := x OR *) BEGIN IF x.mode # Cond THEN loadCond(x) END ; Put3(BC, x.r, x.b); x.b := pc-1; FixLink(x.a); x.a := 0 END Or1; PROCEDURE Or2*(VAR x, y: Item); BEGIN IF y.mode # Cond THEN loadCond(y) END ; x.a := y.a; x.b := merged(y.b, x.b); x.r := y.r END Or2; (* Code generation for arithmetic operators *) PROCEDURE Neg*(VAR x: Item); (* x := -x *) BEGIN IF x.mode = Const THEN x.a := -x.a ELSE load(x); Put1(Mov, RH, 0, 0); Put0(Sub, x.r, RH, x.r) END END Neg; PROCEDURE AddOp*(op: LONGINT; VAR x, y: Item); (* x := x +- y *) BEGIN IF op = OSS.plus THEN IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a + y.a ELSIF y.mode = Const THEN load(x); IF y.a # 0 THEN Put1(Add, x.r, x.r, y.a) END ELSE load(x); load(y); Put0(Add, RH-2, x.r, y.r); DEC(RH); x.r := RH-1 END ELSE (*op = OSS.minus*) IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a - y.a ELSIF y.mode = Const THEN load(x); IF y.a # 0 THEN Put1(Sub, x.r, x.r, y.a) END ELSE load(x); load(y); Put0(Sub, RH-2, x.r, y.r); DEC(RH); x.r := RH-1 END END END AddOp; PROCEDURE MulOp*(VAR x, y: Item); (* x := x * y *) BEGIN IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a * y.a ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(Lsl, x.r, x.r, 1) ELSIF y.mode = Const THEN load(x); Put1(Mul, x.r, x.r, y.a) ELSIF x.mode = Const THEN load(y); Put1(Mul, y.r, y.r, x.a); x.mode := Reg; x.r := y.r ELSE load(x); load(y); Put0(Mul, RH-2, x.r, y.r); DEC(RH); x.r := RH-1 END END MulOp; PROCEDURE DivOp*(op: LONGINT; VAR x, y: Item); (* x := x op y *) BEGIN IF op = OSS.div THEN IF (x.mode = Const) & (y.mode = Const) THEN IF y.a > 0 THEN x.a := x.a DIV y.a ELSE OSS.Mark("bad divisor") END ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(Asr, x.r, x.r, 1) ELSIF y.mode = Const THEN IF y.a > 0 THEN load(x); Put1(Div, x.r, x.r, y.a) ELSE OSS.Mark("bad divisor") END ELSE load(y); load(x); Put0(Div, RH-2, x.r, y.r); DEC(RH); x.r := RH-1 END ELSE (*op = OSS.mod*) IF (x.mode = Const) & (y.mode = Const) THEN IF y.a > 0 THEN x.a := x.a MOD y.a ELSE OSS.Mark("bad modulus") END ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(And, x.r, x.r, 1) ELSIF y.mode = Const THEN IF y.a > 0 THEN load(x); Put1(Div, x.r, x.r, y.a); Put0(Mov+U, x.r, 0, 0) ELSE OSS.Mark("bad modulus") END ELSE load(y); load(x); Put0(Div, RH-2, x.r, y.r); Put0(Mov+U, RH-2, 0, 0); DEC(RH); x.r := RH-1 END END END DivOp; PROCEDURE Relation*(op: INTEGER; VAR x, y: Item); (* x := x ? y *) BEGIN IF y.mode = Const THEN load(x); Put1(Cmp, x.r, x.r, y.a); DEC(RH) ELSE load(x); load(y); Put0(Cmp, x.r, x.r, y.r); DEC(RH, 2) END ; SetCC(x, relmap[op - OSS.eql]) END Relation; PROCEDURE Store*(VAR x, y: Item); (* x := y *) BEGIN load(y); IF x.mode = Var THEN IF x.r > 0 THEN (*local*) Put2(Stw, y.r, SP, x.a) ELSE GetSB; Put2(Stw, y.r, SB, x.a) END ELSIF x.mode = Par THEN Put2(Ldw, RH, SP, x.a); Put2(Stw, y.r, RH, x.b) ELSIF x.mode = RegI THEN Put2(Stw, y.r, x.r, x.a); DEC(RH) ELSE OSS.Mark("illegal assignment") END ; DEC(RH) END Store; PROCEDURE VarParam*(VAR x: Item; ftype: Type); VAR xmd: INTEGER; BEGIN xmd := x.mode; loadAdr(x); IF (ftype.form = Array) & (ftype.len < 0) THEN (*open array*) IF x.type.len >= 0 THEN Put1(Mov, RH, 0, x.type.len) ELSE Put2(Ldw, RH, SP, x.a+4) END ; incR ELSIF ftype.form = Record THEN IF xmd = Par THEN Put2(Ldw, RH, SP, x.a+4); incR END END END VarParam; PROCEDURE ValueParam*(VAR x: Item); BEGIN load(x) END ValueParam; PROCEDURE OpenArrayParam*(VAR x: Item); BEGIN loadAdr(x); IF x.type.len >= 0 THEN Put1(Mov, RH, 0, x.type.len) ELSE Put2(Ldw, RH, SP, x.a+4) END ; incR END OpenArrayParam; (*---------------------------------*) PROCEDURE CFJump*(VAR x: Item); (*conditional forward jump*) BEGIN IF x.mode # Cond THEN loadCond(x) END ; Put3(2, negated(x.r), x.a); FixLink(x.b); x.a := pc-1 END CFJump; PROCEDURE FJump*(VAR L: LONGINT); (*unconditional forward jump*) BEGIN Put3(2, 7, L); L := pc-1 END FJump; PROCEDURE CBJump*(VAR x: Item; L: LONGINT); (*conditional backward jump*) BEGIN IF x.mode # Cond THEN loadCond(x) END ; Put3(2, negated(x.r), L-pc-1) END CBJump; PROCEDURE BJump*(L: LONGINT); (*unconditional backward jump*) BEGIN Put3(2, 7, L-pc-1) END BJump; PROCEDURE Call*(VAR obj: Object); BEGIN Put3(3, 7, (obj.val DIV 4) - pc-1); RH := 0 END Call; PROCEDURE Enter*(parblksize, locblksize: LONGINT; comd: BOOLEAN); VAR a, r: LONGINT; BEGIN a := 4; r := 0; Put1(Sub, SP, SP, locblksize); Put2(Stw, LNK, SP, 0); WHILE a < parblksize DO Put2(Stw, r, SP, a); INC(r); INC(a, 4) END ; (* IF comd THEN Put2(Ldw, SB, 0, 0) END *) END Enter; PROCEDURE Return*(size: LONGINT); BEGIN Put2(Ldw, LNK, SP, 0); Put1(Add, SP, SP, size); Put3(BR, 7, LNK); RH := 0 END Return; PROCEDURE Ord*(VAR x: Item); BEGIN load(x); x.type := intType END Ord; PROCEDURE OpenInput*; BEGIN Put3(3, 7, pc - fixlist + 101000H); fixlist := pc-1; invalSB END OpenInput; PROCEDURE ReadInt*(VAR x: Item); BEGIN loadAdr(x); Put3(3, 7, pc - fixlist + 102000H); fixlist := pc-1; DEC(RH); invalSB END ReadInt; PROCEDURE eot*(VAR x: Item); BEGIN Put3(3, 7, pc - fixlist + 103000H); fixlist := pc-1; Put1(Cmp, 0, 0, Texts.Int); SetCC(x, NE); invalSB END eot; PROCEDURE WriteChar*(VAR x: Item); BEGIN load(x); Put3(3, 7, pc - fixlist + 104000H); fixlist:= pc-1; DEC(RH); invalSB END WriteChar; PROCEDURE WriteInt*(VAR x, y: Item); BEGIN load(x); load(y); Put3(3, 7, pc - fixlist + 105000H); fixlist := pc-1; DEC(RH, 2); invalSB END WriteInt; PROCEDURE WriteLn*; BEGIN Put3(3, 7, pc - fixlist + 106000H); fixlist := pc-1; invalSB END WriteLn; PROCEDURE Switch*(VAR x: Item); BEGIN Put1(Mov, RH, 0, -60); Put2(Ldw, RH, RH, 0); x.mode := Reg; x.type := intType; x.r := RH; INC(RH) END Switch; PROCEDURE LED*(VAR x: Item); BEGIN load(x); Put1(Mov, RH, 0, -60); Put2(Stw, x.r, RH, 0); DEC(RH) END LED ; PROCEDURE Open*; BEGIN curlev := 0; pc := 0; RH := 0; fixlist := 0; fixorgD := 0 END Open; PROCEDURE Header*(size: LONGINT); BEGIN entry := pc*4; Put1(Sub, SP, SP, 4); Put2(Stw, LNK, SP, 0); invalSB END Header; PROCEDURE MakeFileName(VAR FName: OSS.Ident; name, ext: ARRAY OF CHAR); VAR i, j: INTEGER; BEGIN i := 0; j := 0; (*assume name suffix less than 4 characters*) WHILE (i < OSS.IdLen-5) & (name[i] > 0X) DO FName[i] := name[i]; INC(i) END ; REPEAT FName[i]:= ext[j]; INC(i); INC(j) UNTIL ext[j] = 0X; FName[i] := 0X END MakeFileName; PROCEDURE Close*(VAR modid: OSS.Ident; key, datasize: LONGINT; topScope: Object); (*write code file*) VAR i, nofent, nofimp, comsize, size: INTEGER; obj: Object; name: OSS.Ident; F: Files.File; R: Files.Rider; BEGIN Put2(Ldw, LNK, SP, 0); Put1(Add, SP, SP, 4); Put3(BR, 7, LNK); obj := topScope.next; comsize := 4; nofent := 1; nofimp := 1; WHILE obj # NIL DO IF obj.comd THEN i := 0; (*count entries and commands*) WHILE obj.name[i] # 0X DO INC(i) END ; i := (i+4) DIV 4 * 4; INC(comsize, i+4); INC(nofent); INC(nofimp) END ; obj := obj.next END ; size := datasize + comsize + (pc + nofimp + nofent + 1)*4; MakeFileName(name, modid, ".rsc"); (*write code file*) F := Files.New(name); Files.Set(R, F, 0); Files.WriteString(R, modid); Files.WriteInt(R, key); Files.Write(R, 1X); (*version*) Files.WriteInt(R, size); Files.WriteString(R, "IO"); Files.WriteInt(R, 3A8372E2H); Files.Write(R, 0X); (*import*) Files.WriteInt(R, 0); (*no type descriptors*) Files.WriteInt(R, datasize); (*data*) Files.WriteInt(R, 0); (*no strings*) Files.WriteInt(R, pc); (*code len*) FOR i := 0 TO pc-1 DO Files.WriteInt(R, code[i]) END ; (*program*) obj := topScope.next; WHILE obj # NIL DO (*commands*) IF obj.comd THEN Files.WriteString(R, obj.name); Files.WriteInt(R, obj.val) END ; obj := obj.next END ; Files.Write(R, 0X); Files.WriteInt(R, nofent); Files.WriteInt(R, entry); (*of program body*) obj := topScope.next; WHILE obj # NIL DO (*entries*) IF obj.comd THEN Files.WriteInt(R, obj.val) END ; obj := obj.next END ; Files.WriteInt(R, -1); (*no pointer variables*) Files.WriteInt(R, fixlist); Files.WriteInt(R, fixorgD); Files.WriteInt(R, 0); Files.WriteInt(R, entry); Files.Write(R, "O"); Files.Register(F) END Close; (*-------------------- output -----------------------*) PROCEDURE WriteReg(r: LONGINT); BEGIN Texts.Write(W, " "); IF r < 13 THEN Texts.Write(W, "R"); Texts.WriteInt(W, r, 1) ELSIF r = 13 THEN Texts.WriteString(W, "SB") ELSIF r = 14 THEN Texts.WriteString(W, "SP") ELSIF r = 15 THEN Texts.WriteString(W, "LNK") END END WriteReg; PROCEDURE Decode*; VAR i, w, a, b, c, op: LONGINT; BEGIN Texts.WriteHex(W, code[0]); Texts.WriteHex(W, code[1]); Texts.WriteLn(W); i := 0; WHILE i < pc DO w := code[i]; a := w DIV 1000000H MOD 10H; b := w DIV 100000H MOD 10H; Texts.WriteInt(W, i, 4); Texts.WriteHex(W, w); Texts.Write(W, 9X); IF ASR(w, 31) = 0 THEN (*~p: register instruction*) op := w DIV 10000H MOD 10H; Texts.WriteString(W, mnemo0[op]); WriteReg(a); WriteReg(b); IF ~ODD(w DIV 40000000H) THEN (*~q*) WriteReg(w MOD 10H) ELSE c := w MOD 10000H;; IF ODD(w DIV 10000000H) THEN (*v*) c := c + 0FFFF0000H END ; Texts.WriteInt(W, c, 8) END ELSIF ~ODD(w DIV 40000000H) THEN (*load/store*) IF ODD(w DIV 20000000H) THEN Texts.WriteString(W, "STW ") ELSE Texts.WriteString(W, "LDW") END ; WriteReg(a); WriteReg(b); Texts.WriteInt(W, w MOD 100000H, 8) ELSE (*Branch instr*) Texts.Write(W, "B"); IF ODD(w DIV 10000000H) THEN Texts.Write(W, "L") END ; Texts.WriteString(W, mnemo1[a]); IF ~ODD(w DIV 20000000H) THEN WriteReg(w MOD 10H) ELSE w := w MOD 1000000H; IF w >= 800000H THEN w := w - 1000000H END ; Texts.WriteInt(W, w, 8) END END ; Texts.WriteLn(W); INC(i) END ; Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END Decode; PROCEDURE HexCh(k: LONGINT): CHAR; BEGIN IF k >= 10 THEN INC(k, 27H) END ; RETURN CHR(k+30H) END HexCh; BEGIN Texts.OpenWriter(W); NEW(boolType); boolType.form := Boolean; boolType.size := 4; NEW(intType); intType.form := Integer; intType.size := 4; relmap[0] := EQ; relmap[1] := NE; relmap[2] := LT; relmap[3] := LE; relmap[4] := GT; relmap[5] := GE; mnemo0[Mov] := "MOV"; mnemo0[Lsl] := "LSL"; mnemo0[Asr] := "ASR"; mnemo0[Ror] := "ROR"; mnemo0[And] := "AND"; mnemo0[Ann] := "ANN"; mnemo0[Ior] := "IOR"; mnemo0[Xor] := "XOR"; mnemo0[Add] := "ADD"; mnemo0[Sub] := "SUB"; mnemo0[Mul] := "MUL"; mnemo0[Div] := "DIV"; mnemo1[PL] := "PL "; mnemo1[MI] := "MI "; mnemo1[EQ] := "EQ "; mnemo1[NE] := "NE "; mnemo1[LT] := "LT "; mnemo1[GE] := "GE "; mnemo1[LE] := "LE "; mnemo1[GT] := "GT "; mnemo1[15] := "NO "; END OSG.