]> git.mdlowis.com Git - proto/obnc.git/commitdiff
Add oberon0 code from wirth for reference
authorMichael D. Lowis <mike.lowis@gentex.com>
Tue, 20 Apr 2021 19:28:02 +0000 (15:28 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Tue, 20 Apr 2021 19:28:02 +0000 (15:28 -0400)
cerise/oberon0/IO.Mod [new file with mode: 0644]
cerise/oberon0/OSG.Mod [new file with mode: 0644]
cerise/oberon0/OSP.Mod [new file with mode: 0644]
cerise/oberon0/OSS.Mod [new file with mode: 0644]
cerise/oberon0/RISC.Mod [new file with mode: 0644]
cerise/oberon0/TestOberon0.Mod [new file with mode: 0644]

diff --git a/cerise/oberon0/IO.Mod b/cerise/oberon0/IO.Mod
new file mode 100644 (file)
index 0000000..f248fbf
--- /dev/null
@@ -0,0 +1,30 @@
+MODULE IO;   (*for Oberon0   NW 29.4.2017*)\r
+  IMPORT Texts,Oberon;\r
+  VAR S: Texts.Scanner;  W: Texts.Writer;\r
+\r
+  PROCEDURE OpenInput*;\r
+  BEGIN Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S)\r
+  END OpenInput;\r
+\r
+  PROCEDURE ReadInt*(VAR x: LONGINT);\r
+  BEGIN x := S.i;  Texts.Scan(S)\r
+  END ReadInt;\r
+\r
+  PROCEDURE Class*(): INTEGER;\r
+  BEGIN RETURN S.class\r
+  END Class;\r
+\r
+  PROCEDURE Write*(ch: CHAR);\r
+  BEGIN Texts.Write(W, ch)\r
+  END Write;\r
+\r
+  PROCEDURE WriteInt*(x: LONGINT; n: INTEGER);\r
+  BEGIN Texts.WriteInt(W, x, n)\r
+  END WriteInt;\r
+\r
+  PROCEDURE WriteLn*;\r
+  BEGIN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+  END WriteLn;\r
+\r
+BEGIN Texts.OpenWriter(W)\r
+END IO.\r
diff --git a/cerise/oberon0/OSG.Mod b/cerise/oberon0/OSG.Mod
new file mode 100644 (file)
index 0000000..f7c15dd
--- /dev/null
@@ -0,0 +1,565 @@
+MODULE OSG; (* NW 19.12.94 / 20.10.07 / OSGX  9.5.2017*)\r
+  IMPORT SYSTEM, Files, Texts, Oberon, OSS;\r
+\r
+  CONST MemSize = 8192;\r
+    (* class / mode*) Head* = 0;\r
+    Const* = 1; Var* = 2; Par* = 3; Fld* = 4; Typ* = 5;\r
+    SProc* = 6; SFunc* = 7; Proc* = 8; NoTyp* = 9; Reg = 10; RegI = 11; Cond = 12;\r
+    SB = 13; SP = 14; LNK = 15;   (*reserved registers*)\r
+    (* form *) Boolean* = 0; Integer* = 1; Array* = 2; Record* = 3;\r
+\r
+  (*frequently used opcodes*)  U = 2000H;\r
+    Mov = 0; Lsl = 1; Asr = 2; Ror= 3; And = 4; Ann = 5; Ior = 6; Xor = 7;\r
+    Add = 8; Sub = 9; Cmp = 9; Mul = 10; Div = 11;\r
+    Ldw = 0; Stw = 2;\r
+    BR = 0; BLR = 1; BC = 2; BL = 3;\r
+    MI = 0; PL = 8; EQ = 1; NE = 9; LT = 5; GE = 13; LE = 6; GT = 14;\r
+\r
+  TYPE Object* = POINTER TO ObjDesc;\r
+    Type* = POINTER TO TypeDesc;\r
+\r
+    Item* = RECORD\r
+      mode*, lev*: INTEGER;\r
+      type*: Type;\r
+      a*, b, r: LONGINT\r
+    END ;\r
+\r
+    ObjDesc*= RECORD\r
+      class*, lev*: INTEGER;\r
+      next*, dsc*: Object;\r
+      type*: Type;\r
+      name*: OSS.Ident;\r
+      val*, nofpar*: LONGINT;\r
+      comd*: BOOLEAN\r
+    END ;\r
+\r
+    TypeDesc* = RECORD\r
+      form*: INTEGER;\r
+      dsc*: Object;\r
+      base*: Type;\r
+      size*, len*, nofpar*: LONGINT\r
+    END ;\r
+\r
+  VAR boolType*, intType*: Type;\r
+    curlev*, pc*: INTEGER;\r
+    curSB: INTEGER;\r
+    entry, fixlist, fixorgD: LONGINT;\r
+    RH: LONGINT;  (*register stack pointer*)\r
+    W: Texts.Writer;\r
+    relmap: ARRAY 6 OF INTEGER;\r
+    code*: ARRAY MemSize OF LONGINT;\r
+    mnemo0, mnemo1: ARRAY 16, 4 OF CHAR;  (*for decoder*)\r
+\r
+  PROCEDURE Put0(op, a, b, c: LONGINT);\r
+  BEGIN (*emit format-0 instruction*)\r
+    code[pc] := ((a*10H + b) * 10H + op) * 10000H + c; INC(pc)\r
+  END Put0;\r
+\r
+  PROCEDURE Put1(op, a, b, im: LONGINT);\r
+  BEGIN (*emit format-1 instruction*)\r
+    IF im < 0 THEN INC(op, 1000H) END ;  (*set v-bit*)\r
+    code[pc] := (((a+40H) * 10H + b) * 10H + op) * 10000H + (im MOD 10000H); INC(pc)\r
+  END Put1;\r
+\r
+  PROCEDURE Put2(op, a, b, off: LONGINT);\r
+  BEGIN (*emit load/store instruction*)\r
+    code[pc] := (((op+8) * 10H + a) * 10H + b) * 100000H + (off MOD 10000H); INC(pc)\r
+  END Put2;\r
+\r
+  PROCEDURE Put3(op, cond, off: LONGINT);\r
+  BEGIN (*emit branch instruction*)\r
+    code[pc] := ((op+12) * 10H + cond) * 1000000H + (off MOD 1000000H); INC(pc)\r
+  END Put3;\r
+\r
+  PROCEDURE incR;\r
+  BEGIN\r
+    IF RH < SB THEN INC(RH) ELSE OSS.Mark("register stack overflow") END\r
+  END incR;\r
+\r
+  PROCEDURE CheckRegs*;\r
+  BEGIN\r
+    IF RH # 0 THEN\r
+    (*  Texts.WriteString(W, "RegStack!"); Texts.WriteInt(W, R, 4);\r
+      Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) *)\r
+      OSS.Mark("Reg Stack"); RH := 0\r
+    END\r
+  END CheckRegs;\r
+\r
+  PROCEDURE SetCC(VAR x: Item; n: LONGINT);\r
+  BEGIN x.mode := Cond; x.a := 0; x.b := 0; x.r := n\r
+  END SetCC;\r
+\r
+  PROCEDURE TestRange(x: LONGINT);\r
+  BEGIN (*16-bit entity*)\r
+    IF (x > 0FFFFH) OR (x < -10000H) THEN OSS.Mark("value too large") END\r
+  END TestRange;\r
+\r
+  PROCEDURE negated(cond: LONGINT): LONGINT;\r
+  BEGIN\r
+    IF cond < 8 THEN cond := cond+8 ELSE cond := cond-8 END ;\r
+    RETURN cond\r
+  END negated;\r
+\r
+  PROCEDURE invalSB;\r
+  BEGIN curSB := 1\r
+  END invalSB;\r
+\r
+  PROCEDURE fix(at, with: LONGINT);\r
+  BEGIN code[at] := code[at] DIV 1000000H * 1000000H + (with MOD 1000000H)\r
+  END fix;\r
+\r
+  PROCEDURE FixLink*(L: LONGINT);\r
+    VAR L1: LONGINT;\r
+  BEGIN\r
+    WHILE L # 0 DO\r
+      IF L < MemSize THEN L1 := code[L] MOD 40000H; fix(L, pc-L-1); L := L1 END\r
+    END\r
+  END FixLink;\r
+\r
+  PROCEDURE GetSB;\r
+  BEGIN\r
+    IF curSB = 1 THEN Put2(Ldw, SB, 0, pc-fixorgD); fixorgD := pc-1; curSB := 0 END\r
+  END GetSB;\r
+\r
+  PROCEDURE load(VAR x: Item);\r
+  BEGIN\r
+    IF x.mode # Reg THEN\r
+      IF x.mode = Var THEN\r
+        IF x.r > 0 THEN (*local*) Put2(Ldw, RH, SP, x.a) ELSE GetSB; Put2(Ldw, RH, SB, x.a) END ;\r
+        x.r := RH; incR\r
+      ELSIF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); Put2(Ldw, RH, RH, 0); x.r := RH; incR\r
+      ELSIF x.mode = Const THEN\r
+        IF (x.a >= 10000H) OR (x.a < -10000H) THEN OSS.Mark("const too large") END ;\r
+        Put1(Mov, RH, 0, x.a); x.r := RH; incR\r
+      ELSIF x.mode = RegI THEN Put2(Ldw, x.r, x.r, x.a)\r
+      ELSIF x.mode = Cond THEN\r
+        Put3(2, negated(x.r), 2);\r
+        FixLink(x.b); Put1(Mov, RH, 0, 1); Put3(2, 7, 1);\r
+        FixLink(x.a); Put1(Mov, RH, 0, 0); x.r := RH; incR\r
+      END ;\r
+      x.mode := Reg\r
+    END\r
+  END load;\r
+\r
+  PROCEDURE loadAdr(VAR x: Item);\r
+  BEGIN\r
+    IF x.mode = Var THEN\r
+      IF x.r > 0 THEN (*local*) Put1(Add, RH, SP, x.a); x.r := RH ELSE GetSB; Put1(Add, RH, SB, x.a) END ;\r
+      incR\r
+    ELSIF x.mode = Par THEN Put2(Ldw, RH, SP, x.a); Put1(Add, RH, RH, x.b); x.r := RH; incR\r
+    ELSIF (x.mode = RegI) & (x.a # 0) THEN Put1(Add, x.r, x.r, x.a)\r
+    ELSE OSS.Mark("address error")\r
+    END ;\r
+    x.mode := Reg\r
+  END loadAdr;\r
+\r
+  PROCEDURE loadCond(VAR x: Item);\r
+  BEGIN\r
+    IF x.type.form = Boolean THEN\r
+      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 ;\r
+      x.mode := Cond; x.a := 0; x.b := 0\r
+    ELSE OSS.Mark("not Boolean")\r
+    END\r
+  END loadCond;\r
+\r
+  PROCEDURE merged(L0, L1: LONGINT): LONGINT;\r
+    VAR L2, L3: LONGINT;\r
+  BEGIN\r
+    IF L0 # 0 THEN\r
+      L3 := L0;\r
+      REPEAT L2 := L3; L3 := code[L2] MOD 40000H UNTIL L3 = 0;\r
+      code[L2] := code[L2] + L1; L1 := L0\r
+    END ;\r
+    RETURN L1\r
+  END merged;\r
+\r
+  (*-----------------------------------------------*)\r
+\r
+  PROCEDURE IncLevel*(n: INTEGER);\r
+  BEGIN curlev := curlev + n\r
+  END IncLevel;\r
+\r
+  PROCEDURE MakeConstItem*(VAR x: Item; typ: Type; val: LONGINT);\r
+  BEGIN\r
+    x.mode := Const;\r
+    x.type := typ;\r
+    x.a := val\r
+  END MakeConstItem;\r
+\r
+  PROCEDURE MakeItem*(VAR x: Item; y: Object; curlev: LONGINT);\r
+    VAR r: LONGINT;\r
+  BEGIN x.mode := y.class; x.type := y.type; x.a := y.val; x.r := y.lev;\r
+    IF y.class = Par THEN x.b := 0 END ;\r
+    IF (y.lev > 0) & (y.lev # curlev) & (y.class # Const) THEN OSS.Mark("level error") END\r
+  END MakeItem;\r
+\r
+  PROCEDURE Field*(VAR x: Item; y: Object);   (* x := x.y *)\r
+  BEGIN\r
+    IF (x.mode = Var) OR (x.mode = RegI) THEN x.a := x.a + y.val\r
+    ELSIF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); x.mode := RegI; x.r := RH; x.a := y.val; incR\r
+    END\r
+  END Field;\r
+\r
+  PROCEDURE Index*(VAR x, y: Item);   (* x := x[y] *)\r
+    VAR s: LONGINT;\r
+  BEGIN\r
+    IF y.mode = Const THEN\r
+      IF (y.a < 0) OR (y.a >= x.type.len) THEN OSS.Mark("bad index") END ;\r
+      IF x.mode = Par THEN Put2(Ldw, RH, x.r, x.a); x.mode := RegI; x.a := 0 END ;\r
+      x.a := x.a + y.a * x.type.base.size\r
+    ELSE s := x.type.base.size;\r
+      IF y.mode # Reg THEN load(y) END ;\r
+      IF s = 4 THEN Put1(Lsl, y.r, y.r, 2) ELSE Put1(Mul, y.r, y.r, s) END ;\r
+      IF x.mode = Var THEN\r
+        IF x.r > 0 THEN Put0(Add, y.r, SP, y.r) ELSE GetSB; Put0(Add, y.r, SB, y.r) END ;\r
+        x.mode := RegI; x.r := y.r\r
+      ELSIF x.mode = Par THEN\r
+        Put2(Ldw, RH, SP, x.a); Put0(Add, y.r, RH, y.r); x.mode := RegI; x.r := y.r\r
+      ELSIF x.mode = RegI THEN Put0(Add, x.r, x.r, y.r); DEC(RH)\r
+      END\r
+    END\r
+  END Index;\r
+\r
+  (* Code generation for Boolean operators *)\r
+\r
+  PROCEDURE Not*(VAR x: Item);   (* x := ~x *)\r
+    VAR t: LONGINT;\r
+  BEGIN\r
+    IF x.mode # Cond THEN loadCond(x) END ;\r
+    x.r := negated(x.r);\r
+    t := x.a;\r
+    x.a := x.b;\r
+    x.b := t\r
+  END Not;\r
+\r
+  PROCEDURE And1*(VAR x: Item);   (* x := x & *)\r
+  BEGIN\r
+    IF x.mode # Cond THEN loadCond(x) END ;\r
+    Put3(BC, negated(x.r), x.a); x.a := pc-1; FixLink(x.b); x.b := 0\r
+  END And1;\r
+\r
+  PROCEDURE And2*(VAR x, y: Item);\r
+  BEGIN\r
+    IF y.mode # Cond THEN loadCond(y) END ;\r
+    x.a := merged(y.a, x.a); x.b := y.b; x.r := y.r\r
+  END And2;\r
+\r
+  PROCEDURE Or1*(VAR x: Item);   (* x := x OR *)\r
+  BEGIN\r
+    IF x.mode # Cond THEN loadCond(x) END ;\r
+    Put3(BC, x.r, x.b);  x.b := pc-1; FixLink(x.a); x.a := 0\r
+  END Or1;\r
+\r
+  PROCEDURE Or2*(VAR x, y: Item);\r
+  BEGIN\r
+    IF y.mode # Cond THEN loadCond(y) END ;\r
+    x.a := y.a; x.b := merged(y.b, x.b); x.r := y.r\r
+  END Or2;\r
+\r
+  (* Code generation for arithmetic operators *)\r
+\r
+  PROCEDURE Neg*(VAR x: Item);   (* x := -x *)\r
+  BEGIN\r
+    IF x.mode = Const THEN x.a := -x.a\r
+    ELSE load(x); Put1(Mov, RH, 0, 0); Put0(Sub, x.r, RH, x.r)\r
+    END\r
+  END Neg;\r
+\r
+  PROCEDURE AddOp*(op: LONGINT; VAR x, y: Item);   (* x := x +- y *)\r
+  BEGIN\r
+    IF op = OSS.plus THEN\r
+      IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a + y.a\r
+      ELSIF y.mode = Const THEN load(x);\r
+        IF y.a # 0 THEN Put1(Add, x.r, x.r, y.a) END\r
+      ELSE load(x); load(y); Put0(Add, RH-2, x.r, y.r); DEC(RH); x.r := RH-1\r
+      END\r
+    ELSE (*op = OSS.minus*)\r
+      IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a - y.a\r
+      ELSIF y.mode = Const THEN load(x);\r
+        IF y.a # 0 THEN Put1(Sub, x.r, x.r, y.a) END\r
+      ELSE load(x); load(y); Put0(Sub, RH-2, x.r, y.r); DEC(RH); x.r := RH-1\r
+      END\r
+    END\r
+  END AddOp;\r
+\r
+  PROCEDURE MulOp*(VAR x, y: Item);   (* x := x * y *)\r
+  BEGIN\r
+    IF (x.mode = Const) & (y.mode = Const) THEN x.a := x.a * y.a\r
+    ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(Lsl, x.r, x.r, 1)\r
+    ELSIF y.mode = Const THEN load(x); Put1(Mul, x.r, x.r, y.a)\r
+    ELSIF x.mode = Const THEN load(y); Put1(Mul, y.r, y.r, x.a); x.mode := Reg; x.r := y.r\r
+    ELSE load(x); load(y); Put0(Mul, RH-2, x.r, y.r); DEC(RH); x.r := RH-1\r
+    END\r
+  END MulOp;\r
+\r
+  PROCEDURE DivOp*(op: LONGINT; VAR x, y: Item);   (* x := x op y *)\r
+  BEGIN\r
+    IF op = OSS.div THEN\r
+      IF (x.mode = Const) & (y.mode = Const) THEN\r
+        IF y.a > 0 THEN x.a := x.a DIV y.a ELSE OSS.Mark("bad divisor") END\r
+      ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(Asr, x.r, x.r, 1)\r
+      ELSIF y.mode = Const THEN\r
+        IF y.a > 0 THEN load(x); Put1(Div, x.r, x.r, y.a) ELSE OSS.Mark("bad divisor") END\r
+      ELSE load(y); load(x); Put0(Div, RH-2, x.r, y.r); DEC(RH); x.r := RH-1\r
+      END\r
+    ELSE (*op = OSS.mod*)\r
+      IF (x.mode = Const) & (y.mode = Const) THEN\r
+        IF y.a > 0 THEN x.a := x.a MOD y.a ELSE OSS.Mark("bad modulus") END\r
+      ELSIF (y.mode = Const) & (y.a = 2) THEN load(x); Put1(And, x.r, x.r, 1)\r
+      ELSIF y.mode = Const THEN\r
+        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\r
+      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\r
+      END\r
+    END\r
+  END DivOp;\r
+\r
+  PROCEDURE Relation*(op: INTEGER; VAR x, y: Item);   (* x := x ? y *)\r
+  BEGIN\r
+    IF y.mode = Const THEN load(x); Put1(Cmp, x.r, x.r, y.a); DEC(RH)\r
+    ELSE load(x); load(y); Put0(Cmp, x.r, x.r, y.r); DEC(RH, 2)\r
+    END ;\r
+    SetCC(x, relmap[op - OSS.eql])\r
+  END Relation;\r
+\r
+  PROCEDURE Store*(VAR x, y: Item); (* x := y *)\r
+  BEGIN load(y);\r
+    IF x.mode = Var THEN\r
+      IF x.r > 0 THEN (*local*) Put2(Stw, y.r, SP, x.a) ELSE GetSB; Put2(Stw, y.r, SB, x.a) END\r
+    ELSIF x.mode = Par THEN Put2(Ldw, RH, SP, x.a); Put2(Stw, y.r, RH, x.b)\r
+    ELSIF x.mode = RegI THEN Put2(Stw, y.r, x.r, x.a); DEC(RH)\r
+    ELSE OSS.Mark("illegal assignment")\r
+    END ;\r
+    DEC(RH)\r
+  END Store;\r
+\r
+  PROCEDURE VarParam*(VAR x: Item; ftype: Type);\r
+    VAR xmd: INTEGER;\r
+  BEGIN xmd := x.mode; loadAdr(x);\r
+    IF (ftype.form = Array) & (ftype.len < 0) THEN (*open array*)\r
+      IF x.type.len >= 0 THEN Put1(Mov, RH, 0, x.type.len) ELSE  Put2(Ldw, RH, SP, x.a+4) END ;\r
+      incR\r
+    ELSIF ftype.form = Record THEN\r
+      IF xmd = Par THEN Put2(Ldw, RH, SP, x.a+4); incR END\r
+    END\r
+  END VarParam;\r
+\r
+  PROCEDURE ValueParam*(VAR x: Item);\r
+  BEGIN load(x)\r
+  END ValueParam;\r
+\r
+  PROCEDURE OpenArrayParam*(VAR x: Item);\r
+  BEGIN loadAdr(x);\r
+    IF x.type.len >= 0 THEN Put1(Mov, RH, 0, x.type.len) ELSE Put2(Ldw, RH, SP, x.a+4) END ;\r
+    incR\r
+  END OpenArrayParam;\r
+\r
+  (*---------------------------------*)\r
+\r
+  PROCEDURE CFJump*(VAR x: Item);  (*conditional forward jump*)\r
+  BEGIN\r
+    IF x.mode # Cond THEN loadCond(x) END ;\r
+    Put3(2, negated(x.r), x.a); FixLink(x.b); x.a := pc-1\r
+  END CFJump;\r
+\r
+  PROCEDURE FJump*(VAR L: LONGINT);  (*unconditional forward jump*)\r
+  BEGIN Put3(2, 7, L); L := pc-1\r
+  END FJump;\r
+\r
+  PROCEDURE CBJump*(VAR x: Item; L: LONGINT);  (*conditional backward jump*)\r
+  BEGIN\r
+    IF x.mode # Cond THEN loadCond(x) END ;\r
+    Put3(2, negated(x.r), L-pc-1)\r
+  END CBJump;\r
+\r
+  PROCEDURE BJump*(L: LONGINT);  (*unconditional backward jump*)\r
+  BEGIN Put3(2, 7, L-pc-1)\r
+  END BJump;\r
+\r
+  PROCEDURE Call*(VAR obj: Object);\r
+  BEGIN Put3(3, 7, (obj.val DIV 4) - pc-1); RH := 0\r
+  END Call;\r
+\r
+  PROCEDURE Enter*(parblksize, locblksize: LONGINT; comd: BOOLEAN);\r
+    VAR a, r: LONGINT;\r
+  BEGIN a := 4; r := 0; Put1(Sub, SP, SP, locblksize); Put2(Stw, LNK, SP, 0);\r
+    WHILE a < parblksize DO Put2(Stw, r, SP, a); INC(r); INC(a, 4) END ;\r
+  (* IF comd THEN Put2(Ldw, SB, 0, 0) END *)\r
+  END Enter;\r
+\r
+  PROCEDURE Return*(size: LONGINT);\r
+  BEGIN Put2(Ldw, LNK, SP, 0); Put1(Add, SP, SP, size); Put3(BR, 7, LNK); RH := 0\r
+  END Return;\r
+\r
+  PROCEDURE Ord*(VAR x: Item);\r
+  BEGIN load(x); x.type := intType\r
+  END Ord;\r
+\r
+  PROCEDURE OpenInput*;\r
+  BEGIN Put3(3, 7, pc - fixlist + 101000H); fixlist := pc-1; invalSB\r
+  END OpenInput;\r
+\r
+  PROCEDURE ReadInt*(VAR x: Item);\r
+  BEGIN loadAdr(x); Put3(3, 7, pc - fixlist + 102000H); fixlist := pc-1; DEC(RH); invalSB\r
+  END ReadInt;\r
+\r
+  PROCEDURE eot*(VAR x: Item);\r
+  BEGIN Put3(3, 7, pc - fixlist + 103000H); fixlist := pc-1; Put1(Cmp, 0, 0, Texts.Int); SetCC(x, NE); invalSB\r
+  END eot;\r
+\r
+  PROCEDURE WriteChar*(VAR x: Item);\r
+  BEGIN load(x); Put3(3, 7, pc - fixlist + 104000H); fixlist:= pc-1; DEC(RH); invalSB\r
+  END WriteChar;\r
+\r
+  PROCEDURE WriteInt*(VAR x, y: Item);\r
+  BEGIN load(x); load(y); Put3(3, 7, pc - fixlist + 105000H); fixlist := pc-1; DEC(RH, 2); invalSB\r
+  END WriteInt;\r
+\r
+  PROCEDURE WriteLn*;\r
+  BEGIN Put3(3, 7, pc - fixlist + 106000H); fixlist := pc-1; invalSB\r
+  END WriteLn;\r
+\r
+  PROCEDURE Switch*(VAR x: Item);\r
+  BEGIN Put1(Mov, RH, 0, -60); Put2(Ldw, RH, RH, 0);\r
+    x.mode := Reg; x.type := intType; x.r := RH; INC(RH)\r
+  END Switch;\r
+\r
+  PROCEDURE LED*(VAR x: Item);\r
+  BEGIN load(x); Put1(Mov, RH, 0, -60); Put2(Stw, x.r, RH, 0); DEC(RH)\r
+  END LED ;\r
+\r
+  PROCEDURE Open*;\r
+  BEGIN curlev := 0; pc := 0; RH := 0; fixlist := 0; fixorgD := 0\r
+  END Open;\r
+\r
+  PROCEDURE Header*(size: LONGINT);\r
+  BEGIN entry := pc*4; Put1(Sub, SP, SP, 4); Put2(Stw, LNK, SP, 0); invalSB\r
+  END Header;\r
+\r
+  PROCEDURE MakeFileName(VAR FName: OSS.Ident; name, ext: ARRAY OF CHAR);\r
+    VAR i, j: INTEGER;\r
+  BEGIN i := 0; j := 0;  (*assume name suffix less than 4 characters*)\r
+    WHILE (i < OSS.IdLen-5) & (name[i] > 0X) DO FName[i] := name[i]; INC(i) END ;\r
+    REPEAT FName[i]:= ext[j]; INC(i); INC(j) UNTIL ext[j] = 0X;\r
+    FName[i] := 0X\r
+  END MakeFileName;\r
+\r
+  PROCEDURE Close*(VAR modid: OSS.Ident; key, datasize: LONGINT; topScope: Object);  (*write code file*)\r
+    VAR i, nofent, nofimp, comsize, size: INTEGER;\r
+      obj: Object;\r
+      name: OSS.Ident;\r
+      F: Files.File; R: Files.Rider;\r
+  BEGIN Put2(Ldw, LNK, SP, 0); Put1(Add, SP, SP, 4); Put3(BR, 7, LNK);\r
+    obj := topScope.next; comsize := 4; nofent := 1; nofimp := 1;\r
+    WHILE obj # NIL DO\r
+      IF obj.comd THEN i := 0; (*count entries and commands*)\r
+        WHILE obj.name[i] # 0X DO INC(i) END ;\r
+        i := (i+4) DIV 4 * 4; INC(comsize, i+4); INC(nofent); INC(nofimp)\r
+      END ;\r
+      obj := obj.next\r
+    END ;\r
+    size := datasize + comsize + (pc + nofimp + nofent + 1)*4;\r
+    MakeFileName(name, modid, ".rsc"); (*write code file*)\r
+    F := Files.New(name); Files.Set(R, F, 0);\r
+    Files.WriteString(R, modid); Files.WriteInt(R, key); Files.Write(R, 1X);   (*version*)\r
+    Files.WriteInt(R, size);\r
+    Files.WriteString(R, "IO"); Files.WriteInt(R, 3A8372E2H); Files.Write(R, 0X);  (*import*)\r
+    Files.WriteInt(R, 0);  (*no type descriptors*)\r
+    Files.WriteInt(R, datasize);  (*data*)\r
+    Files.WriteInt(R, 0);   (*no strings*)\r
+    Files.WriteInt(R, pc);  (*code len*)\r
+    FOR i := 0 TO pc-1 DO Files.WriteInt(R, code[i]) END ;  (*program*)\r
+    obj := topScope.next;\r
+    WHILE obj # NIL DO  (*commands*)\r
+      IF obj.comd THEN Files.WriteString(R, obj.name); Files.WriteInt(R, obj.val) END ;\r
+      obj := obj.next\r
+    END ;\r
+    Files.Write(R, 0X);\r
+    Files.WriteInt(R, nofent); Files.WriteInt(R, entry);   (*of program body*)\r
+    obj := topScope.next;\r
+    WHILE obj # NIL DO  (*entries*)\r
+      IF obj.comd THEN Files.WriteInt(R, obj.val) END ;\r
+      obj := obj.next\r
+    END ;\r
+    Files.WriteInt(R, -1);   (*no pointer variables*)\r
+    Files.WriteInt(R, fixlist); Files.WriteInt(R, fixorgD); Files.WriteInt(R, 0); Files.WriteInt(R, entry);\r
+    Files.Write(R, "O"); Files.Register(F)\r
+  END Close;\r
+\r
+  (*-------------------- output -----------------------*)\r
+\r
+  PROCEDURE WriteReg(r: LONGINT);\r
+  BEGIN Texts.Write(W, " ");\r
+    IF r < 13 THEN Texts.Write(W, "R"); Texts.WriteInt(W, r, 1)\r
+    ELSIF r = 13 THEN Texts.WriteString(W, "SB")\r
+    ELSIF r = 14 THEN Texts.WriteString(W, "SP")\r
+    ELSIF r = 15 THEN Texts.WriteString(W, "LNK")\r
+    END\r
+  END WriteReg;\r
+\r
+  PROCEDURE Decode*;\r
+    VAR i, w, a, b, c, op: LONGINT;\r
+  BEGIN Texts.WriteHex(W, code[0]); Texts.WriteHex(W, code[1]); Texts.WriteLn(W);\r
+    i := 0;\r
+    WHILE i < pc DO\r
+      w := code[i];\r
+      a := w DIV 1000000H MOD 10H;\r
+      b := w DIV 100000H MOD 10H;\r
+      Texts.WriteInt(W, i, 4); Texts.WriteHex(W, w); Texts.Write(W, 9X);\r
+      IF ASR(w, 31) = 0 THEN  (*~p:  register instruction*)\r
+        op := w DIV 10000H MOD 10H;\r
+        Texts.WriteString(W, mnemo0[op]); WriteReg(a); WriteReg(b);\r
+        IF ~ODD(w DIV 40000000H) THEN (*~q*) WriteReg(w MOD 10H)\r
+        ELSE c := w MOD 10000H;;\r
+          IF ODD(w DIV 10000000H) THEN (*v*) c := c + 0FFFF0000H END ;\r
+          Texts.WriteInt(W, c, 8)\r
+        END\r
+      ELSIF ~ODD(w DIV 40000000H) THEN  (*load/store*)\r
+        IF ODD(w DIV 20000000H) THEN Texts.WriteString(W, "STW ") ELSE Texts.WriteString(W, "LDW") END ;\r
+        WriteReg(a); WriteReg(b); Texts.WriteInt(W, w MOD 100000H, 8)\r
+      ELSE  (*Branch instr*)\r
+        Texts.Write(W, "B");\r
+        IF ODD(w DIV 10000000H) THEN Texts.Write(W, "L") END ;\r
+        Texts.WriteString(W, mnemo1[a]);\r
+        IF ~ODD(w DIV 20000000H) THEN WriteReg(w MOD 10H) ELSE\r
+          w := w MOD 1000000H;\r
+          IF w >= 800000H THEN w := w - 1000000H END ;\r
+          Texts.WriteInt(W, w, 8)\r
+        END\r
+      END ;\r
+      Texts.WriteLn(W); INC(i)\r
+    END ;\r
+    Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+  END Decode;\r
+\r
+  PROCEDURE HexCh(k: LONGINT): CHAR;\r
+  BEGIN\r
+    IF k >= 10 THEN INC(k, 27H) END ;\r
+    RETURN CHR(k+30H)\r
+  END HexCh;\r
+\r
+BEGIN Texts.OpenWriter(W);\r
+  NEW(boolType); boolType.form := Boolean; boolType.size := 4;\r
+  NEW(intType); intType.form := Integer; intType.size := 4;\r
+  relmap[0] := EQ; relmap[1] := NE; relmap[2] := LT; relmap[3] := LE; relmap[4] := GT; relmap[5] := GE;\r
+  mnemo0[Mov] := "MOV";\r
+  mnemo0[Lsl] := "LSL";\r
+  mnemo0[Asr] := "ASR";\r
+  mnemo0[Ror] := "ROR";\r
+  mnemo0[And] := "AND";\r
+  mnemo0[Ann] := "ANN";\r
+  mnemo0[Ior] := "IOR";\r
+  mnemo0[Xor] := "XOR";\r
+  mnemo0[Add] := "ADD";\r
+  mnemo0[Sub] := "SUB";\r
+  mnemo0[Mul] := "MUL";\r
+  mnemo0[Div] := "DIV";\r
+  mnemo1[PL] := "PL ";\r
+  mnemo1[MI] := "MI ";\r
+  mnemo1[EQ] := "EQ ";\r
+  mnemo1[NE] := "NE ";\r
+  mnemo1[LT] := "LT ";\r
+  mnemo1[GE] := "GE ";\r
+  mnemo1[LE] := "LE ";\r
+  mnemo1[GT] := "GT ";\r
+  mnemo1[15] := "NO ";\r
+END OSG.\r
diff --git a/cerise/oberon0/OSP.Mod b/cerise/oberon0/OSP.Mod
new file mode 100644 (file)
index 0000000..58be0a2
--- /dev/null
@@ -0,0 +1,569 @@
+MODULE OSP; (* NW 23.9.93 / 9,5.2017   OSPX*)\r
+  IMPORT Texts, Oberon, OSS, OSG;\r
+\r
+  CONST WordSize = 4;\r
+  VAR sym, level: INTEGER;\r
+    topScope, universe, dummy: OSG.Object;\r
+    expression: PROCEDURE (VAR x: OSG.Item);  (*to avoid forward reference*)\r
+    W: Texts.Writer;\r
+\r
+  PROCEDURE NewObj(VAR obj: OSG.Object; class: INTEGER);\r
+    VAR new, x: OSG.Object;\r
+  BEGIN x := topScope;\r
+    WHILE (x.next # NIL) & (x.next.name # OSS.id) DO x := x.next END ;\r
+    IF x.next = NIL THEN\r
+      NEW(new); new.name := OSS.id; new.class := class; new.next := NIL;\r
+      x.next := new; obj := new\r
+    ELSE obj := x.next; OSS.Mark("mult def")\r
+    END\r
+  END NewObj;\r
+\r
+  PROCEDURE find(VAR obj: OSG.Object);\r
+    VAR s, x: OSG.Object;\r
+  BEGIN s := topScope;\r
+    REPEAT x := s.next;\r
+      WHILE (x # NIL) & (x.name # OSS.id) DO x := x.next END ;\r
+       s := s.dsc\r
+    UNTIL (x # NIL) OR (s = NIL);\r
+    IF x = NIL THEN x := dummy; OSS.Mark("undef") END ;\r
+    obj := x\r
+  END find;\r
+\r
+  PROCEDURE FindField(VAR obj: OSG.Object; list: OSG.Object);\r
+  BEGIN\r
+    WHILE (list # NIL) & (list.name # OSS.id) DO list := list.next END ;\r
+    IF list # NIL THEN obj := list ELSE OSS.Mark("undef"); obj := dummy END\r
+  END FindField;\r
+\r
+  PROCEDURE Check(s: INTEGER; msg: ARRAY OF CHAR);\r
+  BEGIN\r
+    IF sym = s THEN OSS.Get(sym) ELSE OSS.Mark(msg) END\r
+  END Check;\r
+\r
+  PROCEDURE CheckInt(VAR x: OSG.Item);\r
+  BEGIN\r
+    IF x.type.form # OSG.Integer THEN OSS.Mark("not integer") END\r
+  END CheckInt;\r
+\r
+  PROCEDURE CheckBool(VAR x: OSG.Item);\r
+  BEGIN\r
+    IF x.type.form # OSG.Boolean THEN OSS.Mark("not Boolean") END\r
+  END CheckBool;\r
+\r
+  PROCEDURE OpenScope;\r
+    VAR s: OSG.Object;\r
+  BEGIN NEW(s); s.class := OSG.Head; s.dsc := topScope; s.next := NIL; topScope := s\r
+  END OpenScope;\r
+\r
+  PROCEDURE CloseScope;\r
+  BEGIN topScope := topScope.dsc\r
+  END CloseScope;\r
+\r
+  (* -------------------- Parser ---------------------*)\r
+\r
+  PROCEDURE selector(VAR x: OSG.Item);\r
+    VAR y: OSG.Item; obj: OSG.Object;\r
+  BEGIN\r
+    WHILE (sym = OSS.lbrak) OR (sym = OSS.period) DO\r
+      IF sym = OSS.lbrak THEN\r
+        OSS.Get(sym); expression(y);\r
+        IF x.type.form = OSG.Array THEN\r
+          CheckInt(y); OSG.Index(x, y); x.type := x.type.base\r
+        ELSE OSS.Mark("not an array")\r
+        END ;\r
+        Check(OSS.rbrak, "no ]")\r
+      ELSE (*period*) OSS.Get(sym);\r
+        IF sym = OSS.ident THEN\r
+          IF x.type.form = OSG.Record THEN\r
+            FindField(obj, x.type.dsc); OSS.Get(sym);\r
+            IF obj # NIL THEN OSG.Field(x, obj); x.type := obj.type END\r
+          ELSE OSS.Mark("not a record")\r
+          END\r
+        ELSE OSS.Mark("ident?")\r
+        END\r
+      END\r
+    END\r
+  END selector;\r
+\r
+  PROCEDURE CompTypes(t0, t1: OSG.Type): BOOLEAN;\r
+  BEGIN (*Compatible Types*)\r
+    RETURN (t0 = t1)\r
+      OR (t0.form = OSG.Array) & (t1.form = OSG.Array) & CompTypes(t0.base, t1.base)\r
+  END CompTypes;\r
+\r
+  PROCEDURE Parameter(par: OSG.Object);\r
+    VAR x: OSG.Item; varpar: BOOLEAN;\r
+  BEGIN expression(x);\r
+    IF par # NIL THEN\r
+      varpar := par.class = OSG.Par;\r
+      IF CompTypes(par.type, x.type) THEN\r
+        IF ~varpar THEN OSG.ValueParam(x)\r
+        ELSE OSG.VarParam(x, par.type)\r
+        END\r
+      ELSIF (x.type.form = OSG.Array) & (par.type.form = OSG.Array) &\r
+          (x.type.base.form = par.type.base.form) & (par.type.len < 0) THEN\r
+        OSG.OpenArrayParam(x)\r
+      ELSE OSS.Mark("incompatible parameters")\r
+      END\r
+    END\r
+  END Parameter;\r
+\r
+  PROCEDURE ParamList(VAR obj: OSG.Object);\r
+    VAR n: INTEGER; par: OSG.Object;\r
+  BEGIN par := obj.dsc; n := 0;\r
+    IF sym # OSS.rparen THEN\r
+      Parameter(par); n := 1;\r
+      WHILE sym <= OSS.comma DO\r
+        Check(sym, "comma?");\r
+        IF par # NIL THEN par := par.next END ;\r
+        INC(n); Parameter(par)\r
+      END ;\r
+      Check(OSS.rparen, ") missing")\r
+    ELSE OSS.Get(sym);\r
+    END ;\r
+    IF n < obj.nofpar THEN OSS.Mark("too few params")\r
+    ELSIF n > obj.nofpar THEN OSS.Mark("too many params")\r
+    END\r
+  END ParamList;\r
+\r
+  PROCEDURE StandFunc(VAR x: OSG.Item; fctno: LONGINT);\r
+    VAR y, z: OSG.Item;\r
+  BEGIN\r
+    IF sym = OSS.lparen THEN\r
+      OSS.Get(sym);\r
+      IF fctno = 0 THEN (*ORD*) expression(x); OSG.Ord(x)\r
+      ELSIF fctno = 1 THEN (*eot*) OSG.eot(x)\r
+      ELSE (*fctno = 2*) OSG.Switch(x)\r
+      END ;\r
+      IF sym = OSS.rparen THEN OSS.Get(sym) ELSE OSS.Mark("rparen expected") END\r
+    ELSE OSS.Mark("param missing"); OSG.MakeConstItem(x, OSG.intType, 0)\r
+    END\r
+  END StandFunc;\r
+\r
+  PROCEDURE factor(VAR x: OSG.Item);\r
+    VAR obj: OSG.Object;\r
+  BEGIN (*sync*)\r
+    IF (sym < OSS.char) OR (sym > OSS.ident) THEN\r
+        OSS.Mark("expression expected");\r
+        REPEAT OSS.Get(sym) UNTIL (sym >= OSS.int) & (sym <= OSS.ident)\r
+    END ;\r
+    IF sym = OSS.ident THEN\r
+      find(obj);\r
+      OSS.Get(sym);\r
+      IF obj.class = OSG.SFunc THEN\r
+        IF obj.type = NIL THEN\r
+            OSS.Mark("not a function");\r
+            obj.type := OSG.intType\r
+        END ;\r
+        StandFunc(x, obj.val); x.type := obj.type\r
+      ELSE\r
+        OSG.MakeItem(x, obj, level);\r
+        selector(x)\r
+      END\r
+    ELSIF sym = OSS.int THEN\r
+        OSG.MakeConstItem(x, OSG.intType, OSS.val);\r
+        OSS.Get(sym)\r
+    ELSIF sym = OSS.char THEN\r
+        OSG.MakeConstItem(x, OSG.intType, OSS.val);\r
+        OSS.Get(sym)\r
+    ELSIF sym = OSS.lparen THEN\r
+      OSS.Get(sym);\r
+      IF sym # OSS.rparen THEN expression(x) END ;\r
+      Check(OSS.rparen, "no )")\r
+    ELSIF sym = OSS.not THEN\r
+        OSS.Get(sym);\r
+        factor(x);\r
+        CheckBool(x);\r
+        OSG.Not(x)\r
+    ELSIF sym = OSS.false THEN\r
+        OSS.Get(sym);\r
+        OSG.MakeConstItem(x, OSG.boolType, 0)\r
+    ELSIF sym = OSS.true THEN\r
+        OSS.Get(sym);\r
+        OSG.MakeConstItem(x, OSG.boolType, 1)\r
+    ELSE\r
+        OSS.Mark("factor?");\r
+        OSG.MakeItem(x, dummy, level)\r
+    END\r
+  END factor;\r
+\r
+  PROCEDURE term(VAR x: OSG.Item);\r
+    VAR y: OSG.Item; op: INTEGER;\r
+  BEGIN factor(x);\r
+    WHILE (sym >= OSS.times) & (sym <= OSS.and) DO\r
+      op := sym; OSS.Get(sym);\r
+      IF op = OSS.times THEN\r
+        CheckInt(x);\r
+        factor(y);\r
+        CheckInt(y);\r
+        OSG.MulOp(x, y)\r
+      ELSIF (op = OSS.div) OR (op = OSS.mod) THEN\r
+        CheckInt(x);\r
+        factor(y);\r
+        CheckInt(y);\r
+        OSG.DivOp(op, x, y)\r
+      ELSE (*op = and*)\r
+        CheckBool(x);\r
+        OSG.And1(x);\r
+        factor(y);\r
+        CheckBool(y);\r
+        OSG.And2(x, y)\r
+      END\r
+    END\r
+  END term;\r
+\r
+  PROCEDURE SimpleExpression(VAR x: OSG.Item);\r
+    VAR y: OSG.Item; op: INTEGER;\r
+  BEGIN\r
+    IF sym = OSS.plus THEN\r
+        OSS.Get(sym);\r
+        term(x);\r
+        CheckInt(x)\r
+    ELSIF sym = OSS.minus THEN\r
+        OSS.Get(sym);\r
+        term(x);\r
+        CheckInt(x);\r
+        OSG.Neg(x)\r
+    ELSE\r
+        term(x)\r
+    END;\r
+\r
+    WHILE (sym >= OSS.plus) & (sym <= OSS.or) DO\r
+      op := sym; OSS.Get(sym);\r
+      IF op = OSS.or THEN\r
+        OSG.Or1(x);\r
+        CheckBool(x);\r
+        term(y);\r
+        CheckBool(y);\r
+        OSG.Or2(x, y)\r
+      ELSE\r
+        CheckInt(x);\r
+        term(y);\r
+        CheckInt(y);\r
+        OSG.AddOp(op, x, y)\r
+      END\r
+    END\r
+  END SimpleExpression;\r
+\r
+  PROCEDURE expression0(VAR x: OSG.Item);\r
+    VAR y: OSG.Item; op: INTEGER;\r
+  BEGIN SimpleExpression(x);\r
+    IF (sym >= OSS.eql) & (sym <= OSS.geq) THEN\r
+      op := sym;\r
+      OSS.Get(sym);\r
+      SimpleExpression(y);\r
+      IF x.type = y.type THEN\r
+        OSG.Relation(op, x, y)\r
+      ELSE\r
+        OSS.Mark("incompatible types")\r
+      END ;\r
+      x.type := OSG.boolType\r
+    END\r
+  END expression0;\r
+\r
+  PROCEDURE StandProc(pno: LONGINT);\r
+    VAR x, y: OSG.Item;\r
+  BEGIN\r
+    IF pno = 0 THEN  OSG.OpenInput\r
+    ELSIF pno IN {1, 2, 3, 5} THEN\r
+      IF sym = OSS.lparen THEN OSS.Get(sym); expression(x);\r
+        IF pno = 1 THEN OSG.ReadInt(x);\r
+        ELSIF pno = 2 THEN\r
+          IF sym = OSS.comma THEN OSS.Get(sym); expression(y); OSG.WriteInt(x, y) ELSE OSS.Mark("no comma") END\r
+        ELSIF pno = 3 THEN OSG.WriteChar(x)\r
+        ELSIF pno = 5 THEN OSG.LED(x)\r
+            END ;\r
+            IF sym = OSS.rparen THEN OSS.Get(sym) ELSE OSS.Mark("no rparen") END\r
+          ELSE OSS.Mark(" missing lparen")\r
+          END\r
+    ELSIF pno = 4 THEN  OSG.WriteLn\r
+    ELSE OSS.Mark("undef proc")\r
+    END\r
+  END StandProc;\r
+\r
+  PROCEDURE StatSequence;\r
+    VAR par, obj: OSG.Object; x, y: OSG.Item; n, L: LONGINT;\r
+  BEGIN (* StatSequence *)\r
+    REPEAT (*sync*) obj := NIL;\r
+      IF ~((sym = OSS.ident) OR (sym >= OSS.if) & (sym <= OSS.repeat) OR (sym >= OSS.semicolon)) THEN\r
+        OSS.Mark("statement expected");\r
+        REPEAT OSS.Get(sym) UNTIL (sym = OSS.ident) OR (sym >= OSS.if)\r
+      END ;\r
+      IF sym = OSS.ident THEN\r
+        find(obj); OSS.Get(sym);\r
+        IF obj.class = OSG.SProc THEN StandProc(obj.val)\r
+        ELSE OSG.MakeItem(x, obj, level); selector(x);\r
+          IF sym = OSS.becomes THEN (*assignment*)\r
+            OSS.Get(sym); expression(y);\r
+            IF (x.type.form IN {OSG.Boolean, OSG.Integer}) & (x.type.form = y.type.form) THEN OSG.Store(x, y)\r
+            ELSE OSS.Mark("incompatible assignment")\r
+            END\r
+          ELSIF sym = OSS.eql THEN OSS.Mark("should be :="); OSS.Get(sym); expression(y)\r
+          ELSIF sym = OSS.lparen THEN (*procedure call*)\r
+            OSS.Get(sym);\r
+            IF (obj.class = OSG.Proc) & (obj.type = NIL) THEN ParamList(obj); OSG.Call(obj);\r
+            ELSE OSS.Mark("not a procedure")\r
+            END\r
+          ELSIF obj.class = OSG.Proc THEN (*procedure call without parameters*)\r
+            IF obj.nofpar > 0 THEN OSS.Mark("missing parameters") END ;\r
+            IF obj.type = NIL THEN OSG.Call(obj) ELSE OSS.Mark("not a procedure") END\r
+          ELSIF (obj.class = OSG.SProc) & (obj.val = 3) THEN OSG.WriteLn\r
+          ELSIF obj.class = OSG.Typ THEN OSS.Mark("illegal assignment")\r
+          ELSE OSS.Mark("not a procedure")\r
+          END\r
+        END\r
+      ELSIF sym = OSS.if THEN\r
+        OSS.Get(sym); expression(x); CheckBool(x); OSG.CFJump(x); Check(OSS.then, "no THEN");\r
+        StatSequence; L := 0;\r
+        WHILE sym = OSS.elsif DO\r
+          OSS.Get(sym); OSG.FJump(L); OSG.FixLink(x.a); expression(x); CheckBool(x); OSG.CFJump(x);\r
+          IF sym = OSS.then THEN OSS.Get(sym) ELSE OSS.Mark("THEN?") END ;\r
+          StatSequence\r
+        END ;\r
+        IF sym = OSS.else THEN\r
+          OSS.Get(sym); OSG.FJump(L); OSG.FixLink(x.a); StatSequence\r
+        ELSE OSG.FixLink(x.a)\r
+        END ;\r
+        OSG.FixLink(L);\r
+        IF sym = OSS.end THEN OSS.Get(sym) ELSE OSS.Mark("END?") END\r
+      ELSIF sym = OSS.while THEN\r
+        OSS.Get(sym); L := OSG.pc; expression(x); CheckBool(x); OSG.CFJump(x);\r
+        Check(OSS.do, "no DO"); StatSequence; OSG.BJump(L); OSG.FixLink(x.a);\r
+        Check(OSS.end, "no END")\r
+      ELSIF sym = OSS.repeat THEN\r
+        OSS.Get(sym); L := OSG.pc; StatSequence;\r
+        IF sym = OSS.until THEN\r
+          OSS.Get(sym); expression(x); CheckBool(x); OSG.CBJump(x, L)\r
+        ELSE OSS.Mark("missing UNTIL"); OSS.Get(sym)\r
+        END\r
+      END ;\r
+      OSG.CheckRegs;\r
+      IF sym = OSS.semicolon THEN OSS.Get(sym)\r
+      ELSIF sym < OSS.semicolon THEN OSS.Mark("missing semicolon?")\r
+      END\r
+    UNTIL sym > OSS.semicolon\r
+  END StatSequence;\r
+\r
+  PROCEDURE IdentList(class: INTEGER; VAR first: OSG.Object);\r
+    VAR obj: OSG.Object;\r
+  BEGIN\r
+    IF sym = OSS.ident THEN\r
+      NewObj(first, class); OSS.Get(sym);\r
+      WHILE sym = OSS.comma DO\r
+        OSS.Get(sym);\r
+        IF sym = OSS.ident THEN NewObj(obj, class); OSS.Get(sym)\r
+        ELSE OSS.Mark("ident?")\r
+        END\r
+      END;\r
+      Check(OSS.colon, "no :")\r
+    END\r
+  END IdentList;\r
+\r
+  PROCEDURE Type(VAR type: OSG.Type);\r
+    VAR obj, first: OSG.Object; x: OSG.Item; tp: OSG.Type;\r
+  BEGIN type := OSG.intType; (*sync*)\r
+    IF (sym # OSS.ident) & (sym < OSS.array) THEN OSS.Mark("type?");\r
+      REPEAT OSS.Get(sym) UNTIL (sym = OSS.ident) OR (sym >= OSS.array)\r
+    END ;\r
+    IF sym = OSS.ident THEN\r
+      find(obj); OSS.Get(sym);\r
+      IF obj.class = OSG.Typ THEN type := obj.type ELSE OSS.Mark("type?") END\r
+    ELSIF sym = OSS.array THEN\r
+      OSS.Get(sym); expression(x);\r
+      IF (x.mode # OSG.Const) OR (x.a < 0) THEN OSS.Mark("bad index") END ;\r
+      IF sym = OSS.of THEN OSS.Get(sym) ELSE OSS.Mark("OF?") END ;\r
+      Type(tp); NEW(type); type.form := OSG.Array; type.base := tp;\r
+      type.len := x.a; type.size := type.len * tp.size\r
+    ELSIF sym = OSS.record THEN\r
+      OSS.Get(sym); NEW(type); type.form := OSG.Record; type.size := 0; OpenScope;\r
+      REPEAT\r
+        IF sym = OSS.ident THEN\r
+          IdentList(OSG.Fld, first); Type(tp); obj := first;\r
+          WHILE obj # NIL DO\r
+            obj.type := tp; obj.val := type.size; type.size := type.size + obj.type.size; obj := obj.next\r
+          END\r
+        END ;\r
+        IF sym = OSS.semicolon THEN OSS.Get(sym)\r
+        ELSIF sym = OSS.ident THEN OSS.Mark("; ?")\r
+        END\r
+      UNTIL sym # OSS.ident;\r
+      type.dsc := topScope.next; CloseScope; Check(OSS.end, "no END")\r
+    ELSE OSS.Mark("ident?")\r
+    END\r
+  END Type;\r
+\r
+  PROCEDURE Declarations(VAR varsize: LONGINT);\r
+    VAR obj, first: OSG.Object;\r
+      x: OSG.Item; tp: OSG.Type; L: LONGINT;\r
+  BEGIN (*sync*)\r
+    IF (sym < OSS.const) & (sym # OSS.end) THEN\r
+      (* error and attempt recovery *)\r
+      OSS.Mark("declaration?");\r
+      REPEAT OSS.Get(sym) UNTIL (sym >= OSS.const) OR (sym = OSS.end)\r
+    END ;\r
+\r
+    IF sym = OSS.const THEN\r
+      OSS.Get(sym);\r
+      WHILE sym = OSS.ident DO\r
+        NewObj(obj, OSG.Const); OSS.Get(sym);\r
+        IF sym = OSS.eql THEN OSS.Get(sym) ELSE OSS.Mark("=?") END;\r
+        expression(x);\r
+        IF x.mode = OSG.Const THEN\r
+            obj.val := x.a; obj.type := x.type\r
+        ELSE\r
+            OSS.Mark("expression not constant")\r
+        END ;\r
+        Check(OSS.semicolon, "; expected")\r
+      END\r
+    END ;\r
+\r
+    IF sym = OSS.type THEN\r
+      OSS.Get(sym);\r
+      WHILE sym = OSS.ident DO\r
+        NewObj(obj, OSG.Typ); OSS.Get(sym);\r
+        IF sym = OSS.eql THEN OSS.Get(sym) ELSE OSS.Mark("=?") END ;\r
+        Type(obj.type); Check(OSS.semicolon, "; expected")\r
+      END\r
+    END ;\r
+\r
+    IF sym = OSS.var THEN\r
+      OSS.Get(sym);\r
+      WHILE sym = OSS.ident DO\r
+        IdentList(OSG.Var, first); Type(tp);\r
+        obj := first;\r
+        WHILE obj # NIL DO\r
+          obj.type := tp; obj.lev := level;\r
+          obj.val := varsize; varsize := varsize + obj.type.size; obj := obj.next\r
+        END ;\r
+        Check(OSS.semicolon, "; expected")\r
+      END\r
+    END ;\r
+    IF (sym >= OSS.const) & (sym <= OSS.var) THEN OSS.Mark("declaration in bad order") END\r
+  END Declarations;\r
+\r
+  PROCEDURE ProcedureDecl;\r
+    CONST marksize = 4;\r
+    VAR proc, obj: OSG.Object;\r
+      procid: OSS.Ident;\r
+      nofpar: INTEGER;\r
+      locblksize, parblksize: LONGINT;\r
+\r
+    PROCEDURE FPSection(VAR adr: LONGINT; VAR nofpar: INTEGER);\r
+      VAR obj, first: OSG.Object; tp: OSG.Type; parsize: LONGINT;\r
+    BEGIN\r
+      IF sym = OSS.var THEN OSS.Get(sym); IdentList(OSG.Par, first)\r
+      ELSE IdentList(OSG.Var, first)\r
+      END ;\r
+      IF sym = OSS.ident THEN\r
+        find(obj); OSS.Get(sym);\r
+        IF obj.class = OSG.Typ THEN tp := obj.type ELSE OSS.Mark("type?"); tp := OSG.intType END\r
+      ELSE OSS.Mark("ident?"); tp := OSG.intType\r
+      END ;\r
+      IF first.class = OSG.Var THEN\r
+        parsize := tp.size;\r
+        IF tp.form >= OSG.Array THEN OSS.Mark("no struct params") END ;\r
+      ELSE parsize := WordSize\r
+      END ;\r
+      obj := first;\r
+      WHILE obj # NIL DO\r
+        INC(nofpar); obj.type := tp; obj.lev := level; obj.val := adr; adr := adr + parsize;\r
+        obj := obj.next\r
+      END\r
+    END FPSection;\r
+\r
+  BEGIN (* ProcedureDecl *) OSS.Get(sym);\r
+    IF sym = OSS.ident THEN\r
+      procid := OSS.id; NewObj(proc, OSG.Proc); OSS.Get(sym); parblksize := marksize; nofpar := 0;\r
+    (* Texts.Write(W, "%"); Texts.WriteInt(W, sym, 4); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); *)\r
+      OpenScope;  INC(level); proc.val := -1;\r
+      IF sym = OSS.times THEN proc.comd := TRUE; OSS.Get(sym) ELSE proc.comd := FALSE END ;\r
+      IF sym = OSS.lparen THEN\r
+        OSS.Get(sym);\r
+        IF sym = OSS.rparen THEN OSS.Get(sym)\r
+        ELSE FPSection(parblksize, nofpar);\r
+          WHILE sym = OSS.semicolon DO OSS.Get(sym); FPSection(parblksize, nofpar) END ;\r
+          IF sym = OSS.rparen THEN OSS.Get(sym) ELSE OSS.Mark(")?") END ;\r
+          IF proc.comd THEN OSS.Mark("no params allowed") END\r
+        END\r
+      END ;\r
+      locblksize := parblksize; proc.type := NIL; proc.dsc := topScope.next; proc.nofpar := nofpar;\r
+      Check(OSS.semicolon, "; expected");\r
+      Declarations(locblksize); proc.dsc := topScope.next;\r
+      WHILE sym = OSS.procedure DO\r
+        ProcedureDecl; Check(OSS.semicolon, "; expected")\r
+      END ;\r
+      proc.val := OSG.pc * 4; OSG.Enter(parblksize, locblksize, proc.comd);\r
+      IF sym = OSS.begin THEN OSS.Get(sym); StatSequence END ;\r
+      Check(OSS.end, "no END");\r
+      IF sym = OSS.ident THEN\r
+        IF procid # OSS.id THEN OSS.Mark("no match") END ;\r
+        OSS.Get(sym)\r
+      END ;\r
+      OSG.Return(locblksize); DEC(level); CloseScope\r
+    END\r
+  END ProcedureDecl;\r
+\r
+  PROCEDURE Module;\r
+    VAR modid: OSS.Ident; dc: LONGINT;\r
+  BEGIN\r
+    Texts.WriteString(W, "  compiling ");\r
+    IF sym = OSS.module THEN\r
+      OSS.Get(sym); OSG.Open; OpenScope; dc := 0; level := 0;\r
+      IF sym = OSS.ident THEN\r
+        modid := OSS.id; OSS.Get(sym);\r
+        Texts.WriteString(W, modid); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+      ELSE\r
+        OSS.Mark("ident?")\r
+      END ;\r
+      Check(OSS.semicolon, "; expected");\r
+      Declarations(dc);\r
+      WHILE sym = OSS.procedure DO ProcedureDecl; Check(OSS.semicolon, "; expected") END ;\r
+      OSG.Header(dc);\r
+      IF sym = OSS.begin THEN OSS.Get(sym); StatSequence END ;\r
+      Check(OSS.end, "no END");\r
+      IF sym = OSS.ident THEN\r
+        IF modid # OSS.id THEN OSS.Mark("no match") END ;\r
+        OSS.Get(sym)\r
+      ELSE OSS.Mark("ident?")\r
+      END ;\r
+      IF sym # OSS.period THEN OSS.Mark(". ?") END ;\r
+      IF ~OSS.error THEN\r
+        OSG.Close(modid, 1, dc, topScope); Texts.WriteString(W, "code generated "); Texts.WriteString(W, modid);\r
+        Texts.WriteInt(W, OSG.pc, 6); Texts.WriteInt(W, dc, 6); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+      END ;\r
+      CloseScope\r
+    ELSE OSS.Mark("MODULE?")\r
+    END\r
+  END Module;\r
+\r
+  PROCEDURE Compile*;\r
+    VAR beg, end, time: LONGINT; T: Texts.Text;\r
+  BEGIN Oberon.GetSelection(T, beg, end, time);\r
+    IF time >= 0 THEN OSS.Init(T, beg); OSS.Get(sym); Module END\r
+  END Compile;\r
+\r
+  PROCEDURE enter(name: ARRAY OF CHAR; cl: INTEGER; n: LONGINT;  type: OSG.Type);\r
+    VAR obj: OSG.Object;\r
+  BEGIN NEW(obj);\r
+    obj.class := cl; obj.val := n; obj.name := name; obj.type := type; obj.dsc := NIL;\r
+    obj.next := topScope.next; topScope.next := obj\r
+  END enter;\r
+\r
+BEGIN Texts.OpenWriter(W); Texts.WriteString(W, "Oberon-0 Compiler OSP  9.5.2017");\r
+  Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf);\r
+  NEW(dummy); dummy.class := OSG.Var; dummy.type := OSG.intType; dummy.val := 0;\r
+  expression := expression0;\r
+  topScope := NIL; OpenScope;;\r
+  enter("ORD", OSG.SFunc, 0, OSG.intType);\r
+  enter("eot", OSG.SFunc, 1, OSG.boolType);\r
+  enter("Switch", OSG.SFunc, 2, OSG.intType);\r
+  enter("OpenInput", OSG.SProc, 0, NIL);\r
+  enter("ReadInt", OSG.SProc, 1, NIL);\r
+  enter("WriteInt", OSG.SProc, 2, NIL);\r
+  enter("WriteChar", OSG.SProc, 3, NIL);\r
+  enter("WriteLn", OSG.SProc, 4, NIL);\r
+  enter("LED", OSG.SProc, 5, NIL);\r
+  enter("BOOLEAN", OSG.Typ, 0, OSG.boolType);\r
+  enter("INTEGER", OSG.Typ, 1, OSG.intType);\r
+  universe := topScope\r
+END OSP.\r
diff --git a/cerise/oberon0/OSS.Mod b/cerise/oberon0/OSS.Mod
new file mode 100644 (file)
index 0000000..2bc77b9
--- /dev/null
@@ -0,0 +1,178 @@
+MODULE OSS; (* NW 19.9.93 / 17.11.94 / 1.11.2013*)\r
+  IMPORT Texts, Oberon;\r
+\r
+  CONST IdLen* = 16; KW = 34; maxInt = 2147483647;\r
+\r
+    (*lexical symbols of Oberon*)\r
+    null = 0; times* = 1; div* = 3; mod* = 4;\r
+    and* = 5; plus* = 6; minus* = 7; or* = 8; eql* = 9;\r
+    neq* = 10; lss* = 11; leq* = 12; gtr* = 13; geq* = 14;\r
+    period* = 18; char* = 20; int* = 21; false* = 23; true* = 24;\r
+    not* = 27; lparen* = 28; lbrak* = 29;\r
+    ident* = 31; if* = 32; while* = 34;\r
+    repeat* = 35;\r
+    comma* = 40; colon* = 41; becomes* = 42; rparen* = 44;\r
+    rbrak* = 45; then* = 47; of* = 48; do* = 49;\r
+    semicolon* = 52; end* = 53; \r
+    else* = 55; elsif* = 56; until* = 57; \r
+    array* = 60; record* = 61; const* = 63; type* = 64;\r
+    var* = 65; procedure* = 66; begin* = 67;  module* = 69;\r
+    eof = 70;\r
+\r
+  TYPE Ident* = ARRAY IdLen OF CHAR;\r
+\r
+  VAR val*: LONGINT;\r
+    id*: Ident;\r
+    error*: BOOLEAN;\r
+\r
+    ch: CHAR;\r
+    nkw: INTEGER;\r
+    errpos: LONGINT;\r
+    R: Texts.Reader;\r
+    W: Texts.Writer;\r
+    keyTab: ARRAY KW OF  (*keywords of Oberon*)\r
+        RECORD sym: INTEGER; id: ARRAY 12 OF CHAR END;\r
+\r
+  PROCEDURE Mark*(msg: ARRAY OF CHAR);\r
+    VAR p: LONGINT;\r
+  BEGIN p := Texts.Pos(R) - 1;\r
+    IF p > errpos THEN\r
+      Texts.WriteString(W, "  pos "); Texts.WriteInt(W, p, 1);\r
+      Texts.Write(W, " "); Texts.WriteString(W, msg);\r
+      Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+    END ;\r
+    errpos := p; error := TRUE\r
+  END Mark;\r
+\r
+  PROCEDURE Identifier(VAR sym: INTEGER);\r
+    VAR i, k: INTEGER;\r
+  BEGIN i := 0;\r
+    REPEAT\r
+      IF i < IdLen THEN id[i] := ch; INC(i) END ;\r
+      Texts.Read(R, ch)\r
+    UNTIL (ch < "0") OR (ch > "9") & (ch < "A") OR (ch > "Z") & (ch < "a") OR (ch > "z");\r
+    id[i] := 0X; k := 0;\r
+    WHILE (k < nkw) & (id # keyTab[k].id) DO INC(k) END ;\r
+    IF k < nkw THEN sym := keyTab[k].sym ELSE sym := ident END\r
+  END Identifier;\r
+\r
+  PROCEDURE Number(VAR sym: INTEGER);\r
+  BEGIN val := 0; sym := int;\r
+    REPEAT\r
+      IF val <= (maxInt - ORD(ch) + ORD("0")) DIV 10 THEN\r
+        val := 10 * val + (ORD(ch) - ORD("0"))\r
+      ELSE Mark("number too large"); val := 0\r
+      END ;\r
+      Texts.Read(R, ch)\r
+    UNTIL (ch < "0") OR (ch > "9")\r
+  END Number;\r
+\r
+  PROCEDURE comment;\r
+  BEGIN\r
+    REPEAT\r
+      REPEAT Texts.Read(R, ch);\r
+        WHILE ch = "(" DO Texts.Read(R, ch);\r
+          IF ch = "*" THEN comment END\r
+        END ;\r
+      UNTIL (ch = "*") OR R.eot;\r
+      REPEAT Texts.Read(R, ch) UNTIL (ch # "*") OR R.eot\r
+    UNTIL (ch = ")") OR R.eot;\r
+    IF ~R.eot THEN Texts.Read(R, ch) ELSE Mark("comment not terminated") END\r
+  END comment;\r
+\r
+  PROCEDURE Get*(VAR sym: INTEGER);\r
+  BEGIN\r
+    REPEAT\r
+      WHILE ~R.eot & (ch <= " ") DO Texts.Read(R, ch) END;\r
+        IF ch < "A" THEN\r
+        IF ch < "0" THEN\r
+          IF ch = 22X THEN\r
+            Texts.Read(R, ch); val := ORD(ch);\r
+            REPEAT Texts.Read(R, ch) UNTIL (ch = 22X) OR R.eot;\r
+            Texts.Read(R, ch); sym := char\r
+          ELSIF ch = "#" THEN Texts.Read(R, ch); sym := neq\r
+          ELSIF ch = "&" THEN Texts.Read(R, ch); sym := and\r
+          ELSIF ch = "(" THEN Texts.Read(R, ch); \r
+            IF ch = "*" THEN sym := null; comment ELSE sym := lparen END\r
+          ELSIF ch = ")" THEN Texts.Read(R, ch); sym := rparen\r
+          ELSIF ch = "*" THEN Texts.Read(R, ch); sym := times\r
+          ELSIF ch = "+" THEN Texts.Read(R, ch); sym := plus\r
+          ELSIF ch = "," THEN Texts.Read(R, ch); sym := comma\r
+          ELSIF ch = "-" THEN Texts.Read(R, ch); sym := minus\r
+          ELSIF ch = "." THEN Texts.Read(R, ch); sym := period\r
+          ELSIF ch = "/" THEN Texts.Read(R, ch); sym := null\r
+          ELSE Texts.Read(R, ch); (* ! $ % *) sym := null\r
+          END\r
+        ELSIF ch < ":" THEN Number(sym)\r
+        ELSIF ch = ":" THEN Texts.Read(R, ch);\r
+          IF ch = "=" THEN Texts.Read(R, ch); sym := becomes ELSE sym := colon END \r
+        ELSIF ch = ";" THEN Texts.Read(R, ch); sym := semicolon\r
+        ELSIF ch = "<" THEN  Texts.Read(R, ch);\r
+          IF ch = "=" THEN Texts.Read(R, ch); sym := leq ELSE sym := lss END\r
+        ELSIF ch = "=" THEN Texts.Read(R, ch); sym := eql\r
+        ELSIF ch = ">" THEN Texts.Read(R, ch);\r
+          IF ch = "=" THEN Texts.Read(R, ch); sym := geq ELSE sym := gtr END\r
+        ELSE (* ? @ *) Texts.Read(R, ch); sym := null\r
+        END\r
+      ELSIF ch < "[" THEN Identifier(sym)\r
+      ELSIF ch < "a" THEN\r
+        IF ch = "[" THEN sym := lbrak\r
+        ELSIF ch = "]" THEN  sym := rbrak\r
+        ELSIF ch = "^" THEN sym := null\r
+        ELSE (* _ ` *) sym := null\r
+        END ;\r
+        Texts.Read(R, ch)\r
+      ELSIF ch < "{" THEN Identifier(sym) ELSE\r
+        IF ch = "{" THEN sym := null\r
+        ELSIF ch = "}" THEN sym := null\r
+        ELSIF ch = "|" THEN sym := null\r
+        ELSIF ch = "~" THEN  sym := not\r
+        ELSE sym := null\r
+        END ;\r
+        Texts.Read(R, ch)\r
+      END\r
+    UNTIL sym # null\r
+  END Get;\r
+\r
+  PROCEDURE Init*(T: Texts.Text; pos: LONGINT);\r
+  BEGIN error := FALSE; errpos := pos; Texts.OpenReader(R, T, pos); Texts.Read(R, ch)\r
+  END Init;\r
+  \r
+  PROCEDURE EnterKW(sym: INTEGER; name: ARRAY OF CHAR);\r
+  BEGIN keyTab[nkw].sym := sym; COPY(name, keyTab[nkw].id); INC(nkw)\r
+  END EnterKW;\r
+\r
+BEGIN Texts.OpenWriter(W); error := TRUE; nkw := 0;\r
+  EnterKW(array, "ARRAY");\r
+  EnterKW(begin, "BEGIN");\r
+  EnterKW(null, "BY");\r
+  EnterKW(const, "CONST");\r
+  EnterKW(div, "DIV");\r
+  EnterKW(do, "DO");\r
+  EnterKW(else, "ELSE");\r
+  EnterKW(elsif, "ELSIF");\r
+  EnterKW(end, "END");\r
+  EnterKW(false, "FALSE");\r
+  EnterKW(null, "FOR");\r
+  EnterKW(if, "IF");\r
+  EnterKW(null, "IMPORT");\r
+  EnterKW(null, "IN");\r
+  EnterKW(null, "IS");\r
+  EnterKW(mod, "MOD");\r
+  EnterKW(module, "MODULE");\r
+  EnterKW(null, "NIL");\r
+  EnterKW(of, "OF");\r
+  EnterKW(or, "OR");\r
+  EnterKW(null, "POINTER");\r
+  EnterKW(procedure, "PROCEDURE");\r
+  EnterKW(record, "RECORD");\r
+  EnterKW(repeat, "REPEAT");\r
+  EnterKW(null, "RETURN");\r
+  EnterKW(then, "THEN");\r
+  EnterKW(null, "TO");\r
+  EnterKW(true, "TRUE");\r
+  EnterKW(type, "TYPE");\r
+  EnterKW(until, "UNTIL");\r
+  EnterKW(var, "VAR");\r
+  EnterKW(while, "WHILE")\r
+END OSS.\r
diff --git a/cerise/oberon0/RISC.Mod b/cerise/oberon0/RISC.Mod
new file mode 100644 (file)
index 0000000..3e4d739
--- /dev/null
@@ -0,0 +1,78 @@
+MODULE RISC;     (*NW 22.9.07 / 15.12.2013*)\r
+  IMPORT SYSTEM, Texts, Oberon;\r
+  CONST\r
+    MOV = 0; LSL = 1; ASR = 2; ROR = 3; AND = 4; ANN = 5; IOR = 6; XOR = 7;\r
+    ADD = 8; SUB = 9;  MUL = 10; Div = 11;\r
+\r
+  VAR IR: LONGINT;   (*instruction register*)\r
+    PC: LONGINT;   (*program counter*)\r
+    N, Z: BOOLEAN;  (*condition flags*)\r
+    R: ARRAY 16 OF LONGINT;\r
+    H: LONGINT;  (*aux register for division*)\r
+    \r
+  PROCEDURE Execute*(VAR M: ARRAY OF LONGINT; pc: LONGINT;\r
+      VAR S: Texts.Scanner; VAR W: Texts.Writer);\r
+    VAR a, b, op, im: LONGINT;  (*instruction fields*)\r
+      adr, A, B, C: LONGINT;\r
+      MemSize: LONGINT;\r
+  BEGIN PC := 0; R[13] := pc * 4; R[14] := LEN(M)*4;\r
+    REPEAT (*interpretation cycle*)\r
+      IR := M[PC]; INC(PC);\r
+      a := IR DIV 1000000H MOD 10H;\r
+      b := IR DIV 100000H MOD 10H;\r
+      op := IR DIV 10000H MOD 10H;\r
+      im := IR MOD 10000H;\r
+      IF ~ODD(ASH(IR, -31)) THEN  (*~p:  register instruction*)\r
+        B := R[b];\r
+        IF ~ODD(ASH(IR, -30)) THEN (*~q*) C := R[IR MOD 10H]\r
+        ELSIF ~ODD(ASH(IR, -28)) THEN (*q&~v*) C := im \r
+        ELSE (*q&v*) C := im + 0FFFF0000H\r
+        END ;\r
+        CASE op OF\r
+            MOV: IF ~ODD(ASH(IR, -29)) THEN A := C ELSE A := H END |\r
+            LSL: A := SYSTEM.LSH(B, C) |\r
+            ASR: A := ASH(B, -C) |\r
+            ROR: A := SYSTEM.ROT(B, -C) |\r
+            AND: A := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, B) * SYSTEM.VAL(SET, C)) |\r
+            ANN: A := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, B) - SYSTEM.VAL(SET, C)) |\r
+            IOR: A := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, B) + SYSTEM.VAL(SET, C)) |\r
+            XOR: A := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, B) / SYSTEM.VAL(SET, C)) |\r
+            ADD: A := B + C |\r
+            SUB: A := B - C |\r
+            MUL: A := B * C |\r
+            Div: A := B DIV C; H := B MOD C\r
+         END ;\r
+         R[a] := A; N := A < 0; Z := A = 0\r
+      ELSIF ~ODD(ASH(IR, -30)) THEN (*p & ~q: memory instruction*)\r
+        adr := (R[b] + IR MOD 100000H) DIV 4;\r
+        IF ~ODD(ASH(IR, -29)) THEN\r
+          IF adr >= 0 THEN (*load*) R[a] := M[adr]; N := A < 0; Z := A = 0\r
+          ELSE (*input*)\r
+            IF adr = -1 THEN (*ReadInt*) Texts.Scan(S); R[a] := S.i;\r
+            ELSIF adr = -2 THEN (*eot*)  Z := S.class # Texts.Int\r
+            END \r
+          END\r
+        ELSE\r
+          IF adr >= 0 THEN (*store*) M[adr] := R[a];\r
+          ELSE (*output*);\r
+            IF adr = -1 THEN Texts.WriteInt(W, R[a], 4)\r
+            ELSIF adr = -2 THEN Texts.Write(W, CHR(R[a] MOD 80H))\r
+            ELSIF adr = -3 THEN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)\r
+            END\r
+          END\r
+        END\r
+      ELSE (* p & q: branch instruction*)\r
+        IF (a = 0) & N OR (a = 1) & Z OR (a = 5) & N OR (a = 6) & (N OR Z) OR (a = 7) OR\r
+            (a = 8) & ~N OR (a = 9) & ~Z OR (a = 13) & ~N OR (a = 14) & ~(N OR Z) THEN\r
+          IF ODD(ASH(IR, -28)) THEN R[15] := PC * 4 END ;\r
+          IF ODD(ASH(IR, -29)) THEN PC := (PC + (IR MOD 1000000H)) MOD 40000H \r
+          ELSE PC := R[IR MOD 10H] DIV 4\r
+          END\r
+        END\r
+      END\r
+    UNTIL PC = 0;\r
+    Texts.Append(Oberon.Log, W.buf)\r
+  END Execute;\r
+END RISC.\r
+\r
+\r
diff --git a/cerise/oberon0/TestOberon0.Mod b/cerise/oberon0/TestOberon0.Mod
new file mode 100644 (file)
index 0000000..e2da69e
--- /dev/null
@@ -0,0 +1,137 @@
+OSP.Compile @\r
+TestOberon0.Permutations 2 3 4~\r
+TestOberon0.MagicSquares  3~.\r
+TestOberon0.PrimeNumbers 12\r
+TestOberon0.Fractions 16\r
+TestOberon0.Powers 16\r
+\r
+MODULE TestOberon0;\r
+  VAR n: INTEGER;\r
+    a: ARRAY 10 OF INTEGER;\r
+\r
+  PROCEDURE perm(k: INTEGER);\r
+    VAR i, x: INTEGER;\r
+  BEGIN\r
+    IF k = 0 THEN i := 0;\r
+      WHILE i < n DO WriteInt(a[i], 5); i := i+1 END ;\r
+      WriteLn;\r
+    ELSE perm(k-1); i := 0;\r
+      WHILE i < k-1 DO\r
+        x := a[i]; a[i] := a[k-1]; a[k-1] := x;\r
+        perm(k-1);\r
+        x := a[i]; a[i] := a[k-1]; a[k-1] := x;\r
+        i := i+1\r
+      END\r
+    END\r
+  END perm;\r
+\r
+  PROCEDURE Permutations*;\r
+  BEGIN OpenInput; n := 0;\r
+    WHILE ~eot() DO ReadInt(a[n]); n := n+1 END ;\r
+    perm(n)\r
+  END Permutations;\r
+\r
+  PROCEDURE MagicSquares*;  (*magic square of order 3, 5, 7, ... *)\r
+    VAR i, j, x, nx, nsq, n: INTEGER;\r
+      M: ARRAY 13 OF ARRAY 13 OF INTEGER;\r
+  BEGIN OpenInput;\r
+    IF ~eot() THEN\r
+      ReadInt(n); nsq := n*n; x := 0;\r
+      i := n DIV 2; j := n-1;\r
+      WHILE x < nsq DO\r
+        nx := n + x; j := (j-1) MOD n; x := x+1; M[i][j] := x;\r
+        WHILE x < nx DO\r
+          i := (i+1) MOD n; j := (j+1) MOD n;\r
+          x := x+1; M[i][j] := x\r
+        END\r
+      END ;\r
+      i := 0;\r
+      WHILE i < n DO\r
+        j := 0;\r
+        WHILE j < n DO WriteInt(M[i][j], 6); j := j+1 END ;\r
+        i := i+1; WriteLn\r
+      END\r
+    END\r
+  END MagicSquares;\r
+\r
+  PROCEDURE PrimeNumbers*;\r
+    VAR i, k, m, x, inc, lim, sqr: INTEGER; prim: BOOLEAN;\r
+      p: ARRAY 400 OF INTEGER;\r
+      v: ARRAY 20 OF INTEGER;\r
+  BEGIN OpenInput; ReadInt(n);\r
+    x := 1; inc := 4; lim := 1; sqr := 4; m := 0; i := 3;\r
+    WHILE i <= n DO\r
+      REPEAT x := x + inc; inc := 6 - inc;\r
+        IF sqr <= x THEN  (*sqr = p[lim]^2*)\r
+          v[lim] := sqr; lim := lim + 1; sqr := p[lim]*p[lim]\r
+        END ;\r
+        k := 2; prim := TRUE;\r
+        WHILE prim & (k < lim) DO\r
+          k := k+1;\r
+          IF v[k] < x THEN v[k] := v[k] + p[k] END ;\r
+          prim := x # v[k]\r
+        END\r
+      UNTIL prim;\r
+      p[i] := x; WriteInt(x, 5); i := i+1;\r
+      IF m = 10 THEN WriteLn; m := 0 ELSE m := m+1 END\r
+    END ;\r
+    IF m > 0 THEN WriteLn END\r
+  END PrimeNumbers;\r
+\r
+  PROCEDURE Fractions*;  (* Tabulate fractions 1/n*)\r
+    CONST Base = 10; N = 32;\r
+    VAR i, j, m, r, n: INTEGER;\r
+      d: ARRAY N OF INTEGER;  (*digits*)\r
+      x: ARRAY N OF INTEGER;  (*index*)\r
+  BEGIN OpenInput;\r
+    IF ~eot() THEN\r
+      ReadInt(n); i := 2;\r
+      WHILE i <= n DO j := 0;\r
+        WHILE j < i DO x[j] := 0; j := j+1 END ;\r
+        m := 0; r := 1;\r
+        WHILE x[r] = 0 DO\r
+          x[r] := m; r := Base*r; d[m] := r DIV i; r := r MOD i; m := m+1\r
+        END ;\r
+        WriteInt(i, 5); WriteChar(9); WriteChar(46); j := 0;\r
+        WHILE j < x[r] DO WriteChar(d[j] + 48); j := j+1 END ;\r
+        WriteChar(32);  (*blank*)\r
+        WHILE j < m DO WriteChar(d[j] + 48); j := j+1 END ;\r
+        WriteLn; i := i+1\r
+      END\r
+    END\r
+  END Fractions;\r
+\r
+  PROCEDURE Powers*;\r
+    CONST N = 32; M = 11;  (*M ~ N*log2*)\r
+    VAR i, k, n, exp: INTEGER;\r
+      c, r, t: INTEGER;\r
+      d: ARRAY M OF INTEGER;\r
+      f: ARRAY N OF INTEGER;\r
+  BEGIN OpenInput;\r
+    IF ~eot() THEN\r
+      ReadInt(n); d[0] := 1; k := 1; exp := 1;\r
+      WHILE exp < n DO\r
+        (*compute d = 2^exp*)\r
+        c := 0;  (*carry*) i := 0;\r
+        WHILE i < k DO\r
+          t := 2*d[i] + c;\r
+          IF t < 10 THEN d[i] := t; c := 0 ELSE d[i] := t - 10; c := 1 END ;\r
+          i := i+1\r
+        END ;\r
+        IF c = 1 THEN d[k] := 1; k := k+1 END ;\r
+        (*write d*) i := M;\r
+        WHILE i > k DO i := i-1; WriteChar(32) (*blank*) END ;\r
+        WHILE i > 0 DO i := i-1; WriteChar(d[i] + 48) END ;\r
+        WriteInt(exp, M);\r
+        (*compute  f = 2^-exp*)\r
+        WriteChar(9);; WriteChar(46); r := 0; i := 1;\r
+        WHILE i < exp DO\r
+          r := 10*r + f[i]; f[i] := r DIV 2; r := r MOD 2;\r
+          WriteChar(f[i] + 48); i := i+1\r
+        END ;\r
+        f[exp] := 5; WriteChar(53); (*5*) WriteLn; exp := exp + 1\r
+      END\r
+    END\r
+  END Powers;\r
+\r
+END TestOberon0.\r