haxor 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  module Haxor
2
2
  class Consts
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  PATCH = 0
6
6
  GEM_VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  VERSION = (MAJOR << 16) | (MINOR << 8) | PATCH
@@ -11,21 +11,8 @@ module Haxor
11
11
  WORD_SIZE = 8 # 64bit
12
12
  WORD_UNPACK = 'q<'
13
13
 
14
- RESERVED_MEM = 2048 # 1024 for CPU, 1024 for IVT
15
- IVT_ADDR = 1024
16
-
17
- # OpCode
18
- OPCODE_CMD_MASK = 0x0000_0000_0000_00ff
19
- OPCODE_FLG_MASK = 0xffff_ffff_ffff_ff00 # << 8
20
- OPCODE_FLG_OFFSET = 8
21
- # 8 bits of flag - 1st operand
22
- # 8 bits of flag - 2nd operand
23
-
24
- OPERAND_FLAGS = 8 # how many bits per operand
25
- OPERAND_DEREFERENCE = (1 << 0) # Dereference operand (aka *operand)
26
-
27
- # Flags Registry Flags
28
- FR_ZERO = 1 << 0 # a-b == 0
29
- FR_SIGN = 1 << 1 # a-b < 0
14
+ IVT_MEM = 1024
15
+ IVT_ADDR = 0
16
+ RESERVED_MEM = IVT_MEM
30
17
  end
31
18
  end
@@ -8,7 +8,7 @@ module Haxor
8
8
  @cpu = Vm::Cpu::Core.new
9
9
  @units = []
10
10
  @tokens = []
11
- @labels = @cpu.labels.clone
11
+ @labels = {}
12
12
  @stack = 4096
13
13
  end
14
14
 
@@ -56,9 +56,17 @@ module Haxor
56
56
  end
57
57
 
58
58
  def unwind_pointers
59
- walk_tokens(Token::Pointer) do |token|
60
- fail "Label not found: #{token.label}." unless @labels.key? token.label
61
- token.data = @labels[token.label].absolute_addr
59
+ walk_tokens(Token::Cmd) do |token|
60
+ next unless token.imm.is_a? String
61
+ fail "Label not found: #{token.imm}." unless @labels.key? token.imm
62
+
63
+ token.imm = @labels[token.imm].absolute_addr
64
+
65
+ if token.opts.include? :rel_imm
66
+ token.imm -= token.absolute_addr + Consts::WORD_SIZE
67
+ end
68
+
69
+ token.imm /= Consts::WORD_SIZE if token.opts.include? :x8
62
70
  end
63
71
  end
64
72
 
@@ -1,8 +1,35 @@
1
1
  module Haxor
2
2
  module Token
3
- class Cmd < Int64
3
+ class Cmd < Base
4
+ attr_accessor :cmd
5
+ attr_accessor :flags
6
+ attr_accessor :reg1
7
+ attr_accessor :reg2
8
+ attr_accessor :reg3
9
+ attr_accessor :imm
10
+ attr_accessor :opts
11
+
12
+ def initialize
13
+ @cmd = 0
14
+ @flags = 0
15
+ @reg1 = 0
16
+ @reg2 = 0
17
+ @reg3 = 0
18
+ @imm = 0
19
+ @opts = []
20
+ end
21
+
22
+ def size
23
+ Consts::WORD_SIZE
24
+ end
25
+
26
+ def to_bytecode
27
+ data = Utils.encode_opcode @cmd, @flags, @reg1, @reg2, @reg3, @imm
28
+ [data].pack Consts::WORD_UNPACK
29
+ end
30
+
4
31
  def to_s
5
- "Cmd -> 0x#{@data.to_s(16)}"
32
+ "CMD code:[0x#{@cmd.to_s(16)}], flags:[#{@flags.to_s(2)}], regs:[#{@reg1}, #{@reg2}, #{@reg3}], imm:[#{@imm}], opts:#{@opts}"
6
33
  end
7
34
  end
8
35
  end
@@ -0,0 +1,24 @@
1
+ module Haxor
2
+ module Utils
3
+ def self.encode_opcode(cmd = 0, flags = 0, reg1 = 0, reg2 = 0, reg3 = 0, imm = 0)
4
+ result = cmd
5
+ result |= flags << 7
6
+ result |= reg1 << 9
7
+ result |= reg2 << 15
8
+ result |= reg3 << 21
9
+ result |= imm << 27
10
+ result
11
+ end
12
+
13
+ def self.decode_opcode(opcode)
14
+ r = OpenStruct.new
15
+ r.cmd = opcode & 0x7f
16
+ r.flags = (opcode >> 7) & 0x03
17
+ r.reg1 = (opcode >> 9) & 0x3f
18
+ r.reg2 = (opcode >> 15) & 0x3f
19
+ r.reg3 = (opcode >> 21) & 0x3f
20
+ r.imm = (opcode >> 27)
21
+ r
22
+ end
23
+ end
24
+ end
@@ -9,12 +9,7 @@ module Haxor
9
9
  register_subsystem :mem, Mem.new(Consts::RESERVED_MEM)
10
10
  register_subsystem :stack, Stack.new
11
11
  register_subsystem :os, Os.new
12
- register_subsystem :registers, Registers.new
13
-
14
- subsystem(:cpu).labels.each do |_, label|
15
- subsystem(:mem).add_label label.label, label.absolute_addr
16
- subsystem(:registers).add_register label.label, label.absolute_addr
17
- end
12
+ # register_subsystem :registers, Registers.new
18
13
 
19
14
  @units = []
20
15
  @opcodes = {}
@@ -41,14 +36,16 @@ module Haxor
41
36
  @hdr = Header.new
42
37
  @hdr.parse! exe
43
38
 
44
- fail if @hdr.version != Consts::VERSION
39
+ if @hdr.version != Consts::VERSION
40
+ fail 'Program is compiled for different version of Haxor VM Machine'
41
+ end
45
42
 
46
43
  exe = exe[@hdr.size..-1] # cut off header
47
44
  subsystem(:mem).replace_region Consts::RESERVED_MEM, exe
48
- subsystem(:registers).write 'ip', @hdr.entry_point # instruction pointer
45
+ subsystem(:cpu).ip = @hdr.entry_point # instruction pointer
49
46
  subsystem(:mem).enlarge @hdr.bss_size
50
47
  subsystem(:mem).enlarge @hdr.stack_size
51
- subsystem(:registers).write 'sp', subsystem(:mem).size
48
+ subsystem(:cpu).reg Vm::Cpu::Core::REG_STACK, subsystem(:mem).size
52
49
  end
53
50
  end
54
51
  end
@@ -2,126 +2,257 @@ module Haxor
2
2
  module Vm
3
3
  module Cpu
4
4
  class Core < Subsystem
5
- attr_reader :struct
6
- attr_reader :labels
5
+ attr_accessor :ip
6
+
7
+ # Misc
8
+ OP_NOP = 0x00
9
+ OP_EXITI = 0x01
10
+ OP_SYSCALL = 0x02
11
+
12
+ # Arithmetic
13
+ OP_ADD = 0x10
14
+ OP_ADDI = 0x11
15
+ OP_SUB = 0x12
16
+ OP_MULT = 0x13
17
+ OP_DIV = 0x14
18
+ OP_MOD = 0x15
19
+
20
+ # Data transfer
21
+ OP_LW = 0x20
22
+ OP_SW = 0x21
23
+ OP_LUI = 0x22
24
+
25
+ # Logical
26
+ OP_AND = 0x30
27
+ OP_ANDI = 0x31
28
+ OP_OR = 0x32
29
+ OP_ORI = 0x33
30
+ OP_XOR = 0x34
31
+ OP_NOR = 0x35
32
+ OP_SLT = 0x36
33
+ OP_SLTI = 0x37
34
+
35
+ # Bitwise shift
36
+ OP_SLLI = 0x40
37
+ OP_SRLI = 0x41
38
+ OP_SLL = 0x42
39
+ OP_SRL = 0x43
40
+
41
+ # Branch/Jumps
42
+ OP_BEQ = 0x50
43
+ OP_BEQL = 0x51
44
+ OP_BNE = 0x52
45
+ OP_BNEL = 0x53
46
+ OP_J = 0x54
47
+ OP_JR = 0x55
48
+ OP_JAL = 0x56
49
+
50
+ REG_ZERO = 0
51
+ REG_STACK = 61
52
+ REG_RETURN = 62
53
+ REG_SYSCALL = 63
7
54
 
8
55
  def initialize
56
+ @registers = Array.new(64, 0)
9
57
  @units = []
10
58
  @opcodes = {}
59
+ init_opcodes
60
+ end
11
61
 
12
- register_unit Unit::Arithmetic.new
13
- register_unit Unit::Jumps.new
14
- register_unit Unit::Logical.new
15
- register_unit Unit::Transfer.new
16
- register_unit Unit::Various.new
62
+ def init_opcodes
63
+ bind_opcode OP_NOP, :op_nop
64
+ bind_opcode OP_ADD, :op_add
65
+ bind_opcode OP_ADDI, :op_addi
66
+ bind_opcode OP_SUB, :op_sub
67
+ bind_opcode OP_MULT, :op_mult
68
+ bind_opcode OP_DIV, :op_div
69
+ bind_opcode OP_MOD, :op_mod
70
+ bind_opcode OP_LW, :op_lw
71
+ bind_opcode OP_SW, :op_sw
72
+ bind_opcode OP_LUI, :op_lui
73
+ bind_opcode OP_AND, :op_and
74
+ bind_opcode OP_ANDI, :op_andi
75
+ bind_opcode OP_OR, :op_or
76
+ bind_opcode OP_ORI, :op_ori
77
+ bind_opcode OP_XOR, :op_xor
78
+ bind_opcode OP_NOR, :op_nor
79
+ bind_opcode OP_SLT, :op_slt
80
+ bind_opcode OP_SLTI, :op_slti
81
+ bind_opcode OP_SLLI, :op_slli
82
+ bind_opcode OP_SRLI, :op_srli
83
+ bind_opcode OP_SLL, :op_sll
84
+ bind_opcode OP_SRL, :op_srl
85
+ bind_opcode OP_BEQ, :op_beq
86
+ bind_opcode OP_BEQL, :op_beql
87
+ bind_opcode OP_BNE, :op_bne
88
+ bind_opcode OP_BNEL, :op_bnel
89
+ bind_opcode OP_J, :op_j
90
+ bind_opcode OP_JR, :op_jr
91
+ bind_opcode OP_JAL, :op_jal
92
+ bind_opcode OP_EXITI, :op_exiti
93
+ bind_opcode OP_SYSCALL, :op_syscall
94
+ end
17
95
 
18
- build_struct
96
+ def op_nop(info)
19
97
  end
20
98
 
21
- def bind_opcode(opcode, object, method)
22
- fail "OpCode already defined - #{opcode}." if @opcodes.key? opcode
23
- @opcodes[opcode] = {
24
- object: object,
25
- method: method
26
- }
99
+ def op_add(info)
100
+ reg info.reg1, reg(info.reg2) + reg(info.reg3)
27
101
  end
28
102
 
29
- def iterate
30
- opcode = subsystem(:mem).next_cell
31
- cmd = opcode & Consts::OPCODE_CMD_MASK
32
- subsystem(:mem).write 'op', opcode
103
+ def op_addi(info)
104
+ reg info.reg1, reg(info.reg2) + info.imm
105
+ end
106
+
107
+ def op_sub(info)
108
+ reg info.reg1, reg(info.reg2) - reg(info.reg3)
109
+ end
110
+
111
+ def op_mult(info)
112
+ reg info.reg1, reg(info.reg2) * reg(info.reg3)
113
+ end
114
+
115
+ def op_div(info)
116
+ reg info.reg1, reg(info.reg2) / reg(info.reg3)
117
+ end
118
+
119
+ def op_mod(info)
120
+ reg info.reg1, reg(info.reg2) % reg(info.reg3)
121
+ end
122
+
123
+ # reg1 = memory[reg2+imm]
124
+ def op_lw(info)
125
+ addr = reg(info.reg2) + info.imm
126
+ reg info.reg1, subsystem(:mem).read(addr)
127
+ end
128
+
129
+ # memory[reg1+imm] = reg2
130
+ def op_sw(info)
131
+ addr = reg(info.reg1) + info.imm
132
+ subsystem(:mem).write addr, reg(info.reg2)
133
+ end
134
+
135
+ def op_lui(info)
136
+ reg info.reg1, (info.imm << 32)
137
+ end
138
+
139
+ def op_and(info)
140
+ reg info.reg1, reg(info.reg2) & reg(info.reg3)
141
+ end
142
+
143
+ def op_andi(info)
144
+ reg info.reg1, reg(info.reg2) & info.imm
145
+ end
33
146
 
34
- fail "Call to not existing opcode #{cmd}." unless @opcodes.key? cmd
35
- # puts 'cmd = ' + cmd.to_s(16)
36
- # puts flg.to_s(16)
37
- @opcodes[cmd][:object].send(@opcodes[cmd][:method])
147
+ def op_or(info)
148
+ reg info.reg1, reg(info.reg2) | reg(info.reg3)
38
149
  end
39
150
 
40
- def reserved_mem
41
- 1024
151
+ def op_ori(info)
152
+ reg info.reg1, reg(info.reg2) | info.imm
42
153
  end
43
154
 
44
- def register_unit(unit)
45
- @units << unit
46
- unit.vm = self
47
- unit.register
155
+ def op_xor(info)
156
+ reg info.reg1, reg(info.reg2) ^ reg(info.reg3)
48
157
  end
49
158
 
50
- def operand
51
- operands(1)[0]
159
+ def op_nor(info)
160
+ reg info.reg1, ~(reg(info.reg2) | reg(info.reg3))
52
161
  end
53
162
 
54
- def operands(n = 2)
55
- flg = subsystem(:mem).read('op') & Consts::OPCODE_FLG_MASK
56
- flg >> Consts::OPCODE_FLG_OFFSET
163
+ def op_slt(info)
164
+ reg info.reg1, reg(info.reg2) < reg(info.reg3) ? 1 : 0
165
+ end
57
166
 
58
- result = []
59
- n.times do
60
- v = subsystem(:mem).next_cell
61
- v = subsystem(:mem).read a if (flg & Consts::OPERAND_DEREFERENCE != 0)
62
- result << v
63
- flg << Consts::OPERAND_FLAGS
64
- end
167
+ def op_slti(info)
168
+ reg info.reg1, reg(info.reg2) < info.imm ? 1 : 0
169
+ end
65
170
 
66
- result
171
+ def op_slli(info)
172
+ reg info.reg1, reg(info.reg2) << info.imm
67
173
  end
68
174
 
69
- private
175
+ def op_srli(info)
176
+ reg info.reg1, reg(info.reg2) >> info.imm
177
+ end
70
178
 
71
- def build_struct
72
- @struct = []
179
+ def op_sll(info)
180
+ reg info.reg1, reg(info.reg2) << reg(info.reg3)
181
+ end
73
182
 
74
- @struct << Token::Label.new('null')
75
- @struct << Token::Int64.new(0)
183
+ def op_srl(info)
184
+ reg info.reg1, reg(info.reg2) >> reg(info.reg3)
185
+ end
186
+
187
+ def op_beq(info)
188
+ branch(info.imm) if reg(info.reg1) == reg(info.reg2)
189
+ end
190
+
191
+ def op_beql(info)
192
+ return if reg(info.reg1) != reg(info.reg2)
193
+
194
+ reg REG_RETURN, @ip
195
+ branch(info.imm)
196
+ end
197
+
198
+ def op_bne(info)
199
+ branch info.imm if reg(info.reg1) != reg(info.reg2)
200
+ end
76
201
 
77
- @struct << Token::Label.new('registers')
202
+ def op_bnel(info)
203
+ return if reg(info.reg1) == reg(info.reg2)
78
204
 
79
- # instruction pointer
80
- @struct << Token::Label.new('ip')
81
- @struct << Token::Int64.new(0)
205
+ reg REG_RETURN, @ip
206
+ branch(info.imm)
207
+ end
82
208
 
83
- # stack pointer
84
- @struct << Token::Label.new('sp')
85
- @struct << Token::Int64.new(0)
209
+ def op_j(info)
210
+ jmp info.imm
211
+ end
86
212
 
87
- # base pointer
88
- @struct << Token::Label.new('bp')
89
- @struct << Token::Int64.new(0)
213
+ def op_jr(info)
214
+ @ip = reg(info.reg1)
215
+ end
90
216
 
91
- # arithmetic registry I
92
- @struct << Token::Label.new('ar')
93
- @struct << Token::Int64.new(0)
217
+ def op_jal(info)
218
+ reg REG_RETURN, @ip
219
+ jmp info.imm
220
+ end
94
221
 
95
- # arithmetic registry II
96
- @struct << Token::Label.new('dr')
97
- @struct << Token::Int64.new(0)
222
+ def op_exiti(info)
223
+ exit info.imm
224
+ end
98
225
 
99
- # flags registry
100
- @struct << Token::Label.new('fr')
101
- @struct << Token::Int64.new(0)
226
+ def op_syscall(_info)
227
+ @vm.subsystem(:os).syscall
228
+ end
102
229
 
103
- # currently processed opcode (with opcode flags)
104
- @struct << Token::Label.new('op')
105
- @struct << Token::Int64.new(0)
230
+ def reg(id, value = nil)
231
+ return @registers[id] if value.nil?
232
+ return if id == REG_ZERO # $0 cannot be overwritten
233
+ @registers[id] = value
234
+ end
106
235
 
107
- # system call
108
- @struct << Token::Label.new('sc')
109
- @struct << Token::Int64.new(0)
236
+ def jmp(addr)
237
+ @ip = addr * Consts::WORD_SIZE
238
+ end
110
239
 
111
- # general usage registers
112
- (1..10).each do |i|
113
- @struct << Token::Label.new(format('r%02d', i))
114
- @struct << Token::Int64.new(0)
115
- end
240
+ def branch(rel_addr)
241
+ @ip += rel_addr * Consts::WORD_SIZE
242
+ end
116
243
 
117
- addr = 0
118
- @labels = {}
119
- @struct.each do |token|
120
- token.absolute_addr = addr
121
- addr += token.size
244
+ def bind_opcode(opcode, method)
245
+ fail "OpCode already defined - #{opcode}." if @opcodes.key? opcode
246
+ @opcodes[opcode] = {
247
+ method: method
248
+ }
249
+ end
122
250
 
123
- @labels[token.label] = token if token.is_a? Token::Label
124
- end
251
+ def iterate
252
+ opcode = subsystem(:mem).read @ip
253
+ @ip += Consts::WORD_SIZE
254
+ info = Utils.decode_opcode opcode
255
+ send @opcodes[info.cmd][:method], info
125
256
  end
126
257
  end
127
258
  end