haxor 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +27 -0
- data/README.md +414 -0
- data/Rakefile +1 -0
- data/bin/hcc +21 -0
- data/bin/hld +38 -0
- data/bin/hvm +17 -0
- data/examples/build.sh +10 -0
- data/examples/guess-the-number.hax +100 -0
- data/haxor.gemspec +24 -0
- data/lib/haxor.rb +44 -0
- data/lib/haxor/compiler/component/arithmetic.rb +55 -0
- data/lib/haxor/compiler/component/base.rb +37 -0
- data/lib/haxor/compiler/component/data.rb +32 -0
- data/lib/haxor/compiler/component/jumps.rb +95 -0
- data/lib/haxor/compiler/component/logical.rb +43 -0
- data/lib/haxor/compiler/component/other.rb +21 -0
- data/lib/haxor/compiler/component/transfer.rb +29 -0
- data/lib/haxor/compiler/component/various.rb +33 -0
- data/lib/haxor/compiler/core.rb +134 -0
- data/lib/haxor/compiler/section.rb +6 -0
- data/lib/haxor/compiler/unit.rb +39 -0
- data/lib/haxor/consts.rb +28 -0
- data/lib/haxor/header.rb +40 -0
- data/lib/haxor/linker.rb +89 -0
- data/lib/haxor/token/base.rb +7 -0
- data/lib/haxor/token/cmd.rb +9 -0
- data/lib/haxor/token/data.rb +17 -0
- data/lib/haxor/token/int64.rb +17 -0
- data/lib/haxor/token/label.rb +23 -0
- data/lib/haxor/token/pointer.rb +13 -0
- data/lib/haxor/vm/core.rb +53 -0
- data/lib/haxor/vm/cpu/core.rb +129 -0
- data/lib/haxor/vm/cpu/unit/arithmetic.rb +92 -0
- data/lib/haxor/vm/cpu/unit/base.rb +46 -0
- data/lib/haxor/vm/cpu/unit/jumps.rb +123 -0
- data/lib/haxor/vm/cpu/unit/logical.rb +59 -0
- data/lib/haxor/vm/cpu/unit/transfer.rb +37 -0
- data/lib/haxor/vm/cpu/unit/various.rb +47 -0
- data/lib/haxor/vm/mem.rb +101 -0
- data/lib/haxor/vm/os.rb +115 -0
- data/lib/haxor/vm/stack.rb +31 -0
- data/lib/haxor/vm/subsystem.rb +16 -0
- data/media/memory.png +0 -0
- data/media/vm.png +0 -0
- metadata +122 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Logical < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'and', :cmd_and
|
7
|
+
bind_cmd 'neg', :cmd_neg
|
8
|
+
bind_cmd 'not', :cmd_not
|
9
|
+
bind_cmd 'or', :cmd_or
|
10
|
+
bind_cmd 'xor', :cmd_xor
|
11
|
+
end
|
12
|
+
|
13
|
+
def cmd_and(a, b)
|
14
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_AND | offset_flags(a, b))
|
15
|
+
parse_value a
|
16
|
+
parse_value b
|
17
|
+
end
|
18
|
+
|
19
|
+
def cmd_neg(a)
|
20
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_NEG | offset_flags(a))
|
21
|
+
parse_value a
|
22
|
+
end
|
23
|
+
|
24
|
+
def cmd_not(a)
|
25
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_NOT | offset_flags(a))
|
26
|
+
parse_value a
|
27
|
+
end
|
28
|
+
|
29
|
+
def cmd_or(a, b)
|
30
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_OR | offset_flags(a, b))
|
31
|
+
parse_value a
|
32
|
+
parse_value b
|
33
|
+
end
|
34
|
+
|
35
|
+
def cmd_xor(a, b)
|
36
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Logical::OP_XOR | offset_flags(a, b))
|
37
|
+
parse_value a
|
38
|
+
parse_value b
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Other < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'label', :cmd_label
|
7
|
+
bind_cmd 'rem', :cmd_rem
|
8
|
+
bind_cmd '#', :cmd_rem
|
9
|
+
end
|
10
|
+
|
11
|
+
def cmd_rem(*_args)
|
12
|
+
# nothing
|
13
|
+
end
|
14
|
+
|
15
|
+
def cmd_label(*args)
|
16
|
+
add Token::Label.new(args[0])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Transfer < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'mov', :cmd_mov
|
7
|
+
bind_cmd 'push', :cmd_push
|
8
|
+
bind_cmd 'pop', :cmd_pop
|
9
|
+
end
|
10
|
+
|
11
|
+
def cmd_push(a)
|
12
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_PUSH | offset_flags(a))
|
13
|
+
parse_value a
|
14
|
+
end
|
15
|
+
|
16
|
+
def cmd_pop(a)
|
17
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_POP | offset_flags(a))
|
18
|
+
parse_value a
|
19
|
+
end
|
20
|
+
|
21
|
+
def cmd_mov(a, b)
|
22
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Transfer::OP_MOV | offset_flags(a, b))
|
23
|
+
parse_value a
|
24
|
+
parse_value b
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
module Component
|
4
|
+
class Various < Base
|
5
|
+
def register
|
6
|
+
bind_cmd 'nop', :cmd_nop
|
7
|
+
bind_cmd 'lea', :cmd_lea
|
8
|
+
bind_cmd 'int', :cmd_int
|
9
|
+
bind_cmd 'syscall', :cmd_syscall
|
10
|
+
end
|
11
|
+
|
12
|
+
def cmd_nop(*_args)
|
13
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_NOP)
|
14
|
+
end
|
15
|
+
|
16
|
+
def cmd_lea(a, b)
|
17
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_LEA | offset_flags(a, b))
|
18
|
+
parse_value a
|
19
|
+
parse_value b
|
20
|
+
end
|
21
|
+
|
22
|
+
def cmd_int(a)
|
23
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_INT)
|
24
|
+
parse_value a
|
25
|
+
end
|
26
|
+
|
27
|
+
def cmd_syscall
|
28
|
+
add Token::Cmd.new(Vm::Cpu::Unit::Various::OP_SYSCALL)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
class Core
|
4
|
+
def initialize
|
5
|
+
@units = []
|
6
|
+
@cmds = {}
|
7
|
+
@autolabel = 0
|
8
|
+
@prev_sections = []
|
9
|
+
|
10
|
+
bind_cmd 'section', self, :cmd_section
|
11
|
+
end
|
12
|
+
|
13
|
+
def register_unit(unit)
|
14
|
+
@units << unit
|
15
|
+
unit.compiler = self
|
16
|
+
unit.register
|
17
|
+
end
|
18
|
+
|
19
|
+
def bind_cmd(cmd, object, method)
|
20
|
+
@cmds[cmd] = {
|
21
|
+
object: object,
|
22
|
+
method: method
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def cmd_section(name)
|
27
|
+
@unit.section = name[1..-1].to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
# this is ugly and must be reworked
|
31
|
+
def split_arguments(string)
|
32
|
+
inside_quotes = false
|
33
|
+
splits = []
|
34
|
+
string.each_char.with_index(0) do |x, i|
|
35
|
+
if x == '"' && string[i - 1] != '\\'
|
36
|
+
inside_quotes ^= true
|
37
|
+
end
|
38
|
+
|
39
|
+
if x == ',' && !inside_quotes
|
40
|
+
splits << i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
splits << string.size
|
45
|
+
|
46
|
+
last_x = 0
|
47
|
+
args = []
|
48
|
+
splits.each do |x|
|
49
|
+
args << string[last_x...x]
|
50
|
+
last_x = x + 1
|
51
|
+
end
|
52
|
+
args.map!(&:strip).delete_if { |x| x.length == 0 }
|
53
|
+
end
|
54
|
+
|
55
|
+
def compile(filename)
|
56
|
+
input = File.read(filename, encoding: 'ASCII-8BIT')
|
57
|
+
|
58
|
+
@unit = Unit.new
|
59
|
+
|
60
|
+
input.lines.map(&:chomp).each_with_index do |line, index|
|
61
|
+
next if line.empty?
|
62
|
+
|
63
|
+
tmp = line.split ' ', 2
|
64
|
+
cmd = tmp[0]
|
65
|
+
args = split_arguments(tmp[1] || '')
|
66
|
+
|
67
|
+
fail "Unknown command #{cmd} in line #{index}." unless @cmds.key? cmd
|
68
|
+
|
69
|
+
@cmds[cmd][:object].send(@cmds[cmd][:method], *args)
|
70
|
+
end
|
71
|
+
|
72
|
+
@unit.save(filename + '.u')
|
73
|
+
end
|
74
|
+
|
75
|
+
def add(token)
|
76
|
+
@unit.add token
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_number(value)
|
80
|
+
md = value.match(/\A([-+]?[0-9]+[0-9A-F]*)([bhd]?)\z/)
|
81
|
+
fail if md.nil?
|
82
|
+
|
83
|
+
case md[2]
|
84
|
+
when 'b'
|
85
|
+
base = 2
|
86
|
+
when 'h'
|
87
|
+
base = 16
|
88
|
+
when 'd'
|
89
|
+
else
|
90
|
+
base = 10
|
91
|
+
end
|
92
|
+
|
93
|
+
md[1].to_i(base)
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_value(value)
|
97
|
+
value = strip_offset value
|
98
|
+
|
99
|
+
begin
|
100
|
+
number = parse_number(value)
|
101
|
+
@autolabel += 1
|
102
|
+
label = "__autolabel_#{@autolabel}"
|
103
|
+
|
104
|
+
old_section = @unit.section
|
105
|
+
@unit.section = :data
|
106
|
+
add Token::Label.new(label)
|
107
|
+
add Token::Int64.new(number)
|
108
|
+
@unit.section = old_section
|
109
|
+
|
110
|
+
add Token::Pointer.new(label)
|
111
|
+
rescue
|
112
|
+
add Token::Pointer.new(value)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def offset_flags(a, b = nil)
|
117
|
+
result = 0
|
118
|
+
result |= Consts::OPCODE_FLG_DA if offset?(a)
|
119
|
+
result |= Consts::OPCODE_FLG_DB if offset?(b)
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
def offset?(value)
|
124
|
+
return false unless value.is_a? String
|
125
|
+
value.start_with?('[') && value.end_with?(']')
|
126
|
+
end
|
127
|
+
|
128
|
+
def strip_offset(value)
|
129
|
+
return value[1...-1] if offset?(value)
|
130
|
+
value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Haxor
|
2
|
+
module Compiler
|
3
|
+
class Unit
|
4
|
+
attr_reader :section
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@section = :text
|
8
|
+
@sections = {
|
9
|
+
text: [],
|
10
|
+
data: [],
|
11
|
+
bss: []
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(item)
|
16
|
+
@sections[@section] << item
|
17
|
+
end
|
18
|
+
|
19
|
+
def section=(section)
|
20
|
+
fail unless @sections.key? section
|
21
|
+
@section = section
|
22
|
+
end
|
23
|
+
|
24
|
+
def tokens
|
25
|
+
@sections[@section]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.load(filename)
|
29
|
+
Marshal.load File.read(filename)
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(filename)
|
33
|
+
fd = File.open(filename, 'wb')
|
34
|
+
fd.write Marshal.dump(self)
|
35
|
+
fd.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/haxor/consts.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Consts
|
3
|
+
MAJOR = 0
|
4
|
+
MINOR = 1
|
5
|
+
PATCH = 0
|
6
|
+
GEM_VERSION = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
7
|
+
VERSION = (MAJOR << 16) | (MINOR << 8) | PATCH
|
8
|
+
|
9
|
+
ROOT_PATH = __dir__ + '/../..'
|
10
|
+
|
11
|
+
WORD_SIZE = 8 # 64bit
|
12
|
+
WORD_UNPACK = 'q<'
|
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
|
20
|
+
OPCODE_FLG_OFFSET = 8
|
21
|
+
OPCODE_FLG_DA = 1 << OPCODE_FLG_OFFSET # dereference A operand
|
22
|
+
OPCODE_FLG_DB = 2 << OPCODE_FLG_OFFSET # dereference B operand
|
23
|
+
|
24
|
+
# Flags Registry Flags
|
25
|
+
FR_ZERO = 1 << 0 # a-b == 0
|
26
|
+
FR_SIGN = 1 << 1 # a-b < 0
|
27
|
+
end
|
28
|
+
end
|
data/lib/haxor/header.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Header
|
3
|
+
MAGIC = 0x72_65_88_79_82 # HAXOR (ASCII)
|
4
|
+
SIZE = 64 # max 8 values (64bit per entry)
|
5
|
+
|
6
|
+
attr_accessor :version
|
7
|
+
attr_accessor :entry_point
|
8
|
+
attr_accessor :bss_size
|
9
|
+
attr_accessor :stack_size
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@magic = MAGIC
|
13
|
+
end
|
14
|
+
|
15
|
+
def dump
|
16
|
+
result = ''
|
17
|
+
result << [
|
18
|
+
@magic,
|
19
|
+
@version,
|
20
|
+
@entry_point,
|
21
|
+
@bss_size,
|
22
|
+
@stack_size
|
23
|
+
].pack('q<*')
|
24
|
+
result.ljust(SIZE)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse!(data)
|
28
|
+
data = data.unpack('q<*')
|
29
|
+
@magic = data.shift
|
30
|
+
@version = data.shift
|
31
|
+
@entry_point = data.shift
|
32
|
+
@bss_size = data.shift
|
33
|
+
@stack_size = data.shift
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
SIZE
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/haxor/linker.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Haxor
|
2
|
+
class Linker
|
3
|
+
attr_accessor :stack
|
4
|
+
|
5
|
+
SECTIONS = [:text, :data, :bss]
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@cpu = Vm::Cpu::Core.new
|
9
|
+
@units = []
|
10
|
+
@tokens = []
|
11
|
+
@labels = @cpu.labels.clone
|
12
|
+
@stack = 4096
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_unit(filename)
|
16
|
+
@units << Haxor::Compiler::Unit.load(filename)
|
17
|
+
end
|
18
|
+
|
19
|
+
def link(filename)
|
20
|
+
calc_absolute_addr
|
21
|
+
collect_labels
|
22
|
+
unwind_pointers
|
23
|
+
|
24
|
+
output = File.open(filename, 'wb')
|
25
|
+
output.write build
|
26
|
+
output.close
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def walk_tokens(type = nil)
|
32
|
+
SECTIONS.each do |section|
|
33
|
+
@units.each do |unit|
|
34
|
+
unit.section = section
|
35
|
+
unit.tokens.each do |token|
|
36
|
+
next unless type.nil? || token.is_a?(type)
|
37
|
+
yield token, section
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def calc_absolute_addr
|
44
|
+
addr = Consts::RESERVED_MEM
|
45
|
+
walk_tokens do |token|
|
46
|
+
token.absolute_addr = addr
|
47
|
+
addr += token.size
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def collect_labels
|
52
|
+
walk_tokens(Token::Label) do |token|
|
53
|
+
fail "Label already exists: #{token.label}." if @labels.key? token.label
|
54
|
+
@labels[token.label] = token
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
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
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_header
|
66
|
+
bss_size = 0
|
67
|
+
walk_tokens do |token, section|
|
68
|
+
bss_size += token.size if section == :bss
|
69
|
+
end
|
70
|
+
|
71
|
+
hdr = Header.new
|
72
|
+
hdr.version = Consts::VERSION
|
73
|
+
hdr.entry_point = @labels['main'].absolute_addr
|
74
|
+
hdr.stack_size = @stack
|
75
|
+
hdr.bss_size = bss_size
|
76
|
+
hdr
|
77
|
+
end
|
78
|
+
|
79
|
+
def build
|
80
|
+
result = build_header.dump
|
81
|
+
walk_tokens do |token, section|
|
82
|
+
next unless [:text, :data].include? section
|
83
|
+
puts "[#{section}] [#{token.absolute_addr}] #{token}"
|
84
|
+
result << token.to_bytecode
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|