-
Notifications
You must be signed in to change notification settings - Fork 0
/
mvm.lua
160 lines (128 loc) · 3.73 KB
/
mvm.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
-- mustache VM
-- stack based virtual machine
local dbg = false -- set to true to output machine actions
local log = function (msg)
if dbg then print("log: "..msg) end
end
timer = true -- true to calculate time of execution
--[[ instructions :
-- PSH A -> push A to TOS
-- POP -> pop TOS
-- ADD -> add TOS-1 to TOS
-- SUB -> substitue TOS-1 to TOS
-- MUL -> multiply TOS-1 to TOS
-- DIV -> divide TOS by TOS-1
-- JNP A -> jump to prg[A]
-- JNZ A -> jump to prg[A] if TOS not zero
-- OUT HELLO
--]]
-- instruction ptr and stack ptr
local ip, sp = 0, 0
-- the stack, a simple table
local stack = {}
-- get program table from file
local prg = {}
for l in io.lines("instructions.txt") do
if string.find(l, ";") then
l = string.sub(l, 1, string.find(l, ";")-1) -- ignore comments
end
if string.find(l,"-") then -- if contains a string (i.e. OUT -STR)
table.insert(prg, string.sub(l,1,3))
table.insert(prg, tostring(string.sub(l,6)))
elseif string.find(l,"%d") then -- if contains a number (e.g. PSH X)
table.insert(prg, string.sub(l,1,3))
table.insert(prg, tonumber(string.sub(l,4)))
else
table.insert(prg,l)
end
end
-- start timer if in debug mode
local dt
if timer then dt = os.clock() end
-- main fetch - exec loop
while prg[ip] ~= "HLT" do
local inst = prg[ip] -- current instruction
if inst == "PSH" then
sp = sp + 1
ip = ip + 1
stack[sp] = prg[ip]
log("pushed "..prg[ip].." to TOS")
elseif inst == "POP" then
tmp = stack[sp]
stack[sp] = nil
sp = sp - 1
log("poped "..tmp.." from TOS")
elseif inst == "ADD" then
-- pop TOS and store in tmp1
tmp1 = stack[sp]
stack[sp] = nil
sp = sp -1
-- pop TOS and store in tmp2
tmp2 = stack[sp]
stack[sp] = nil
sp = sp -1
-- push sum to TOS
sp = sp + 1
stack[sp] = tmp1 + tmp2
log("added "..tmp1.." and "..tmp2.." in TOS")
elseif inst == "SUB" then
-- pop TOS and store in tmp1
tmp1 = stack[sp]
stack[sp] = nil
sp = sp -1
-- pop TOS and store in tmp2
tmp2 = stack[sp]
stack[sp] = nil
sp = sp -1
-- push substraction to TOS
sp = sp + 1
stack[sp] = tmp2 - tmp1
log("substracted "..tmp1.." to "..tmp2.." in TOS")
elseif inst == "MUL" then
-- pop TOS and store in tmp1
tmp1 = stack[sp]
stack[sp] = nil
sp = sp -1
-- pop TOS and store in tmp2
tmp2 = stack[sp]
stack[sp] = nil
sp = sp -1
-- push product to TOS
sp = sp + 1
stack[sp] = tmp1 * tmp2
log("multiplied "..tmp1.." and "..tmp2.." in TOS")
elseif inst == "DIV" then
-- pop TOS and store in tmp1
tmp1 = stack[sp]
stack[sp] = nil
sp = sp -1
-- pop TOS and store in tmp2
tmp2 = stack[sp]
stack[sp] = nil
sp = sp -1
-- push sum to TOS
sp = sp + 1
stack[sp] = tmp1 / tmp2
log("divided "..tmp1.." by "..tmp2.." in TOS")
elseif inst == "JNP" then
ip = prg[ip+1]-1
log("branched to "..(ip+1))
elseif inst == "JNZ" then
if stack[sp] ~= 0 then
-- set ip to branch
ip = prg[ip+1]-1 -- -1 beacuse starts at 0
log("TOS not zero, branched to "..(ip+1))
else
log("TOS zero, continue")
end
elseif inst == "OUT" then
print("OUT >> "..prg[ip+1])
end
tmp1, tmp2 = nil, nil -- temp regstrs for operations
ip = ip + 1
end
log("End of execution") -- HLT
if timer then print(string.format("execution time: %.5f", os.clock()-dt)) end
print("\nfinal stack values :")
for i=1,#stack do print(i,stack[i]) end
print("final sp value : "..sp)