(* BSD 2-Clause License Copyright (c) 2020, Anton Krotov All rights reserved. *) (* RVM32I executor and disassembler for win32 only Usage: RVM32I.exe -run [program parameters] RVM32I.exe -dis *) MODULE RVM32I; IMPORT SYSTEM, File, Args, Out, API, HOST, RTL; CONST opSTOP = 0; opRET = 1; opENTER = 2; opNEG = 3; opNOT = 4; opABS = 5; opXCHG = 6; opLDR8 = 7; opLDR16 = 8; opLDR32 = 9; opPUSH = 10; opPUSHC = 11; opPOP = 12; opJGZ = 13; opJZ = 14; opJNZ = 15; opLLA = 16; opJGA = 17; opJLA = 18; opJMP = 19; opCALL = 20; opCALLI = 21; opMOV = 22; opMUL = 24; opADD = 26; opSUB = 28; opDIV = 30; opMOD = 32; opSTR8 = 34; opSTR16 = 36; opSTR32 = 38; opINCL = 40; opEXCL = 42; opIN = 44; opAND = 46; opOR = 48; opXOR = 50; opASR = 52; opLSR = 54; opLSL = 56; opROR = 58; opMIN = 60; opMAX = 62; opEQ = 64; opNE = 66; opLT = 68; opLE = 70; opGT = 72; opGE = 74; opBT = 76; opMOVC = 23; opMULC = 25; opADDC = 27; opSUBC = 29; opDIVC = 31; opMODC = 33; opSTR8C = 35; opSTR16C = 37; opSTR32C = 39; opINCLC = 41; opEXCLC = 43; opINC = 45; opANDC = 47; opORC = 49; opXORC = 51; opASRC = 53; opLSRC = 55; opLSLC = 57; opRORC = 59; opMINC = 61; opMAXC = 63; opEQC = 65; opNEC = 67; opLTC = 69; opLEC = 71; opGTC = 73; opGEC = 75; opBTC = 77; opLEA = 78; opLABEL = 79; opSYSCALL = 80; 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: COMMAND END; VAR R: ARRAY 32 OF INTEGER; Sections: ARRAY 5 OF RECORD address: INTEGER; name: ARRAY 16 OF CHAR END; first, last: COMMAND; Labels: ARRAY 30000 OF COMMAND; F: INTEGER; buf: ARRAY 65536 OF BYTE; cnt: INTEGER; PROCEDURE syscall (ptr: INTEGER); VAR fn, p1, p2, p3, p4, r: INTEGER; proc2: PROCEDURE (a, b: INTEGER): INTEGER; proc3: PROCEDURE (a, b, c: INTEGER): INTEGER; proc4: PROCEDURE (a, b, c, d: INTEGER): INTEGER; BEGIN SYSTEM.GET(ptr, fn); SYSTEM.GET(ptr + 4, p1); SYSTEM.GET(ptr + 8, p2); SYSTEM.GET(ptr + 12, p3); SYSTEM.GET(ptr + 16, p4); CASE fn OF | 0: HOST.ExitProcess(p1) | 1: SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.GetCurrentDirectory)); r := proc2(p1, p2) | 2: SYSTEM.PUT(SYSTEM.ADR(proc3), SYSTEM.ADR(HOST.GetArg)); r := proc3(p1 + 2, p2, p3) | 3: SYSTEM.PUT(SYSTEM.ADR(proc4), SYSTEM.ADR(HOST.FileRead)); SYSTEM.PUT(ptr, proc4(p1, p2, p3, p4)) | 4: SYSTEM.PUT(SYSTEM.ADR(proc4), SYSTEM.ADR(HOST.FileWrite)); SYSTEM.PUT(ptr, proc4(p1, p2, p3, p4)) | 5: SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.FileCreate)); SYSTEM.PUT(ptr, proc2(p1, p2)) | 6: HOST.FileClose(p1) | 7: SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.FileOpen)); SYSTEM.PUT(ptr, proc2(p1, p2)) | 8: HOST.OutChar(CHR(p1)) | 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(p1, p2)) |12: SYSTEM.PUT(SYSTEM.ADR(proc2), SYSTEM.ADR(HOST.chmod)); r := proc2(p1, p2) END END syscall; PROCEDURE exec; VAR cmd: COMMAND; param1, param2: INTEGER; temp: INTEGER; BEGIN cmd := first; WHILE cmd # NIL DO param1 := cmd.param1; param2 := cmd.param2; CASE cmd.op OF |opSTOP: cmd := last |opRET: SYSTEM.MOVE(R[SP], SYSTEM.ADR(cmd), 4); INC(R[SP], 4) |opENTER: DEC(R[SP], 4); SYSTEM.PUT32(R[SP], R[BP]); R[BP] := R[SP]; WHILE param1 > 0 DO DEC(R[SP], 4); SYSTEM.PUT32(R[SP], 0); DEC(param1) END |opPOP: SYSTEM.GET32(R[SP], R[param1]); INC(R[SP], 4) |opNEG: R[param1] := -R[param1] |opNOT: R[param1] := ORD(-BITS(R[param1])) |opABS: R[param1] := ABS(R[param1]) |opXCHG: temp := R[param1]; R[param1] := R[param2]; R[param2] := temp |opLDR8: SYSTEM.GET8(R[param2], R[param1]); R[param1] := R[param1] MOD 256; |opLDR16: SYSTEM.GET16(R[param2], R[param1]); R[param1] := R[param1] MOD 65536; |opLDR32: SYSTEM.GET32(R[param2], R[param1]) |opPUSH: DEC(R[SP], 4); SYSTEM.PUT32(R[SP], R[param1]) |opPUSHC: DEC(R[SP], 4); SYSTEM.PUT32(R[SP], param1) |opJGZ: IF R[param1] > 0 THEN cmd := Labels[cmd.param2] END |opJZ: IF R[param1] = 0 THEN cmd := Labels[cmd.param2] END |opJNZ: IF R[param1] # 0 THEN cmd := Labels[cmd.param2] END |opLLA: SYSTEM.MOVE(SYSTEM.ADR(Labels[cmd.param2]), SYSTEM.ADR(R[param1]), 4) |opJGA: IF R[ACC] > param1 THEN cmd := Labels[cmd.param2] END |opJLA: IF R[ACC] < param1 THEN cmd := Labels[cmd.param2] END |opJMP: cmd := Labels[cmd.param1] |opCALL: DEC(R[SP], 4); SYSTEM.MOVE(SYSTEM.ADR(cmd), R[SP], 4); cmd := Labels[cmd.param1] |opCALLI: DEC(R[SP], 4); SYSTEM.MOVE(SYSTEM.ADR(cmd), R[SP], 4); SYSTEM.MOVE(SYSTEM.ADR(R[param1]), SYSTEM.ADR(cmd), 4) |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 |opSTR8: SYSTEM.PUT8(R[param1], R[param2]) |opSTR8C: SYSTEM.PUT8(R[param1], param2) |opSTR16: SYSTEM.PUT16(R[param1], R[param2]) |opSTR16C: SYSTEM.PUT16(R[param1], param2) |opSTR32: SYSTEM.PUT32(R[param1], R[param2]) |opSTR32C: SYSTEM.PUT32(R[param1], param2) |opINCL: SYSTEM.GET32(R[param1], temp); SYSTEM.PUT32(R[param1], ORD(BITS(temp) + {R[param2]})) |opINCLC: SYSTEM.GET32(R[param1], temp); SYSTEM.PUT32(R[param1], ORD(BITS(temp) + {param2})) |opEXCL: SYSTEM.GET32(R[param1], temp); SYSTEM.PUT32(R[param1], ORD(BITS(temp) - {R[param2]})) |opEXCLC: SYSTEM.GET32(R[param1], temp); SYSTEM.PUT32(R[param1], ORD(BITS(temp) - {param2})) |opIN: R[param1] := ORD(R[param1] IN BITS(R[param2])) |opINC: R[param1] := ORD(R[param1] IN BITS(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) |opMIN: R[param1] := MIN(R[param1], R[param2]) |opMINC: R[param1] := MIN(R[param1], param2) |opMAX: R[param1] := MAX(R[param1], R[param2]) |opMAXC: R[param1] := MAX(R[param1], param2) |opEQ: R[param1] := ORD(R[param1] = R[param2]) |opEQC: R[param1] := ORD(R[param1] = param2) |opNE: R[param1] := ORD(R[param1] # R[param2]) |opNEC: R[param1] := ORD(R[param1] # param2) |opLT: R[param1] := ORD(R[param1] < R[param2]) |opLTC: R[param1] := ORD(R[param1] < param2) |opLE: R[param1] := ORD(R[param1] <= R[param2]) |opLEC: R[param1] := ORD(R[param1] <= param2) |opGT: R[param1] := ORD(R[param1] > R[param2]) |opGTC: R[param1] := ORD(R[param1] > param2) |opGE: R[param1] := ORD(R[param1] >= R[param2]) |opGEC: R[param1] := ORD(R[param1] >= param2) |opBT: R[param1] := ORD((R[param1] < R[param2]) & (R[param1] >= 0)) |opBTC: R[param1] := ORD((R[param1] < param2) & (R[param1] >= 0)) |opLEA: R[param1 MOD 256] := Sections[param1 DIV 256].address + param2 |opLABEL: |opSYSCALL: syscall(R[param1]) 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; 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 11 OF CHAR; n: INTEGER; BEGIN n := 10; str[10] := 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 Sections[Types].name := "TYPES"; Sections[Strings].name := "STRINGS"; Sections[Global].name := "GLOBAL"; Sections[Heap].name := "HEAP"; Sections[Stack].name := "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) |opABS: String("ABS "); Reg(param1) |opXCHG: String("XCHG "); Reg2(param1, param2) |opLDR8: String("LDR8 "); Reg2(param1, param2) |opLDR16: String("LDR16 "); Reg2(param1, param2) |opLDR32: String("LDR32 "); Reg2(param1, param2) |opPUSH: String("PUSH "); Reg(param1) |opPUSHC: String("PUSH "); Hex(param1) |opJGZ: String("JGZ "); RegL(param1, param2) |opJZ: String("JZ "); RegL(param1, param2) |opJNZ: String("JNZ "); RegL(param1, param2) |opLLA: String("LLA "); RegL(param1, param2) |opJGA: String("JGA "); Hex(param1); String(", L"); Hex(param2) |opJLA: String("JLA "); Hex(param1); String(", L"); Hex(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) |opSTR8: String("STR8 "); Reg2(param1, param2) |opSTR8C: String("STR8 "); RegC(param1, param2) |opSTR16: String("STR16 "); Reg2(param1, param2) |opSTR16C: String("STR16 "); RegC(param1, param2) |opSTR32: String("STR32 "); Reg2(param1, param2) |opSTR32C: String("STR32 "); RegC(param1, param2) |opINCL: String("INCL "); Reg2(param1, param2) |opINCLC: String("INCL "); RegC(param1, param2) |opEXCL: String("EXCL "); Reg2(param1, param2) |opEXCLC: String("EXCL "); RegC(param1, param2) |opIN: String("IN "); Reg2(param1, param2) |opINC: String("IN "); RegC(param1, 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) |opMIN: String("MIN "); Reg2(param1, param2) |opMINC: String("MIN "); RegC(param1, param2) |opMAX: String("MAX "); Reg2(param1, param2) |opMAXC: String("MAX "); RegC(param1, param2) |opEQ: String("EQ "); Reg2(param1, param2) |opEQC: String("EQ "); RegC(param1, param2) |opNE: String("NE "); Reg2(param1, param2) |opNEC: String("NE "); RegC(param1, param2) |opLT: String("LT "); Reg2(param1, param2) |opLTC: String("LT "); RegC(param1, param2) |opLE: String("LE "); Reg2(param1, param2) |opLEC: String("LE "); RegC(param1, param2) |opGT: String("GT "); Reg2(param1, param2) |opGTC: String("GT "); RegC(param1, param2) |opGE: String("GE "); Reg2(param1, param2) |opGEC: String("GE "); RegC(param1, param2) |opBT: String("BT "); Reg2(param1, param2) |opBTC: String("BT "); RegC(param1, param2) |opLEA: String("LEA "); Reg(param1 MOD 256); String(", "); String(Sections[param1 DIV 256].name); String(" + "); Hex(param2) |opLABEL: String("L"); Hex(param1); String(":") |opSYSCALL: String("SYSCALL "); Reg(param1) END; Ln; cmd := cmd.next END; String("TYPES:"); ptr := Sections[Types].address; FOR i := 0 TO t_count - 1 DO IF i MOD 4 = 0 THEN Ln; String("WORD ") ELSE String(", ") END; SYSTEM.GET32(ptr, t); INC(ptr, 4); Hex(t) END; Ln; String("STRINGS:"); ptr := Sections[Strings].address; 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.GET32(adr, op); SYSTEM.GET32(adr + 4, param1); SYSTEM.GET32(adr + 8, 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 12 = 0); offStrings := descr[1]; ASSERT(offStrings < fsize - DescrSize); ASSERT(offStrings > 0); ASSERT(offStrings MOD 4 = 0); ASSERT(offStrings > offTypes); GlobalSize := descr[2]; ASSERT(GlobalSize > 0); HeapStackSize := descr[3]; ASSERT(HeapStackSize > 0); Sections[Types].address := API._NEW(offStrings - offTypes); ASSERT(Sections[Types].address # 0); SYSTEM.MOVE(file + offTypes, Sections[Types].address, offStrings - offTypes); Sections[Strings].address := API._NEW(fsize - offStrings - DescrSize); ASSERT(Sections[Strings].address # 0); SYSTEM.MOVE(file + offStrings, Sections[Strings].address, fsize - offStrings - DescrSize); Sections[Global].address := API._NEW(GlobalSize * 4); ASSERT(Sections[Global].address # 0); Sections[Heap].address := API._NEW(HeapStackSize * 4); ASSERT(Sections[Heap].address # 0); Sections[Stack].address := Sections[Heap].address + HeapStackSize * 4 - 32; n := offTypes DIV 12; first := GetCommand(file + offTypes - n * 12); last := first; DEC(n); WHILE n > 0 DO cmd := GetCommand(file + offTypes - n * 12); IF cmd.op = opLABEL THEN Labels[cmd.param1] := cmd END; last.next := cmd; 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 4, fsize - offStrings - DescrSize, GlobalSize, HeapStackSize) END ELSIF param = "-run" THEN exec END ELSE Out.String("file not found"); Out.Ln END END main; BEGIN ASSERT(RTL.bit_depth = 32); main END RVM32I.