superinstance-flux-runtime 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ea9ba7c8b41057f91a8ff10c2d6cb643dffd8ce9bc269678cd23ee725381f588
4
+ data.tar.gz: 8b1c353e2a4b3e021f568972240888c87679b087074f39d94f803caad7e5e9e1
5
+ SHA512:
6
+ metadata.gz: ad2712c1004e3c47645cb40911a5bfc5a3d285fc618517b7a983984a23ed2c485b3e7863a6630df8c36e585418102ea5f6f3b0733cbd8799747f996f97996878
7
+ data.tar.gz: b123e8b9b9a7e356b2ff7a76b5aa09d2a8fb280fb09926f9e7a29be6f1f5f2824374c7ad451b763f5fe6f7ab8e4dcd923d16d09e458d1e199c521de088f37756
@@ -0,0 +1,305 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'flux_vm'
4
+
5
+ module Flux
6
+ # Text assembly to bytecode
7
+ class Assembler
8
+ REGISTER_ALIASES = {
9
+ 'RV' => 8, 'R8' => 8,
10
+ 'A0' => 9, 'R9' => 9,
11
+ 'A1' => 10, 'R10' => 10,
12
+ 'SP' => 11, 'R11' => 11,
13
+ 'FP' => 12, 'R12' => 12,
14
+ 'FL' => 13, 'R13' => 13,
15
+ 'TP' => 14, 'R14' => 14,
16
+ 'LR' => 15, 'R15' => 15
17
+ }.freeze
18
+
19
+ OPCODE_MAP = {
20
+ # Control Flow
21
+ 'HALT' => 0x00, 'NOP' => 0x01, 'RET' => 0x02,
22
+ 'JUMP' => 0x03, 'JUMPIF' => 0x04, 'JUMPIFNOT' => 0x05,
23
+ 'CALL' => 0x06, 'CALLINDIRECT' => 0x07,
24
+ 'YIELD' => 0x08, 'PANIC' => 0x09, 'UNREACHABLE' => 0x0A,
25
+
26
+ # Stack Operations
27
+ 'PUSH' => 0x10, 'POP' => 0x11, 'DUP' => 0x12, 'SWAP' => 0x13,
28
+
29
+ # Integer Arithmetic
30
+ 'IMOV' => 0x20, 'IADD' => 0x21, 'ISUB' => 0x22,
31
+ 'IMUL' => 0x23, 'IDIV' => 0x24, 'IMOD' => 0x25,
32
+ 'INEG' => 0x26, 'IABS' => 0x27,
33
+ 'IINC' => 0x28, 'IDEC' => 0x29,
34
+ 'IMIN' => 0x2A, 'IMAX' => 0x2B,
35
+ 'IAND' => 0x2C, 'IOR' => 0x2D, 'IXOR' => 0x2E,
36
+ 'ISHL' => 0x2F, 'ISHR' => 0x30, 'INOT' => 0x31,
37
+ 'ICMPEQ' => 0x32, 'ICMPNE' => 0x33,
38
+ 'ICMPLT' => 0x34, 'ICMPLE' => 0x35,
39
+ 'ICMPGT' => 0x36, 'ICMPGE' => 0x37,
40
+
41
+ # Float Arithmetic
42
+ 'FMOV' => 0x40, 'FADD' => 0x41, 'FSUB' => 0x42,
43
+ 'FMUL' => 0x43, 'FDIV' => 0x44, 'FMOD' => 0x45,
44
+ 'FNEG' => 0x46, 'FABS' => 0x47, 'FSQRT' => 0x48,
45
+ 'FFLOOR' => 0x49, 'FCEIL' => 0x4A, 'FROUND' => 0x4B,
46
+ 'FMIN' => 0x4C, 'FMAX' => 0x4D,
47
+ 'FSIN' => 0x4E, 'FCOS' => 0x4F,
48
+ 'FEXP' => 0x50, 'FLOG' => 0x51,
49
+ 'FCLAMP' => 0x52, 'FLERP' => 0x53,
50
+ 'FCMPEQ' => 0x54, 'FCMPNE' => 0x55,
51
+ 'FCMPLT' => 0x56, 'FCMPLE' => 0x57,
52
+ 'FCMPGT' => 0x58, 'FCMPGE' => 0x59,
53
+
54
+ # Conversions
55
+ 'ITOF' => 0x60, 'FTOI' => 0x61, 'BTOI' => 0x62, 'ITOB' => 0x63,
56
+
57
+ # Memory Operations
58
+ 'LOAD8' => 0x70, 'LOAD16' => 0x71, 'LOAD32' => 0x72, 'LOAD64' => 0x73,
59
+ 'STORE8' => 0x74, 'STORE16' => 0x75, 'STORE32' => 0x76, 'STORE64' => 0x77,
60
+ 'LOADADDR' => 0x78, 'STACKALLOC' => 0x79,
61
+
62
+ # A2A Communication
63
+ 'ASEND' => 0x80, 'ARECV' => 0x81, 'AASK' => 0x82,
64
+ 'ATELL' => 0x83, 'ADELEGATE' => 0x84, 'ABROADCAST' => 0x85,
65
+ 'ASUBSCRIBE' => 0x86, 'AWAIT' => 0x87, 'ATRUST' => 0x88, 'AVERIFY' => 0x89,
66
+
67
+ # Type/Meta
68
+ 'CAST' => 0x90, 'SIZEOF' => 0x91, 'TYPEOF' => 0x92,
69
+
70
+ # Bitwise
71
+ 'BAND' => 0xA0, 'BOR' => 0xA1, 'BXOR' => 0xA2,
72
+ 'BSHL' => 0xA3, 'BSHR' => 0xA4, 'BNOT' => 0xA5,
73
+
74
+ # Vector
75
+ 'VLOAD' => 0xB0, 'VSTORE' => 0xB1, 'VADD' => 0xB2,
76
+ 'VMUL' => 0xB3, 'VDOT' => 0xB4
77
+ }.freeze
78
+
79
+ def initialize
80
+ @labels = {}
81
+ end
82
+
83
+ def assemble(source_code)
84
+ output = []
85
+ lines = source_code.lines.map(&:chomp)
86
+
87
+ # Single pass: collect labels and build bytecode
88
+ @labels = {}
89
+ pc = 0
90
+
91
+ # First pass: find labels
92
+ lines.each do |line|
93
+ line = strip_comment(line)
94
+ next if line.empty?
95
+
96
+ if line.end_with?(':')
97
+ label_name = line.chomp(':').upcase
98
+ @labels[label_name] = pc
99
+ else
100
+ pc += estimate_size(line)
101
+ end
102
+ end
103
+
104
+ # Second pass: generate bytecode
105
+ pc = 0
106
+ lines.each do |line|
107
+ line = strip_comment(line)
108
+ next if line.empty?
109
+ next if line.end_with?(':')
110
+
111
+ bytes = encode_instruction(line, pc)
112
+ output.concat(bytes)
113
+ pc += bytes.length
114
+ end
115
+
116
+ output.pack('C*')
117
+ end
118
+
119
+ private
120
+
121
+ def strip_comment(line)
122
+ line.split(/\/\/|#/).first.to_s.strip
123
+ end
124
+
125
+ def estimate_size(line)
126
+ parts = line.upcase.split(/\s+/)
127
+ op = parts[0]
128
+ opcode = OPCODE_MAP[op]
129
+ return 1 unless opcode
130
+
131
+ case opcode
132
+ when 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A then 1
133
+ when 0x10, 0x11, 0x12, 0x13, 0x20, 0x40 then 3
134
+ when 0x21..0x37, 0x41..0x59, 0x60..0x63, 0x90..0x92, 0xA0..0xA5, 0xB2..0xB4 then 4
135
+ when 0x28, 0x29, 0x79 then 4
136
+ when 0x70..0x78, 0xB0, 0xB1 then 5
137
+ when 0x03, 0x04, 0x05 then 4
138
+ when 0x06 then 4
139
+ when 0x07 then 3
140
+ when 0x80..0x89 then 4
141
+ else 4
142
+ end
143
+ end
144
+
145
+ def encode_instruction(line, pc)
146
+ parts = line.upcase.split(/\s+/)
147
+ op = parts[0]
148
+
149
+ # Join remaining parts with space and parse comma-separated operands
150
+ raw_args = parts[1..].join(' ')
151
+ args = raw_args.split(',').map(&:strip).reject(&:empty?)
152
+
153
+ opcode = OPCODE_MAP[op]
154
+ raise ArgumentError, "Unknown opcode: #{op}" unless opcode
155
+
156
+ bytes = [opcode]
157
+
158
+ case opcode
159
+ # Format A: no operands
160
+ when 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A
161
+ bytes
162
+
163
+ # Format B: Rd, Rs
164
+ when 0x10, 0x11, 0x12, 0x20, 0x40
165
+ bytes << parse_register(args[0]) << parse_register(args[1] || 'R0')
166
+
167
+ when 0x13 # Swap: Ra, Rb
168
+ bytes << parse_register(args[0]) << parse_register(args[1] || 'R0')
169
+
170
+ # Format C: Rd, Ra, Rb
171
+ when 0x21..0x37, 0x41..0x59, 0x60..0x63, 0x90..0x92, 0xA0..0xA5, 0xB2..0xB4
172
+ bytes << parse_register(args[0]) << parse_register(args[1] || 'R0') << parse_register(args[2] || 'R0')
173
+
174
+ # Format D: Rd, imm16
175
+ when 0x28, 0x29
176
+ bytes << parse_register(args[0]) << encode_imm16(args[1] || '0')
177
+
178
+ when 0x79 # StackAlloc
179
+ bytes << parse_register(args[0] || 'R0') << encode_imm16(args[1] || '0')
180
+
181
+ # Format E: Rd, Rb, off16
182
+ when 0x70..0x78, 0xB0, 0xB1
183
+ bytes << parse_register(args[0]) << parse_register(args[1] || 'R0') << encode_imm16(args[2] || '0')
184
+
185
+ # Format G: variable
186
+ when 0x03 # Jump
187
+ offset = resolve_label(args[0], pc, 4) || 0
188
+ bytes << 4 << encode_imm16(offset.to_s)
189
+
190
+ when 0x04, 0x05 # JumpIf, JumpIfNot
191
+ rd = parse_register(args[0] || 'R0')
192
+ offset = resolve_label(args[1], pc, 5) || 0
193
+ bytes << 5 << rd << encode_imm16(offset.to_s)
194
+
195
+ when 0x06 # Call
196
+ func_idx = resolve_func_idx(args[0] || '0')
197
+ bytes << 4 << encode_imm16(func_idx.to_s)
198
+
199
+ when 0x07 # CallIndirect
200
+ reg = parse_register(args[0] || 'R0')
201
+ bytes << 3 << reg
202
+
203
+ when 0x80 # ASend
204
+ agent_id = parse_immediate(args[0] || '0')
205
+ reg = parse_register(args[1] || 'R0')
206
+ bytes << 4 << agent_id << reg
207
+
208
+ when 0x81 # ARecv
209
+ agent_id = parse_immediate(args[0] || '0')
210
+ reg = parse_register(args[1] || 'R0')
211
+ bytes << 4 << agent_id << reg
212
+
213
+ when 0x82 # AAsk
214
+ agent_id = parse_immediate(args[0] || '0')
215
+ reg = parse_register(args[1] || 'R0')
216
+ bytes << 4 << agent_id << reg
217
+
218
+ when 0x83 # ATell
219
+ agent_id = parse_immediate(args[0] || '0')
220
+ reg = parse_register(args[1] || 'R0')
221
+ bytes << 4 << agent_id << reg
222
+
223
+ when 0x84 # ADelegate
224
+ agent_id = parse_immediate(args[0] || '0')
225
+ bc_start = resolve_func_idx(args[1] || '0')
226
+ bytes << 4 << agent_id << encode_imm16(bc_start.to_s)
227
+
228
+ when 0x85 # ABroadcast
229
+ reg = parse_register(args[0] || 'R0')
230
+ bytes << 3 << reg
231
+
232
+ when 0x86 # ASubscribe
233
+ channel_id = parse_immediate(args[0] || '0')
234
+ bytes << 3 << channel_id
235
+
236
+ when 0x87 # AWait
237
+ cond_reg = parse_register(args[0] || 'R0')
238
+ bytes << 3 << cond_reg
239
+
240
+ when 0x88 # ATrust
241
+ agent_id = parse_immediate(args[0] || '0')
242
+ level = parse_immediate(args[1] || '0')
243
+ bytes << 4 << agent_id << level
244
+
245
+ when 0x89 # AVerify
246
+ agent_id = parse_immediate(args[0] || '0')
247
+ result_reg = parse_register(args[1] || 'R0')
248
+ bytes << 4 << agent_id << result_reg
249
+
250
+ else
251
+ bytes
252
+ end
253
+ end
254
+
255
+ def parse_register(name)
256
+ return 0 unless name
257
+
258
+ name = name.to_s.strip.upcase
259
+ return REGISTER_ALIASES[name] if REGISTER_ALIASES[name]
260
+
261
+ if name.start_with?('R')
262
+ num = name[1..].to_i
263
+ return num if num >= 0 && num < 16
264
+ end
265
+
266
+ raise ArgumentError, "Invalid register: #{name}"
267
+ end
268
+
269
+ def parse_immediate(value)
270
+ value = value.to_s.strip
271
+ if value.start_with?('0x', '0X')
272
+ value[2..].to_i(16)
273
+ else
274
+ value.to_i
275
+ end
276
+ end
277
+
278
+ def encode_imm16(value)
279
+ val = parse_immediate(value)
280
+ [val & 0xFF, (val >> 8) & 0xFF]
281
+ end
282
+
283
+ def resolve_func_idx(value)
284
+ value = value.to_s.strip
285
+ if @labels[value.upcase]
286
+ @labels[value.upcase]
287
+ elsif @labels["FUNC_#{value.upcase}"]
288
+ @labels["FUNC_#{value.upcase}"]
289
+ else
290
+ value.to_i
291
+ end
292
+ end
293
+
294
+ def resolve_label(name, current_pc, instruction_size)
295
+ return nil unless name
296
+
297
+ name = name.to_s.strip.upcase
298
+ if @labels[name]
299
+ @labels[name] - current_pc - instruction_size
300
+ else
301
+ nil
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'flux_vm'
4
+ require_relative 'assembler'
5
+ require_relative 'disassembler'
6
+ require_relative 'loader'
7
+
8
+ module Flux
9
+ # Command-line interface for FLUX VM
10
+ class CLI
11
+ def self.run(args)
12
+ new.run(args)
13
+ end
14
+
15
+ def run(args)
16
+ command = args[0]
17
+ path = args[1]
18
+
19
+ case command
20
+ when 'run'
21
+ run_bytecode(path)
22
+ when 'asm'
23
+ assemble_file(path)
24
+ when 'dis'
25
+ disassemble_file(path)
26
+ when 'repl'
27
+ start_repl
28
+ when 'help', nil
29
+ print_help
30
+ else
31
+ $stderr.puts "Unknown command: #{command}"
32
+ print_help
33
+ exit 1
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def run_bytecode(path)
40
+ unless path
41
+ $stderr.puts 'Error: No file specified'
42
+ exit 1
43
+ end
44
+
45
+ loader = Loader.new
46
+ bytecode = loader.load_file(path)
47
+
48
+ vm = FluxVM.new
49
+ vm.load(bytecode)
50
+
51
+ trap('INT') do
52
+ $stderr.puts "\nInterrupted"
53
+ exit 1
54
+ end
55
+
56
+ vm.run
57
+
58
+ puts "VM state: #{vm.state}"
59
+ puts "Cycles: #{vm.stats[:cycles]}"
60
+ puts "Instructions: #{vm.stats[:instruction_count]}"
61
+ puts "Registers: #{vm.regs}"
62
+ end
63
+
64
+ def assemble_file(path)
65
+ unless path
66
+ $stderr.puts 'Error: No file specified'
67
+ exit 1
68
+ end
69
+
70
+ source = File.read(path)
71
+ asm = Assembler.new
72
+ bytecode = asm.assemble(source)
73
+
74
+ output_path = path.sub(/\.asm$/, '.flux')
75
+ output_path = "#{path}.flux" unless path.include?('.')
76
+
77
+ File.binwrite(output_path, bytecode)
78
+ puts "Assembled: #{path} -> #{output_path} (#{bytecode.bytesize} bytes)"
79
+ end
80
+
81
+ def disassemble_file(path)
82
+ unless path
83
+ $stderr.puts 'Error: No file specified'
84
+ exit 1
85
+ end
86
+
87
+ loader = Loader.new
88
+ bytecode = loader.load_file(path)
89
+
90
+ dis = Disassembler.new
91
+ puts dis.disassemble(bytecode)
92
+ end
93
+
94
+ def start_repl
95
+ puts 'FLUX REPL v1.0.0'
96
+ puts 'Type "help" for commands, "quit" to exit'
97
+ puts
98
+
99
+ vm = FluxVM.new
100
+ loader = Loader.new
101
+ asm = Assembler.new
102
+
103
+ loop do
104
+ print 'flux> '
105
+ line = gets
106
+ break unless line
107
+
108
+ line = line.strip
109
+ next if line.empty?
110
+
111
+ case line
112
+ when 'quit', 'exit', 'q'
113
+ break
114
+ when 'help', '?'
115
+ puts 'Commands:'
116
+ puts ' run - Run loaded bytecode'
117
+ puts ' step - Step one instruction'
118
+ puts ' regs - Show registers'
119
+ puts ' state - Show VM state'
120
+ puts ' reset - Reset VM'
121
+ puts ' load <file> - Load bytecode file'
122
+ puts ' asm <code> - Assemble and load'
123
+ puts ' dis - Disassemble loaded bytecode'
124
+ puts ' quit - Exit REPL'
125
+ when 'run'
126
+ vm.run
127
+ puts "State: #{vm.state}"
128
+ when 'step'
129
+ vm.step
130
+ puts "State: #{vm.state}"
131
+ when 'regs'
132
+ puts vm.regs.inspect
133
+ when 'state'
134
+ puts "State: #{vm.state}"
135
+ puts "PC: #{vm.pc}"
136
+ puts "SP: #{vm.sp}"
137
+ when 'reset'
138
+ vm.reset
139
+ puts 'VM reset'
140
+ when /^load\s+(.+)$/
141
+ path = $1
142
+ bytecode = loader.load_file(path)
143
+ vm.load(bytecode)
144
+ puts "Loaded #{bytecode.bytesize} bytes"
145
+ when /^asm\s+(.+)$/
146
+ code = $1
147
+ begin
148
+ bytecode = asm.assemble(code)
149
+ vm.load(bytecode)
150
+ puts "Assembled #{bytecode.bytesize} bytes"
151
+ rescue => e
152
+ puts "Error: #{e.message}"
153
+ end
154
+ when 'dis'
155
+ dis = Disassembler.new
156
+ # Disassemble what's in memory
157
+ puts dis.disassemble(vm.instance_variable_get(:memory).pack('C*'))
158
+ else
159
+ puts "Unknown command: #{line}"
160
+ end
161
+ end
162
+ end
163
+
164
+ def print_help
165
+ puts <<~HELP
166
+ FLUX Runtime v1.0.0
167
+ Pure Ruby FLUX ISA v3.0 Virtual Machine
168
+
169
+ Usage: flux <command> [options]
170
+
171
+ Commands:
172
+ run <file> Run a FLUX bytecode file
173
+ asm <file> Assemble .asm file to .flux
174
+ dis <file> Disassemble .flux to .asm
175
+ repl Start interactive REPL
176
+ help Show this help
177
+
178
+ Examples:
179
+ flux run program.flux
180
+ flux asm source.asm
181
+ flux dis program.flux
182
+ HELP
183
+ end
184
+ end
185
+ end
186
+
187
+ # Run CLI if executed directly
188
+ if $PROGRAM_NAME == __FILE__ || File.basename($PROGRAM_NAME) == 'flux'
189
+ Flux::CLI.run(ARGV)
190
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'opcode'
4
+
5
+ module Flux
6
+ # Bytecode to text disassembly
7
+ class Disassembler
8
+ include OpcodeRegistry
9
+
10
+ MNEMONIC_MAP = OPCODE_CLASSES.transform_keys(&:to_i).freeze
11
+
12
+ def disassemble(bytecode_string)
13
+ bytecode = bytecode_string.dup.force_encoding('BINARY')
14
+ output = []
15
+ pc = 0
16
+
17
+ while pc < bytecode.bytesize
18
+ offset = pc
19
+ opcode = bytecode.getbyte(pc)
20
+ pc += 1
21
+
22
+ mnemonic = OPCODE_NAMES[opcode] || "Unknown_0x#{opcode.to_s(16).upcase}"
23
+ line = "#{mnemonic}"
24
+
25
+ case opcode
26
+ # Format A: 1 byte
27
+ when 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A
28
+ # No operands
29
+
30
+ # Format B: +Rd +Rs (2 bytes)
31
+ when 0x10, 0x11, 0x12, 0x20, 0x40
32
+ rd = bytecode.getbyte(pc)
33
+ rs = bytecode.getbyte(pc + 1)
34
+ line = "#{mnemonic} R#{rd}, R#{rs}"
35
+ pc += 2
36
+
37
+ when 0x13 # Swap: Ra, Rb
38
+ ra = bytecode.getbyte(pc)
39
+ rb = bytecode.getbyte(pc + 1)
40
+ line = "#{mnemonic} R#{ra}, R#{rb}"
41
+ pc += 2
42
+
43
+ # Format C: +Rd +Ra +Rb (3 bytes)
44
+ when 0x21..0x37, 0x41..0x59, 0x60..0x63, 0x90..0x92, 0xA0..0xA5, 0xB2..0xB4
45
+ rd = bytecode.getbyte(pc)
46
+ ra = bytecode.getbyte(pc + 1)
47
+ rb = bytecode.getbyte(pc + 2)
48
+ line = "#{mnemonic} R#{rd}, R#{ra}, R#{rb}"
49
+ pc += 3
50
+
51
+ # Format D: +Rd +imm16 (3 bytes)
52
+ when 0x28, 0x29, 0x79
53
+ rd = bytecode.getbyte(pc)
54
+ imm_lo = bytecode.getbyte(pc + 1)
55
+ imm_hi = bytecode.getbyte(pc + 2)
56
+ imm = imm_lo | (imm_hi << 8)
57
+ # Sign extend
58
+ imm = (imm << 16) >> 16
59
+ line = "#{mnemonic} R#{rd}, #{imm}"
60
+ pc += 3
61
+
62
+ # Format E: +Rd +Rb +off16 (4 bytes)
63
+ when 0x70..0x78
64
+ rd = bytecode.getbyte(pc)
65
+ rb = bytecode.getbyte(pc + 1)
66
+ off_lo = bytecode.getbyte(pc + 2)
67
+ off_hi = bytecode.getbyte(pc + 3)
68
+ off = off_lo | (off_hi << 8)
69
+ line = "#{mnemonic} R#{rd}, R#{rb}, #{off}"
70
+ pc += 4
71
+
72
+ when 0xB0, 0xB1 # VLoad, VStore
73
+ rd = bytecode.getbyte(pc)
74
+ rb = bytecode.getbyte(pc + 1)
75
+ off_lo = bytecode.getbyte(pc + 2)
76
+ off_hi = bytecode.getbyte(pc + 3)
77
+ off = off_lo | (off_hi << 8)
78
+ line = "#{mnemonic} R#{rd}, R#{rb}, #{off}"
79
+ pc += 4
80
+
81
+ # Format G: variable
82
+ when 0x03, 0x04, 0x05 # Jump, JumpIf, JumpIfNot
83
+ length = bytecode.getbyte(pc)
84
+ pc += 1
85
+ off_lo = bytecode.getbyte(pc)
86
+ off_hi = bytecode.getbyte(pc + 1)
87
+ off = off_lo | (off_hi << 8)
88
+ off = (off << 16) >> 16 # Sign extend
89
+ line = "#{mnemonic} #{off}"
90
+ pc += 2
91
+
92
+ when 0x06 # Call
93
+ length = bytecode.getbyte(pc)
94
+ pc += 1
95
+ func_lo = bytecode.getbyte(pc)
96
+ func_hi = bytecode.getbyte(pc + 1)
97
+ func_idx = func_lo | (func_hi << 8)
98
+ line = "#{mnemonic} #{func_idx}"
99
+ pc += 2
100
+
101
+ when 0x07 # CallIndirect
102
+ length = bytecode.getbyte(pc)
103
+ pc += 1
104
+ reg = bytecode.getbyte(pc)
105
+ line = "#{mnemonic} R#{reg}"
106
+ pc += 1
107
+
108
+ when 0x80 # ASend
109
+ length = bytecode.getbyte(pc)
110
+ pc += 1
111
+ agent_id = bytecode.getbyte(pc)
112
+ reg = bytecode.getbyte(pc + 1)
113
+ line = "#{mnemonic} #{agent_id}, R#{reg}"
114
+ pc += 2
115
+
116
+ when 0x81 # ARecv
117
+ length = bytecode.getbyte(pc)
118
+ pc += 1
119
+ agent_id = bytecode.getbyte(pc)
120
+ reg = bytecode.getbyte(pc + 1)
121
+ line = "#{mnemonic} #{agent_id}, R#{reg}"
122
+ pc += 2
123
+
124
+ when 0x82 # AAsk
125
+ length = bytecode.getbyte(pc)
126
+ pc += 1
127
+ agent_id = bytecode.getbyte(pc)
128
+ reg = bytecode.getbyte(pc + 1)
129
+ line = "#{mnemonic} #{agent_id}, R#{reg}"
130
+ pc += 2
131
+
132
+ when 0x83 # ATell
133
+ length = bytecode.getbyte(pc)
134
+ pc += 1
135
+ agent_id = bytecode.getbyte(pc)
136
+ reg = bytecode.getbyte(pc + 1)
137
+ line = "#{mnemonic} #{agent_id}, R#{reg}"
138
+ pc += 2
139
+
140
+ when 0x84 # ADelegate
141
+ length = bytecode.getbyte(pc)
142
+ pc += 1
143
+ agent_id = bytecode.getbyte(pc)
144
+ bc_lo = bytecode.getbyte(pc + 1)
145
+ bc_hi = bytecode.getbyte(pc + 2)
146
+ bc_start = bc_lo | (bc_hi << 8)
147
+ line = "#{mnemonic} #{agent_id}, #{bc_start}"
148
+ pc += 3
149
+
150
+ when 0x85 # ABroadcast
151
+ length = bytecode.getbyte(pc)
152
+ pc += 1
153
+ reg = bytecode.getbyte(pc)
154
+ line = "#{mnemonic} R#{reg}"
155
+ pc += 1
156
+
157
+ when 0x86 # ASubscribe
158
+ length = bytecode.getbyte(pc)
159
+ pc += 1
160
+ channel_id = bytecode.getbyte(pc)
161
+ line = "#{mnemonic} #{channel_id}"
162
+ pc += 1
163
+
164
+ when 0x87 # AWait
165
+ length = bytecode.getbyte(pc)
166
+ pc += 1
167
+ cond_reg = bytecode.getbyte(pc)
168
+ line = "#{mnemonic} R#{cond_reg}"
169
+ pc += 1
170
+
171
+ when 0x88 # ATrust
172
+ length = bytecode.getbyte(pc)
173
+ pc += 1
174
+ agent_id = bytecode.getbyte(pc)
175
+ level = bytecode.getbyte(pc + 1)
176
+ line = "#{mnemonic} #{agent_id}, #{level}"
177
+ pc += 2
178
+
179
+ when 0x89 # AVerify
180
+ length = bytecode.getbyte(pc)
181
+ pc += 1
182
+ agent_id = bytecode.getbyte(pc)
183
+ result_reg = bytecode.getbyte(pc + 1)
184
+ line = "#{mnemonic} #{agent_id}, R#{result_reg}"
185
+ pc += 2
186
+
187
+ else
188
+ pc += 3 # Skip unknown bytes
189
+ end
190
+
191
+ output << " #{offset.to_s(16).rjust(8, '0')}: #{line}"
192
+ end
193
+
194
+ output.join("\n")
195
+ end
196
+ end
197
+ end