Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 8097 → Rev 8859

/programs/develop/oberon07/tools/RVMxI.ob07
0,0 → 1,668
(*
BSD 2-Clause License
 
Copyright (c) 2020-2021, Anton Krotov
All rights reserved.
*)
 
(*
RVMxI executor and disassembler
 
Usage:
RVMxI.exe <program file> -run [program parameters]
RVMxI.exe <program file> -dis <output file>
*)
 
MODULE RVMxI;
 
IMPORT SYSTEM, File, Args, Out, API, HOST;
 
 
CONST
 
szWORD = HOST.bit_depth DIV 8;
 
opSTOP = 0; opRET = 1; opENTER = 2; opNEG = 3; opNOT = 4; opNOP = 5;
opXCHG = 6; opLDB = 7; opLDH = 8; opLDW = 9; opPUSH = 10; opPUSHC = 11;
opPOP = 12; opLABEL = 13; opLEA = 14; opLLA = 15;
opLDD = 16; (* 17, 18 *)
opJMP = 19; opCALL = 20; opCALLI = 21;
 
opMOV = 22; opMUL = 24; opADD = 26; opSUB = 28; opDIV = 30; opMOD = 32;
opSTB = 34; opSTH = 36; opSTW = 38; opSTD = 40; (* 42, 44 *)
opAND = 46; opOR = 48; opXOR = 50; opASR = 52; opLSR = 54;
opLSL = 56; opROR = 58; (* 60, 62 *) opCMP = 64;
 
opMOVC = 23; opMULC = 25; opADDC = 27; opSUBC = 29; opDIVC = 31; opMODC = 33;
opSTBC = 35; opSTHC = 37; opSTWC = 39; opSTDC = 41; (* 43, 45 *)
opANDC = 47; opORC = 49; opXORC = 51; opASRC = 53; opLSRC = 55;
opLSLC = 57; opRORC = 59; (* 61, 63 *) opCMPC = 65;
 
opBIT = 66; opSYSCALL = 67; opJBT = 68; opADDRC = 69;
 
opJEQ = 70; opJNE = 71; opJLT = 72; opJGE = 73; opJGT = 74; opJLE = 75;
opSEQ = 76; opSNE = 77; opSLT = 78; opSGE = 79; opSGT = 80; opSLE = 81;
 
 
nREG = 16;
ACC = 0; BP = 3; SP = 4;
 
Types = 0;
Strings = 1;
Global = 2;
Heap = 3;
Stack = 4;
 
 
TYPE
 
COMMAND = POINTER TO RECORD
 
op, param1, param2: INTEGER;
next, prev: COMMAND
 
END;
 
LABELS = ARRAY 30000 OF COMMAND;
 
SECTIONS = ARRAY 5 OF INTEGER;
 
 
VAR
 
Sections: SECTIONS;
 
first, last: COMMAND;
 
Labels: LABELS;
 
F: INTEGER; buf: ARRAY 65536 OF BYTE; cnt: INTEGER;
 
 
PROCEDURE syscall (ptr: INTEGER);
VAR
fn, r, n: INTEGER;
 
proc2: PROCEDURE (a, b: INTEGER): INTEGER;
proc3: PROCEDURE (a, b, c: INTEGER): INTEGER;
proc4: PROCEDURE (a, b, c, d: INTEGER): INTEGER;
 
r1, r2: REAL;
 
 
PROCEDURE GetInt (ptr, n: INTEGER): INTEGER;
BEGIN
SYSTEM.GET(ptr + SYSTEM.SIZE(INTEGER) * n, n)
RETURN n
END GetInt;
 
 
PROCEDURE GetReal (ptr, n: INTEGER): REAL;
VAR
r: REAL;
 
BEGIN
SYSTEM.GET(ptr + SYSTEM.SIZE(INTEGER) * n, r)
RETURN r
END GetReal;
 
 
BEGIN
fn := GetInt(ptr, 0);
CASE fn OF
| 0:
HOST.ExitProcess(GetInt(ptr, 1))
 
| 1:
SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.GetCurrentDirectory));
r := proc2(GetInt(ptr, 1), GetInt(ptr, 2))
 
| 2:
n := GetInt(ptr, 1);
SYSTEM.PUT(SYSTEM.ADR(proc3), SYSTEM.ADR(HOST.GetArg));
r := proc3(n - ORD(n = 0) + 2, GetInt(ptr, 2), GetInt(ptr, 3))
 
| 3:
SYSTEM.PUT(SYSTEM.ADR(proc4), SYSTEM.ADR(HOST.FileRead));
SYSTEM.PUT(ptr, proc4(GetInt(ptr, 1), GetInt(ptr, 2), GetInt(ptr, 3), GetInt(ptr, 4)))
 
| 4:
SYSTEM.PUT(SYSTEM.ADR(proc4), SYSTEM.ADR(HOST.FileWrite));
SYSTEM.PUT(ptr, proc4(GetInt(ptr, 1), GetInt(ptr, 2), GetInt(ptr, 3), GetInt(ptr, 4)))
 
| 5:
SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.FileCreate));
SYSTEM.PUT(ptr, proc2(GetInt(ptr, 1), GetInt(ptr, 2)))
 
| 6:
HOST.FileClose(GetInt(ptr, 1))
 
| 7:
SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.FileOpen));
SYSTEM.PUT(ptr, proc2(GetInt(ptr, 1), GetInt(ptr, 2)))
 
| 8:
HOST.OutChar(CHR(GetInt(ptr, 1)))
 
| 9:
SYSTEM.PUT(ptr, HOST.GetTickCount())
 
|10:
SYSTEM.PUT(ptr, HOST.UnixTime())
 
|11:
SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.isRelative));
SYSTEM.PUT(ptr, proc2(GetInt(ptr, 1), GetInt(ptr, 2)))
 
|12:
SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.chmod));
r := proc2(GetInt(ptr, 1), GetInt(ptr, 2))
 
|100..103:
r1 := GetReal(ptr, 1);
r2 := GetReal(ptr, 2);
CASE fn OF
|100: SYSTEM.PUT(ptr, r2 * r1)
|101: SYSTEM.PUT(ptr, r2 / r1)
|102: SYSTEM.PUT(ptr, r2 + r1)
|103: SYSTEM.PUT(ptr, r2 - r1)
END
 
|104:
r1 := GetReal(ptr, 2);
r2 := GetReal(ptr, 3);
CASE GetInt(ptr, 1) OF
|0: SYSTEM.PUT(ptr, ORD(r2 = r1))
|1: SYSTEM.PUT(ptr, ORD(r2 # r1))
|2: SYSTEM.PUT(ptr, ORD(r2 < r1))
|3: SYSTEM.PUT(ptr, ORD(r2 <= r1))
|4: SYSTEM.PUT(ptr, ORD(r2 > r1))
|5: SYSTEM.PUT(ptr, ORD(r2 >= r1))
END
 
|105:
SYSTEM.PUT(ptr, FLOOR(GetReal(ptr, 1)))
 
|106:
SYSTEM.PUT(ptr, FLT(GetInt(ptr, 1)))
 
END
END syscall;
 
 
PROCEDURE exec (VAR Labels: LABELS; first, last: COMMAND; Sections: SECTIONS);
VAR
cmd: COMMAND;
param1, param2, i: INTEGER;
R: ARRAY nREG OF INTEGER;
 
fe, fl, fb: BOOLEAN;
 
BEGIN
FOR i := 0 TO LEN(Labels) - 1 DO
cmd := Labels[i];
IF cmd # NIL THEN
REPEAT
cmd := cmd.next
UNTIL cmd.op # opLABEL;
Labels[i] := cmd
END
END;
 
cmd := first;
WHILE cmd # NIL DO
IF cmd.op = opLABEL THEN
cmd.prev.next := cmd.next;
cmd.next.prev := cmd.prev
END;
cmd := cmd.next
END;
 
FOR i := 0 TO LEN(Labels) - 1 DO
IF Labels[i] # NIL THEN
Labels[i] := Labels[i].prev
END
END;
 
cmd := first;
WHILE cmd # NIL DO
param1 := cmd.param1;
param2 := cmd.param2;
 
CASE cmd.op OF
|opSTOP: cmd := last
|opRET: SYSTEM.GET(R[SP], cmd); INC(R[SP], szWORD)
|opENTER: DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], R[BP]); R[BP] := R[SP];
WHILE param1 > 0 DO DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], 0); DEC(param1) END
|opPOP: SYSTEM.GET(R[SP], R[param1]); INC(R[SP], szWORD)
|opPUSH: DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], R[param1])
|opPUSHC: DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], param1)
|opCALL: DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], cmd); cmd := Labels[param1]
|opCALLI: DEC(R[SP], szWORD); SYSTEM.PUT(R[SP], cmd); SYSTEM.GET(SYSTEM.ADR(R[param1]), cmd)
|opNEG: R[param1] := -R[param1]
|opNOT: R[param1] := ORD(-BITS(R[param1]))
|opNOP:
|opXCHG: i := R[param1]; R[param1] := R[param2]; R[param2] := i
|opLDB: i := param1 MOD 256; SYSTEM.GET8(R[param1 DIV 256] + param2, R[i]); R[i] := R[i] MOD 256
|opLDH: i := param1 MOD 256; SYSTEM.GET16(R[param1 DIV 256] + param2, R[i]); R[i] := R[i] MOD 65536
|opLDW: SYSTEM.GET32(R[param1 DIV 256] + param2, R[param1 MOD 256]);
$IF (CPU_X8664)
R[param1 MOD 256] := R[param1 MOD 256] MOD 100000000H
$END
|opLDD: SYSTEM.GET(R[param1 DIV 256] + param2, R[param1 MOD 256])
|opLLA: SYSTEM.GET(SYSTEM.ADR(Labels[param2]), R[param1])
|opJMP: cmd := Labels[param1]
|opMOV: R[param1] := R[param2]
|opMOVC: R[param1] := param2
|opMUL: R[param1] := R[param1] * R[param2]
|opMULC: R[param1] := R[param1] * param2
|opADD: INC(R[param1], R[param2])
|opADDC: INC(R[param1], param2)
|opSUB: DEC(R[param1], R[param2])
|opSUBC: DEC(R[param1], param2)
|opDIV: R[param1] := R[param1] DIV R[param2]
|opDIVC: R[param1] := R[param1] DIV param2
|opMOD: R[param1] := R[param1] MOD R[param2]
|opMODC: R[param1] := R[param1] MOD param2
|opSTB: SYSTEM.PUT8(R[param1 DIV 256] + param2, R[param1 MOD 256])
|opSTH: SYSTEM.PUT16(R[param1 DIV 256] + param2, R[param1 MOD 256])
|opSTW: SYSTEM.PUT32(R[param1 DIV 256] + param2, R[param1 MOD 256])
|opSTD: SYSTEM.PUT(R[param1 DIV 256] + param2, R[param1 MOD 256])
|opSTBC: SYSTEM.PUT8(R[param1], param2)
|opSTHC: SYSTEM.PUT16(R[param1], param2)
|opSTWC: SYSTEM.PUT32(R[param1], param2)
|opSTDC: SYSTEM.PUT(R[param1], param2)
|opAND: R[param1] := ORD(BITS(R[param1]) * BITS(R[param2]))
|opANDC: R[param1] := ORD(BITS(R[param1]) * BITS(param2))
|opOR: R[param1] := ORD(BITS(R[param1]) + BITS(R[param2]))
|opORC: R[param1] := ORD(BITS(R[param1]) + BITS(param2))
|opXOR: R[param1] := ORD(BITS(R[param1]) / BITS(R[param2]))
|opXORC: R[param1] := ORD(BITS(R[param1]) / BITS(param2))
|opASR: R[param1] := ASR(R[param1], R[param2])
|opASRC: R[param1] := ASR(R[param1], param2)
|opLSR: R[param1] := LSR(R[param1], R[param2])
|opLSRC: R[param1] := LSR(R[param1], param2)
|opLSL: R[param1] := LSL(R[param1], R[param2])
|opLSLC: R[param1] := LSL(R[param1], param2)
|opROR: R[param1] := ROR(R[param1], R[param2])
|opRORC: R[param1] := ROR(R[param1], param2)
|opLEA: R[param1 MOD 256] := Sections[param1 DIV 256] + param2
(*|opLABEL:*)
|opSYSCALL: syscall(R[param1])
|opADDRC: R[param1 MOD 256] := R[param1 DIV 256] + param2
|opCMP: fl := R[param1] < R[param2]; fe := R[param1] = R[param2]; fb := fl & (R[param1] >= 0)
|opCMPC: fl := R[param1] < param2; fe := R[param1] = param2; fb := fl & (R[param1] >= 0)
|opJEQ: IF fe THEN cmd := Labels[param1] END
|opJNE: IF ~fe THEN cmd := Labels[param1] END
|opJLT: IF fl THEN cmd := Labels[param1] END
|opJLE: IF fl OR fe THEN cmd := Labels[param1] END
|opJGT: IF ~fl & ~fe THEN cmd := Labels[param1] END
|opJGE: IF ~fl THEN cmd := Labels[param1] END
|opSEQ: R[param1] := ORD(fe)
|opSNE: R[param1] := ORD(~fe)
|opSLT: R[param1] := ORD(fl)
|opSLE: R[param1] := ORD(fl OR fe)
|opSGT: R[param1] := ORD(~fl & ~fe)
|opSGE: R[param1] := ORD(~fl)
|opJBT: IF fb THEN cmd := Labels[param1] END
|opBIT: R[param1] := ORD({R[param2]})
END;
cmd := cmd.next
END
END exec;
 
 
PROCEDURE disasm (name: ARRAY OF CHAR; t_count, c_count, glob, heap: INTEGER);
VAR
cmd: COMMAND;
param1, param2, i, t, ptr: INTEGER;
b: BYTE;
Names: ARRAY 5, 16 OF CHAR;
 
 
PROCEDURE String (s: ARRAY OF CHAR);
VAR
n: INTEGER;
 
BEGIN
n := LENGTH(s);
IF n > LEN(buf) - cnt THEN
ASSERT(File.Write(F, SYSTEM.ADR(buf[0]), cnt) = cnt);
cnt := 0
END;
SYSTEM.MOVE(SYSTEM.ADR(s[0]), SYSTEM.ADR(buf[0]) + cnt, n);
INC(cnt, n)
END String;
 
 
PROCEDURE Ln;
BEGIN
String(0DX + 0AX)
END Ln;
 
 
PROCEDURE hexdgt (n: INTEGER): CHAR;
BEGIN
IF n < 10 THEN
INC(n, ORD("0"))
ELSE
INC(n, ORD("A") - 10)
END
 
RETURN CHR(n)
END hexdgt;
 
 
PROCEDURE Hex (x: INTEGER);
VAR
str: ARRAY 19 OF CHAR;
n: INTEGER;
 
BEGIN
n := szWORD * 2 + 2;
str[n] := 0X;
WHILE n > 2 DO
str[n - 1] := hexdgt(x MOD 16);
x := x DIV 16;
DEC(n)
END;
str[1] := "x";
str[0] := "0";
String(str)
END Hex;
 
 
PROCEDURE Byte (x: BYTE);
VAR
str: ARRAY 5 OF CHAR;
 
BEGIN
str[4] := 0X;
str[3] := hexdgt(x MOD 16);
str[2] := hexdgt(x DIV 16);
str[1] := "x";
str[0] := "0";
String(str)
END Byte;
 
 
PROCEDURE Reg (n: INTEGER);
VAR
s: ARRAY 2 OF CHAR;
BEGIN
IF n = BP THEN
String("BP")
ELSIF n = SP THEN
String("SP")
ELSE
String("R");
s[1] := 0X;
IF n >= 10 THEN
s[0] := CHR(n DIV 10 + ORD("0"));
String(s)
END;
s[0] := CHR(n MOD 10 + ORD("0"));
String(s)
END
END Reg;
 
 
PROCEDURE Reg2 (r1, r2: INTEGER);
BEGIN
Reg(r1); String(", "); Reg(r2)
END Reg2;
 
 
PROCEDURE RegC (r, c: INTEGER);
BEGIN
Reg(r); String(", "); Hex(c)
END RegC;
 
 
PROCEDURE RegL (r, label: INTEGER);
BEGIN
Reg(r); String(", L"); Hex(label)
END RegL;
 
 
BEGIN
Names[Types] := "TYPES";
Names[Strings] := "STRINGS";
Names[Global] := "GLOBAL";
Names[Heap] := "HEAP";
Names[Stack] := "STACK";
 
F := File.Create(name);
ASSERT(F > 0);
cnt := 0;
String("CODE:"); Ln;
cmd := first;
WHILE cmd # NIL DO
param1 := cmd.param1;
param2 := cmd.param2;
CASE cmd.op OF
|opSTOP: String("STOP")
|opRET: String("RET")
|opENTER: String("ENTER "); Hex(param1)
|opPOP: String("POP "); Reg(param1)
|opNEG: String("NEG "); Reg(param1)
|opNOT: String("NOT "); Reg(param1)
|opNOP: String("NOP")
|opXCHG: String("XCHG "); Reg2(param1, param2)
|opLDB: String("LDB "); Reg(param1 MOD 256); String(", ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("]")
|opLDH: String("LDH "); Reg(param1 MOD 256); String(", ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("]")
|opLDW: String("LDW "); Reg(param1 MOD 256); String(", ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("]")
|opLDD: String("LDD "); Reg(param1 MOD 256); String(", ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("]")
|opPUSH: String("PUSH "); Reg(param1)
|opPUSHC: String("PUSH "); Hex(param1)
|opLLA: String("LLA "); RegL(param1, param2)
|opJMP: String("JMP L"); Hex(param1)
|opCALL: String("CALL L"); Hex(param1)
|opCALLI: String("CALL "); Reg(param1)
|opMOV: String("MOV "); Reg2(param1, param2)
|opMOVC: String("MOV "); RegC(param1, param2)
|opMUL: String("MUL "); Reg2(param1, param2)
|opMULC: String("MUL "); RegC(param1, param2)
|opADD: String("ADD "); Reg2(param1, param2)
|opADDC: String("ADD "); RegC(param1, param2)
|opSUB: String("SUB "); Reg2(param1, param2)
|opSUBC: String("SUB "); RegC(param1, param2)
|opDIV: String("DIV "); Reg2(param1, param2)
|opDIVC: String("DIV "); RegC(param1, param2)
|opMOD: String("MOD "); Reg2(param1, param2)
|opMODC: String("MOD "); RegC(param1, param2)
|opSTB: String("STB ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("], "); Reg(param1 MOD 256)
|opSTH: String("STH ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("], "); Reg(param1 MOD 256)
|opSTW: String("STW ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("], "); Reg(param1 MOD 256)
|opSTD: String("STD ["); Reg(param1 DIV 256); String(" + "); Hex(param2); String("], "); Reg(param1 MOD 256)
|opSTBC: String("STB ["); Reg(param1); String("], "); Hex(param2)
|opSTHC: String("STH ["); Reg(param1); String("], "); Hex(param2)
|opSTWC: String("STW ["); Reg(param1); String("], "); Hex(param2)
|opSTDC: String("STD ["); Reg(param1); String("], "); Hex(param2)
|opAND: String("AND "); Reg2(param1, param2)
|opANDC: String("AND "); RegC(param1, param2)
|opOR: String("OR "); Reg2(param1, param2)
|opORC: String("OR "); RegC(param1, param2)
|opXOR: String("XOR "); Reg2(param1, param2)
|opXORC: String("XOR "); RegC(param1, param2)
|opASR: String("ASR "); Reg2(param1, param2)
|opASRC: String("ASR "); RegC(param1, param2)
|opLSR: String("LSR "); Reg2(param1, param2)
|opLSRC: String("LSR "); RegC(param1, param2)
|opLSL: String("LSL "); Reg2(param1, param2)
|opLSLC: String("LSL "); RegC(param1, param2)
|opROR: String("ROR "); Reg2(param1, param2)
|opRORC: String("ROR "); RegC(param1, param2)
|opLEA: String("LEA "); Reg(param1 MOD 256); String(", "); String(Names[param1 DIV 256]); String(" + "); Hex(param2)
|opADDRC: String("ADD "); Reg(param1 MOD 256); String(", "); Reg(param1 DIV 256); String(", "); Hex(param2)
|opLABEL: String("L"); Hex(param1); String(":")
|opSYSCALL: String("SYSCALL "); Reg(param1)
|opCMP: String("CMP "); Reg2(param1, param2)
|opCMPC: String("CMP "); RegC(param1, param2)
|opJEQ: String("JEQ L"); Hex(param1)
|opJNE: String("JNE L"); Hex(param1)
|opJLT: String("JLT L"); Hex(param1)
|opJLE: String("JLE L"); Hex(param1)
|opJGT: String("JGT L"); Hex(param1)
|opJGE: String("JGE L"); Hex(param1)
|opSEQ: String("SEQ "); Reg(param1)
|opSNE: String("SNE "); Reg(param1)
|opSLT: String("SLT "); Reg(param1)
|opSLE: String("SLE "); Reg(param1)
|opSGT: String("SGT "); Reg(param1)
|opSGE: String("SGE "); Reg(param1)
|opJBT: String("JBT L"); Hex(param1)
|opBIT: String("BIT "); Reg2(param1, param2)
END;
Ln;
cmd := cmd.next
END;
 
String("TYPES:");
ptr := Sections[Types];
FOR i := 0 TO t_count - 1 DO
IF i MOD 4 = 0 THEN
Ln; String("WORD ")
ELSE
String(", ")
END;
SYSTEM.GET(ptr, t); INC(ptr, szWORD);
Hex(t)
END;
Ln;
 
String("STRINGS:");
ptr := Sections[Strings];
FOR i := 0 TO c_count - 1 DO
IF i MOD 8 = 0 THEN
Ln; String("BYTE ")
ELSE
String(", ")
END;
SYSTEM.GET8(ptr, b); INC(ptr);
Byte(b)
END;
Ln;
 
String("GLOBAL:"); Ln;
String("WORDS "); Hex(glob); Ln;
String("HEAP:"); Ln;
String("WORDS "); Hex(heap); Ln;
String("STACK:"); Ln;
String("WORDS 8"); Ln;
 
ASSERT(File.Write(F, SYSTEM.ADR(buf[0]), cnt) = cnt);
File.Close(F)
END disasm;
 
 
PROCEDURE GetCommand (adr: INTEGER): COMMAND;
VAR
op, param1, param2: INTEGER;
res: COMMAND;
 
BEGIN
op := 0; param1 := 0; param2 := 0;
SYSTEM.GET(adr, op);
SYSTEM.GET(adr + szWORD, param1);
SYSTEM.GET(adr + szWORD * 2, param2);
NEW(res);
res.op := op;
res.param1 := param1;
res.param2 := param2;
res.next := NIL
 
RETURN res
END GetCommand;
 
 
PROCEDURE main;
VAR
name, param: ARRAY 1024 OF CHAR;
cmd: COMMAND;
file, fsize, n: INTEGER;
 
descr: ARRAY 12 OF INTEGER;
 
offTypes, offStrings, GlobalSize, HeapStackSize, DescrSize: INTEGER;
 
BEGIN
Out.Open;
Args.GetArg(1, name);
F := File.Open(name, File.OPEN_R);
IF F > 0 THEN
DescrSize := LEN(descr) * SYSTEM.SIZE(INTEGER);
fsize := File.Seek(F, 0, File.SEEK_END);
ASSERT(fsize > DescrSize);
file := API._NEW(fsize);
ASSERT(file # 0);
n := File.Seek(F, 0, File.SEEK_BEG);
ASSERT(fsize = File.Read(F, file, fsize));
File.Close(F);
 
SYSTEM.MOVE(file + fsize - DescrSize, SYSTEM.ADR(descr[0]), DescrSize);
offTypes := descr[0];
ASSERT(offTypes < fsize - DescrSize);
ASSERT(offTypes > 0);
ASSERT(offTypes MOD (3 * szWORD) = 0);
offStrings := descr[1];
ASSERT(offStrings < fsize - DescrSize);
ASSERT(offStrings > 0);
ASSERT(offStrings MOD szWORD = 0);
ASSERT(offStrings > offTypes);
GlobalSize := descr[2];
ASSERT(GlobalSize > 0);
HeapStackSize := descr[3];
ASSERT(HeapStackSize > 0);
 
Sections[Types] := API._NEW(offStrings - offTypes);
ASSERT(Sections[Types] # 0);
SYSTEM.MOVE(file + offTypes, Sections[Types], offStrings - offTypes);
 
Sections[Strings] := API._NEW(fsize - offStrings - DescrSize);
ASSERT(Sections[Strings] # 0);
SYSTEM.MOVE(file + offStrings, Sections[Strings], fsize - offStrings - DescrSize);
 
Sections[Global] := API._NEW(GlobalSize * szWORD);
ASSERT(Sections[Global] # 0);
 
Sections[Heap] := API._NEW(HeapStackSize * szWORD);
ASSERT(Sections[Heap] # 0);
 
Sections[Stack] := Sections[Heap] + HeapStackSize * szWORD - szWORD*8;
 
n := offTypes DIV (3 * szWORD);
 
first := GetCommand(file + offTypes - n * (3 * szWORD));
first.prev := NIL;
last := first;
DEC(n);
WHILE n > 0 DO
cmd := GetCommand(file + offTypes - n * (3 * szWORD));
IF cmd.op = opLABEL THEN
Labels[cmd.param1] := cmd
END;
last.next := cmd;
cmd.prev := last;
last := cmd;
DEC(n)
END;
file := API._DISPOSE(file);
Args.GetArg(2, param);
IF param = "-dis" THEN
Args.GetArg(3, name);
IF name # "" THEN
disasm(name, (offStrings - offTypes) DIV szWORD, fsize - offStrings - DescrSize, GlobalSize, HeapStackSize)
END
ELSIF param = "-run" THEN
exec(Labels, first, last, Sections)
END
ELSE
Out.String("file not found"); Out.Ln
END
END main;
 
 
BEGIN
main
END RVMxI.
/programs/develop/oberon07/tools/RVMxI.txt
0,0 → 1,270

Экспериментальная 32/64-битная виртуальная машина RVMxI
---------------------------------------------------------------------------------------------------
 
Использование
 
Скомпилировать исполнитель/дизассемблер в .\tools\RVMxI.ob07
для Windows32/64 Console или Linux32/64:
 
Compiler.exe .\tools\RVMxI.ob07 win32con -nochk a -out RVMxI.exe
Compiler.exe .\tools\RVMxI.ob07 win64con -nochk a -out RVMxI.exe
Compiler ./tools/RVMxI.ob07 linux32exe -nochk a -out RVMxI
Compiler ./tools/RVMxI.ob07 linux64exe -nochk a -out RVMxI
 
Будет создан файл "RVMxI.exe" и/или "RVMxI".
 
Компилировать программу в байт-код RVMxI:
 
Compiler.exe program.ob07 rvm32i [-ram size] [-def host_linux]
Compiler.exe program.ob07 rvm64i [-ram size] [-def host_linux]
-ram size -- установить размер оперативной памяти для программы в килобайтах 32768..262144
(32..256 Мбайт), по умолчанию 32768 (32 Мбайт)
-def host_linux -- если байт-код будет исполняться на Linux (по умолчанию -- Windows)
 
Будет создан файл "program.bin".
 
Выпонить программу:
 
RVMxI.exe program.bin -run <параметры>
 
Дизассемблировать программу:
 
RVMxI.exe program.bin -dis program.txt
 
Будет создан файл "program.txt".
---------------------------------------------------------------------------------------------------
 
Архитектура
 
Регистры
 
Не меньше пяти 32/64-битных регистров:
 
R0, R1, R2 регистры общего назначения
BP(R3) указатель кадра стэка
SP(R4) указатель стэка (растет вниз)
 
R5, R6... регистры общего назначения (опционально)
 
Регистра связи нет (адрес возврата передается через стэк),
регистр-счетчик команд (PC) -- скрытый, регистр флагов -- скрытый.
 
Нет вещественных регистров, операции с плавающей точкой (single (32-бит) или double (64-бит))
эмулируются.
 
Формат кадра стэка
 
Стэк:
 
меньше <- |лок. переменные|старый BP|адрес возврата|парам1|парам2|...|парамN| -> больше
 
(* 32 бита *)
адрес(парам1) = BP + 8
адрес(парам2) = BP + 12
...
 
(* 64 бита *)
адрес(парам1) = BP + 16
адрес(парам2) = BP + 24
...
 
Параметры передаются через стэк справа налево (как cdecl), результат передается через R0,
вызывающая процедура очищает стэк (как cdecl).
 
---------------------------------------------------------------------------------------------------
 
Формат "исполняемого" файла
 
RECORD
 
Text: ARRAY i OF RECORD opcode, param1, param2: INTEGER END; (* байт-код *)
Types: ARRAY t OF INTEGER; (* таблица типов-записей *)
Strings: ARRAY s OF BYTE; (* строковые литералы *)
offTypes: INTEGER; (* смещение таблицы типов-записей от начала файла (в байтах) *)
offStrings: INTEGER; (* смещение строковых литералов от начала файла (в байтах) *)
GlobalSize: INTEGER; (* размер глобальных переменных (в словах; слово = 4 байта) *)
HeapStackSize: INTEGER; (* размер области кучи/стэка (в словах; слово = 4 байта) *)
Reserved: ARRAY 8 OF INTEGER (* зарезервировано *)
 
END
 
Где:
 
INTEGER = INT32/INT64
i = offTypes DIV (3 * sizeof(INTEGER));
t = (offStrings - offTypes) DIV sizeof(INTEGER)
s = FILE_SIZE - offStrings - 12 * sizeof(INTEGER)
---------------------------------------------------------------------------------------------------
 
Система команд
 
мнемоника опкод парам1 парам2 действие
 
STOP 0 0 0 остановить программу
RET 1 0 0 возврат из процедуры (pop PC)
ENTER imm 2 imm 0 push BP; BP := SP; WHILE imm > 0 DO push 0; DEC(imm) END
NEG Rn 3 n 0 Rn := -Rn
NOT Rn 4 n 0 Rn := ORD(-BITS(Rn))
NOP 5 0 0 нет операции
XCHG Rn, Rm 6 n m temp := Rn; Rn := Rm; Rm := temp
LDB Rn, [Rm + imm] 7 m*256 + n imm Rn := UInt8Ptr(Rm + imm)^
LDH Rn, [Rm + imm] 8 m*256 + n imm Rn := UInt16Ptr(Rm + imm)^
LDW Rn, [Rm + imm] 9 m*256 + n imm Rn := UInt32Ptr(Rm + imm)^
* PUSH Rn 10 n 0 DEC(SP, 4); UInt32Ptr(SP)^ := Rn
* PUSH imm 11 imm 0 DEC(SP, 4); UInt32Ptr(SP)^ := imm
* POP Rn 12 n 0 Rn := UInt32Ptr(SP)^; INC(SP, 4)
** PUSH Rn 10 n 0 DEC(SP, 8); UInt64Ptr(SP)^ := Rn
** PUSH imm 11 imm 0 DEC(SP, 8); UInt64Ptr(SP)^ := imm
** POP Rn 12 n 0 Rn := UInt64Ptr(SP)^; INC(SP, 8)
L#hex: 13 hex 0 метка:
LEA Rn, TYPES + imm 14 n + 000H imm Rn := imm + address(TYPES)
LEA Rn, STRINGS + imm 14 n + 100H imm Rn := imm + address(STRINGS)
LEA Rn, GLOBAL + imm 14 n + 200H imm Rn := imm + address(GLOBAL)
LEA Rn, HEAP + imm 14 n + 300H imm Rn := imm + address(HEAP)
LEA Rn, STACK + imm 14 n + 400H imm Rn := imm + address(STACK)
LLA Rn, L#hex 15 n hex Rn := address(L#hex)
** LDD Rn, [Rm + imm] 16 m*256 + n imm Rn := UInt64Ptr(Rm + imm)^
 
JMP L#hex 19 hex 0 goto L#hex
CALL L#hex 20 hex 0 push PC; goto L#hex
CALL Rn 21 n 0 push PC; goto Rn
MOV Rn, Rm 22 n m Rn := Rm
MOV Rn, imm 23 n imm Rn := imm
MUL Rn, Rm 24 n m Rn := Rn * Rm
MUL Rn, imm 25 n imm Rn := Rm * imm
ADD Rn, Rm 26 n m Rn := Rn + Rm
ADD Rn, imm 27 n imm Rn := Rn + imm
SUB Rn, Rm 28 n m Rn := Rn - Rm
SUB Rn, imm 29 n imm Rn := Rn - imm
DIV Rn, Rm 30 n m Rn := Rn DIV Rm
DIV Rn, imm 31 n imm Rn := Rn DIV imm
MOD Rn, Rm 32 n m Rn := Rn MOD Rm
MOD Rn, imm 33 n imm Rn := Rn MOD imm
STB [Rn + imm], Rm 34 n*256 + m imm UInt8Ptr(Rn + imm)^ := Rm MOD 256
STB [Rn], imm 35 n imm UInt8Ptr(Rn)^ := imm MOD 256
STH [Rn + imm], Rm 36 n*256 + m imm UInt16Ptr(Rn + imm)^ := Rm MOD 65536
STH [Rn], imm 37 n imm UInt16Ptr(Rn)^ := imm MOD 65536
* STW [Rn + imm], Rm 38 n*256 + m imm UInt32Ptr(Rn + imm)^ := Rm
* STW [Rn], imm 39 n imm UInt32Ptr(Rn)^ := imm
** STW [Rn + imm], Rm 38 n*256 + m imm UInt32Ptr(Rn + imm)^ := Rm MOD 100000000H
** STW [Rn], imm 39 n imm UInt32Ptr(Rn)^ := imm MOD 100000000H
** STD [Rn + imm], Rm 40 n*256 + m imm UInt64Ptr(Rn + imm)^ := Rm
** STD [Rn], imm 41 n imm UInt64Ptr(Rn)^ := imm
 
AND Rn, Rm 46 n m Rn := ORD(BITS(Rn) * BITS(Rm))
AND Rn, imm 47 n imm Rn := ORD(BITS(Rn) * BITS(imm))
OR Rn, Rm 48 n m Rn := ORD(BITS(Rn) + BITS(Rm))
OR Rn, imm 49 n imm Rn := ORD(BITS(Rn) + BITS(imm))
XOR Rn, Rm 50 n m Rn := ORD(BITS(Rn) / BITS(Rm))
XOR Rn, imm 51 n imm Rn := ORD(BITS(Rn) / BITS(imm))
ASR Rn, Rm 52 n m Rn := ASR(Rn, Rm)
ASR Rn, imm 53 n imm Rn := ASR(Rn, imm)
LSR Rn, Rm 54 n m Rn := LSR(Rn, Rm)
LSR Rn, imm 55 n imm Rn := LSR(Rn, imm)
LSL Rn, Rm 56 n m Rn := LSL(Rn, Rm)
LSL Rn, imm 57 n imm Rn := LSL(Rn, imm)
ROR Rn, Rm 58 n m Rn := ROR(Rn, Rm)
ROR Rn, imm 59 n imm Rn := ROR(Rn, imm)
 
CMP Rn, Rm 64 n m сравнить Rn и Rm
CMP Rn, imm 65 n imm сравнить Rn и imm
BIT Rn, Rm 66 n m Rn := ORD({Rm})
SYSCALL Rn 67 n 0 системный вызов; Rn содержит адрес параметров
JBT L#hex 68 hex 0 перейти на метку L#hex, если "ниже"
ADD Rn, Rm, imm 69 m*256 + n imm Rn := Rm + imm
JEQ L#hex 70 hex 0 перейти на метку L#hex, если "равно"
JNE L#hex 71 hex 0 перейти на метку L#hex, если "не равно"
JLT L#hex 72 hex 0 перейти на метку L#hex, если "меньше"
JGE L#hex 73 hex 0 перейти на метку L#hex, если "не меньше"
JGT L#hex 74 hex 0 перейти на метку L#hex, если "больше"
JLE L#hex 75 hex 0 перейти на метку L#hex, если "не больше"
SEQ Rn 76 n 0 если "равно": Rn := 1, иначе Rn := 0
SNE Rn 77 n 0 если "не равно": Rn := 1, иначе Rn := 0
SLT Rn 78 n 0 если "меньше": Rn := 1, иначе Rn := 0
SGE Rn 79 n 0 если "не меньше": Rn := 1, иначе Rn := 0
SGT Rn 80 n 0 если "больше": Rn := 1, иначе Rn := 0
SLE Rn 81 n 0 если "не больше": Rn := 1, иначе Rn := 0
 
Команда CMP сохраняет результат сравнения в скрытом регистре, этот результат используется
в командах перехода по условию (JEQ, JNE, JLT, JGE, JGT, JLE, JBT) а также в командах
установки регистра по условию (SEQ, SNE, SLT, SGE, SGT, SLE).
 
* Команда для 32-битной виртуальной машины
** Команда для 64-битной виртуальной машины
 
---------------------------------------------------------------------------------------------------
 
Общая структура программы
 
CODE: (* машинный код *)
LEA SP, STACK + 0x00000000 (* точка входа; инициализация регистра SP *)
...
STOP (* конец программы *)
 
TYPES: (* таблица типов-записей *)
WORD 0x00000000, 0x00000000, 0x00000000, 0x00000000
WORD 0x00000002, 0x00000002, 0x00000002, 0x00000002
WORD 0x00000000, 0x00000006, 0x00000000, 0x00000000
WORD 0x00000002, 0x00000000, 0x0000000D, 0x0000000E
WORD 0x0000000C, 0x0000000E, 0x0000000C, 0x00000000
WORD 0x00000000, 0x0000000C, 0x0000000C, 0x00000016
WORD 0x00000000, 0x0000000C, 0x0000000C, 0x0000000C
WORD 0x00000000, 0x00000000, 0x0000000C, 0x0000000C
WORD 0x0000000C, 0x0000000C, 0x0000000C, 0x0000000C
WORD 0x0000000C, 0x0000000C, 0x00000000, 0x00000000
WORD 0x0000000C, 0x0000000C, 0x0000000C, 0x00000000
WORD 0x00000000, 0x0000000C, 0x0000002D, 0x0000002D
WORD 0x0000002D, 0x00000030, 0x00000030, 0x00000030
WORD 0x00000030, 0x0000002D, 0x00000000, 0x00000000
WORD 0x0000000A, 0x00000000, 0x00000002, 0x00000000
WORD 0x00000000, 0x00000000, 0x00000000, 0x00000000
WORD 0x00000000, 0x00000000, 0x00000000, 0x00000000
WORD 0x00000000, 0x0000000C, 0x0000000C, 0x00000000
WORD 0x00000000, 0x0000000C, 0x00000049, 0x00000049
WORD 0x00000049, 0x0000004C, 0x0000004C, 0x0000004C
WORD 0x00000049, 0x0000000C, 0x00000000, 0x0000000C
WORD 0x00000053, 0x00000053, 0x00000053, 0x00000053
WORD 0x0000000C, 0x00000000, 0x00000000, 0x00000000
WORD 0x00000006, 0x0000000C
 
STRINGS: (* строковые литералы *)
BYTE 0x46, 0x50, 0x55, 0x00, 0x54, 0x72, 0x61, 0x70
BYTE 0x00, 0x0D, 0x0A, 0x00, 0x61, 0x73, 0x73, 0x65
BYTE 0x72, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x61
BYTE 0x69, 0x6C, 0x75, 0x72, 0x65, 0x00, 0x4E, 0x49
BYTE 0x4C, 0x20, 0x64, 0x65, 0x72, 0x65, 0x66, 0x65
BYTE 0x72, 0x65, 0x6E, 0x63, 0x65, 0x00, 0x62, 0x61
BYTE 0x64, 0x20, 0x64, 0x69, 0x76, 0x69, 0x73, 0x6F
BYTE 0x72, 0x00, 0x4E, 0x49, 0x4C, 0x20, 0x70, 0x72
BYTE 0x6F, 0x63, 0x65, 0x64, 0x75, 0x72, 0x65, 0x20
BYTE 0x63, 0x61, 0x6C, 0x6C, 0x00, 0x74, 0x79, 0x70
BYTE 0x65, 0x20, 0x67, 0x75, 0x61, 0x72, 0x64, 0x20
BYTE 0x65, 0x72, 0x72, 0x6F, 0x72, 0x00, 0x69, 0x6E
BYTE 0x64, 0x65, 0x78, 0x20, 0x6F, 0x75, 0x74, 0x20
BYTE 0x6F, 0x66, 0x20, 0x72, 0x61, 0x6E, 0x67, 0x65
BYTE 0x00, 0x69, 0x6E, 0x76, 0x61, 0x6C, 0x69, 0x64
BYTE 0x20, 0x43, 0x41, 0x53, 0x45, 0x00, 0x61, 0x72
BYTE 0x72, 0x61, 0x79, 0x20, 0x61, 0x73, 0x73, 0x69
BYTE 0x67, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x65
BYTE 0x72, 0x72, 0x6F, 0x72, 0x00, 0x43, 0x48, 0x52
BYTE 0x20, 0x6F, 0x75, 0x74, 0x20, 0x6F, 0x66, 0x20
BYTE 0x72, 0x61, 0x6E, 0x67, 0x65, 0x00, 0x57, 0x43
BYTE 0x48, 0x52, 0x20, 0x6F, 0x75, 0x74, 0x20, 0x6F
BYTE 0x66, 0x20, 0x72, 0x61, 0x6E, 0x67, 0x65, 0x00
BYTE 0x42, 0x59, 0x54, 0x45, 0x20, 0x6F, 0x75, 0x74
BYTE 0x20, 0x6F, 0x66, 0x20, 0x72, 0x61, 0x6E, 0x67
BYTE 0x65, 0x00, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x20
BYTE 0x28, 0x00, 0x29, 0x3A, 0x20, 0x00, 0x6D, 0x6F
BYTE 0x64, 0x75, 0x6C, 0x65, 0x3A, 0x20, 0x00, 0x6C
BYTE 0x69, 0x6E, 0x65, 0x3A, 0x20, 0x00, 0x52, 0x54
BYTE 0x4C, 0x00, 0x54, 0x65, 0x73, 0x74, 0x00, 0x00
 
GLOBAL:
WORDS 0x00000004 (* размер глобальных переменных в словах (слово = 4 или 8 байт) *)
 
HEAP:
WORDS 0x007FFFBF (* размер области кучи/стэка в словах (слово = 4 или 8 байт) *)
STACK:
WORDS 8 (* зарезервировано *)
---------------------------------------------------------------------------------------------------