fortran66のブログ

fortran について書きます。

【メモ帳】Wirth の Oberon-0 用の RISC エミュレータ

N. Wirth の Compiler Construction

Wirth の Compiler Contruction という本が無料公開されていまして、Wirth の Oberon 言語のサブセットの Oberon-0 言語のコンパイラの作り方を開陳して Oberon 言語で実装しています。対象機械としては簡潔な RISC の模型を想定して、そのエミュレータ用の機械語を出力するようになっています。エミュレータも Oberon で書かれていますがとても短く簡潔なものになっています。

people.inf.ethz.ch

しかし Wirth が Oberon の仕様を何度も改定し、Compiler Construction も改訂を繰り返すので RISC の仕様が変動しネットに落ちている Oberon + Oberon-0 の組み合わせで中々エラーなしに動くものが見つかりません。

Oberon + Oberon-0 組み合わせ

ネットを検索していたところ、以下のレポジトリで Java code を吐く Oberon-07 上で、Oberon-0 を動くようにしてくれていることが分かりました。

How to build に従ってまず Oberon-07 で`Oberon-0 をコンパイルjava 用のバイナリをつくり、その後は java 上の Oberon-0 でコンパイルRISC エミュレータ上での実行となります。

github.com

Fortran で Oberon-0 用 RISC エミュレータ

Oberon-0 の吐き出す仮想のモデル RISC 用のオブジェクトを実行するエミュレータFortran でも試みました。まだデバッグ中ですが、メモ帳代わりに張り付けておきますw

Oberon-0 ソースプログラム File unko.Mod

ファイル名と Module 名が一致していないと駄目なようです。

MODULE unko;

PROCEDURE go;
BEGIN
  WriteChar("F");
  WriteChar("o");
  WriteChar("r");
  WriteChar("t");
  WriteChar("r");
  WriteChar("a");
  WriteChar("n");
  WriteLn;
END go;

BEGIN go
END unko.

生成オブジェクト

Oberon-0 のソースファイルの一つ OSG.Mod 中の Execute プロシージャを書き換えて生成オブジェクトを画面に書き出し、コピペして fort.10 というファイルに保存し、これを Fortran から読み込むこととします。

  PROCEDURE Execute*;
  VAR i: INTEGER;
  BEGIN
     i:=0;
     WHILE i < 100 DO Out.Int(code[i],8);Out.Ln; i:=i+1 END;

     RISC.Execute(code, pc)
  END Execute;
-419430372
1323892740
-1344274432
1073741894
1359020024
-1609564160
1073741935
1359020024
-1609564160
1073741938
1359020024
-1609564160
1073741940
1359020024
-1609564160
1073741938
1359020024
-1609564160
1073741921
1359020024
-1609564160
1073741934
1359020024
-1609564160
1342242804
-1610612736
-1881145344
1323827204
-956301297
-134217757
1073741824
-956301312
       0
       0
       0
       0
       0
       0
       0
       0
       0 
Oberon-0 Compiler OSP  30.10.2013
  compiling unko
code generated    32     0
Fortran

Fortran ソースプログラム

Fortran は符号なし整数が無く、元のままではうまくゆかないので、ビット演算を用いました。

(R2/9/15) bugfix

    program risc
        implicit none
        integer, parameter :: &
          MOV = 0, Lsl = 1, Asr = 2, Ror = 3, AND = 4, ANN = 5, JOR = 6, XOR = 7, &
          ADD = 8, SUB = 9,  MUL = 10, Div = 11
    
        integer :: ir, pc, h = 0! instruction register, program counter, aux registere for division
        integer :: r(0:15) 
        logical :: n, z   ! condition flags
        
        integer :: m(0:1024)
        read(10, *, end = 99) m
        
99      call execute(m, pc)

    contains
    
        subroutine execute(m, pc)
            integer, intent(in out) :: m(0:), pc
            integer :: a, b, op, im
            integer :: adr, aa, bb, cc
            pc = 0
            r(13) = pc * 4
            r(14) = size(m) * 4
            do ! interpretation cycle
                ir = m(pc)
                pc = pc + 1
                a  = iand(shiftr(ir, 24), z'F') ! modulo(ir / z'1000000', z'10')
                b  = iand(shiftr(ir, 20), z'F') ! modulo(ir / z'100000' , z'10')
                op = iand(shiftr(ir, 16), z'F') ! modulo(ir / z'10000'  , z'10')
                im = iand(ir, z'FFFF')          ! modulo(ir , z'10000')
                if (.not. btest(ir, 31))then
                    !modulo(ir / z'80000000', 2) == 0) then ! ~p register instruction 
                    bb = r(b)
                    if (.not. btest(ir, 30)) then 
                        !modulo(ir / z'40000000', 2) == 0) then ! ~q 
                        cc = r(iand(ir, z'F')) !r(modulo(ir, z'10'))
                    else if (.not. btest(ir, 28)) then  
                        !modulo(ir / z'10000000', 2) == 0) then ! q&~v
                        cc = im
                    else ! q & v
                        cc = im + z'ffff0000'
                    end if
                    select case (op)
                        case (MOV)
                            if (.not. btest(ir, 29)) then
                                !modulo(ir / z'20000000', 2) == 0) then
                                aa = cc
                            else
                                aa = h
                            end if
                        case (Lsl); aa = shiftl(bb, cc) !LSL(bb, cc)
                        case (Asr); aa = shifta(bb, cc) !ASR(bb, cc)
                        case (Ror); aa = ishftc(bb,-cc) !ROR(bb, cc)
                        case (AND); aa = iand(bb, cc)
                        case (ANN); aa = iand(bb, not(cc))
                        case (JOR); aa = ior(bb, cc)
                        case (XOR); aa = ieor(bb, cc)
                        case (ADD); aa = bb + cc
                        case (SUB); aa = bb - cc
                        case (MUL); aa = bb * cc
                        case (Div); aa = bb / cc; h = mod(bb, cc)
                    end select
                    r(a) = aa
                    n = aa < 0
                    z = aa == 0
                else if (.not. btest(ir, 30)) then
                         !modulo(ir / z'40000000',2) == 0) then ! p & ~q
                    adr = (r(b) + modulo(ir, z'100000')) / 4
                    if (.not. btest(ir, 29)) then
                        !modulo(ir / z'20000000',2) == 0) then
                        if (adr >= 0) then ! load 
                            r(a) = m(adr)
                            n = a < 0
                            z = a == 0
                        else ! input
                            if (adr == -1) then ! ReadInt
                                read *, r(a)
                            end if
                        end if
                    else
                        if (adr >= 0) then ! store
                            m(adr) = r(a)
                        else ! output
                            if (adr == -1) then 
                                write(*, '(g0, x)', advance = 'no') r(a)
                            else if (adr == -2) then 
                                write(*, '(a1)', advance = 'no') char(modulo(r(a), z'80'))
                            else if (adr == -3) then 
                                print *
                            end if    
                        end if
                    end if    
                else ! p & q branch instruction
                    if ( (a == 0 .and. n) .or. (a == 1 .and. z) .or. (a == 5 .and. n) &
                     .or. (a == 6 .and. (n .or. z)) .or. (a == 7) .or. (a ==8 .and. .not. n) &
                     .or. (a == 9 .and. .not. z) .or. (a == 13 .and.  .not. n) &
                     .or. (a == 14 .and. .not. (n .or. z)) ) then 
                        if (btest(ir, 28)) r(15) = pc * 4
                            !modulo(ir / z'10000000', 2) == 1) r(15) = pc * 4
                        if (btest(ir, 29)) then
                            !modulo(ir / z'20000000', 2) == 1) then 
                            pc = iand(pc + iand(ir, z'FFFFFF'), z'3FFFF')
                            !pc = modulo(pc + modulo(ir, z'1000000'), z'40000')
                        else
                            pc = r(iand(ir, z'F')) / 4
                            !pc = r(modulo(ir, z'10')) / 4
                        end if
                    end if
                end if  
                if (pc == 0) exit
            end do
        end subroutine execute
    end program risc 

Fortran での実行結果

とりあえずうまく行きましたが、まだ命令の一部しか使っていないので何の保証にもなりませんw Bug ってたので修正。

Fortran

積計算

test.Mod

MODULE test;

PROCEDURE Multiply;
  VAR x, y, z: INTEGER;
BEGIN
  WriteChar("x");WriteChar("?");
  ReadInt(x);
  WriteChar("y");WriteChar("?");
  ReadInt(y);
  z := 0;
  WHILE x > 0 DO
    IF x MOD 2 = 1 THEN z := z + y END;
    y := 2 * y;
    x := x DIV 2
  END;
  WriteInt(x);
  WriteInt(y);
  WriteInt(z);
  WriteLn
END Multiply;

BEGIN
  Multiply

END test.
Oberon-0 Compiler OSP  30.10.2013
  compiling test
code generated    60     0
x?2
y?3
   0  12   6
-419430344
1323892752
-1344274432
1073741944
1359020024
-1609564160
1073741887
1359020024
-1609564160
1088946180
1359020028
-2129657856
-1593835520
1073741945
1359020024
-1609564160
1073741887
1359020024
-1609564160
1088946184
1359020028
-2129657856
-1593835520
1073741824
-1595932660
-2132803580
1074331648
-436207601
-2132803580
1074003969
1074331649
-385875964
-2132803572
-2116026360
  524289
-1595932660
-2132803576
1074397186
-1595932664
-2132803580
1073872897
-1595932668
-402653202
-2132803580
1359020028
-1609564160
-2132803576
1359020028
-1609564160
-2132803572
1359020028
-1609564160
1342242804
-1610612736
-1881145344
1323827216
-956301297
-134217785
1073741824
-956301312

Fortran にて

x?2
y?3
0 12 6
x?123
y?456
0 58368 56088

Compiler Construction (International Computer Science Series)

Compiler Construction (International Computer Science Series)

Objektorientierte Programmierung in Oberon-2

Objektorientierte Programmierung in Oberon-2

Programming in Oberon: Steps Beyond Pascal and Modula (ACM Press)

Programming in Oberon: Steps Beyond Pascal and Modula (ACM Press)