-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathum.rb
More file actions
104 lines (101 loc) · 2.76 KB
/
um.rb
File metadata and controls
104 lines (101 loc) · 2.76 KB
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
PROGRAM_SEGMENT = 0
NUM_REGISTERS = 8
LOAD_VALUE_NUM_BITS = 25
LOAD_VALUE_MASK = (1 << LOAD_VALUE_NUM_BITS) - 1
OP_CONDITIONAL_MOVE = 0
OP_SEGMENTED_LOAD = 1
OP_SEGMENTED_STORE = 2
OP_ADDITION = 3
OP_MULTIPLICATION = 4
OP_DIVISION = 5
OP_BITWISE_NAND = 6
OP_HALT = 7
OP_MAP_SEGMENT = 8
OP_UNMAP_SEGMENT = 9
OP_OUTPUT = 10
OP_INPUT = 11
OP_LOAD_PROGRAM = 12
OP_LOAD_VALUE = 13
def um_run(program)
segments = [program]
registers = [0] * NUM_REGISTERS
program_counter = 0
free_segment_ids = []
while true
instruction = program[program_counter]
program_counter += 1
c = instruction & 0b111
b = (instruction >> 3) & 0b111
a = (instruction >> 6) & 0b111
opcode = (instruction >> 28) & 0xff
case opcode
when OP_CONDITIONAL_MOVE
if registers[c] != 0
registers[a] = registers[b]
end
when OP_SEGMENTED_LOAD
registers[a] = segments[registers[b]][registers[c]]
when OP_SEGMENTED_STORE
segment = segments[registers[a]]
raise "Location out of bounds" if registers[b] >= segment.length
segment[registers[b]] = registers[c]
when OP_ADDITION
registers[a] = (registers[b] + registers[c]) & 0xffffffff
when OP_MULTIPLICATION
registers[a] = (registers[b] * registers[c]) & 0xffffffff
when OP_DIVISION
registers[a] = registers[b] / registers[c]
when OP_BITWISE_NAND
# Something something NAND is weird on Ruby bignum
registers[a] = ~(registers[b] & registers[c]) & ((1 << 32) - 1)
when OP_HALT
break
when OP_MAP_SEGMENT
size = registers[c]
if free_segment_ids.empty?
segments << [0] * size
registers[b] = segment_id = segments.length - 1
else
registers[b] = segment_id = free_segment_ids.pop
segments[segment_id].fill(0, 0, size)
end
when OP_UNMAP_SEGMENT
free_segment_ids << registers[c]
when OP_OUTPUT
char = registers[c]
raise "Char too small" if char < 0
raise "Char too big" if char > 255
printf "%c", char
when OP_INPUT
byte = STDIN.read(1)
if byte
registers[c] = byte.ord
else
registers[c] = 0xffffffff
end
when OP_LOAD_PROGRAM
if registers[b] != 0
program = segments[PROGRAM_SEGMENT]
program.clear
program.concat(segments[registers[b]])
end
program_counter = registers[c]
when OP_LOAD_VALUE
value = instruction & LOAD_VALUE_MASK
a = (instruction >> LOAD_VALUE_NUM_BITS) & 0b111
registers[a] = value
else
raise "Invalid opcode #{opcode}"
end
end
end
filename = ARGV[0]
file = File.open(filename, "rb")
program = []
loop do
instruction = file.read(4)&.unpack("L>")&.[](0)
break unless instruction
program << instruction
end
puts "Done loading."
um_run(program)