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.
@@ -0,0 +1,1020 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'exceptions'
4
+ require_relative 'opcode'
5
+
6
+ module Flux
7
+ # Main FLUX ISA v3.0 Virtual Machine
8
+ #
9
+ # Register Model:
10
+ # - 16 GP registers (R0-R15, int32)
11
+ # - 16 FP registers (F0-F15, float64)
12
+ # - 16 VR registers (V0-V15, 256 bytes each)
13
+ # - PC, SP, FP_REG, FLAGS, STATE
14
+ #
15
+ # Memory: flat binary string, 64KB default
16
+ class FluxVM
17
+ include OpcodeRegistry
18
+
19
+ # Register aliases per ISA
20
+ RV = 8 # Return value
21
+ A0 = 9 # First argument
22
+ A1 = 10 # Second argument
23
+ SP_REG = 11 # Stack pointer
24
+ FP_REG = 12 # Frame pointer
25
+ FLAGS = 13 # Flags register
26
+ TP = 14 # Temporary
27
+ LR = 15 # Link register
28
+
29
+ # VM states
30
+ RUNNING = :running
31
+ HALTED = :halted
32
+ PANICKED = :panicked
33
+ YIELDED = :yielded
34
+
35
+ # Flag bits
36
+ FLAG_Z = 1 << 0
37
+ FLAG_S = 1 << 1
38
+ FLAG_C = 1 << 2
39
+ FLAG_V = 1 << 3
40
+
41
+ attr_accessor :gp, :fp, :vr
42
+ attr_accessor :pc, :sp, :fp_reg, :flags, :state
43
+ attr_reader :memory_size, :memory
44
+
45
+ # Opcode registry for runtime-registered opcodes
46
+ @@opcode_registry = {}
47
+ @@runtime_opcodes = {}
48
+
49
+ class << self
50
+ attr_accessor :opcode_registry, :runtime_opcodes
51
+
52
+ def register_opcode(opcode_class)
53
+ @@runtime_opcodes[opcode_class.opcode_id] = opcode_class
54
+ end
55
+ end
56
+
57
+ def initialize(memory_size: 65536)
58
+ @memory_size = memory_size
59
+ @memory = [0] * memory_size # Simpler Array of integers
60
+
61
+ # General purpose registers (int32)
62
+ @gp = [0] * 16
63
+
64
+ # Floating point registers (float64)
65
+ @fp = [0.0] * 16
66
+
67
+ # Vector registers (256 bytes each as binary string)
68
+ @vr = Array.new(16) { "\x00" * 256 }
69
+
70
+ # System registers
71
+ @pc = 0
72
+ @sp = memory_size # Stack grows down
73
+ @fp_reg = 0
74
+ @flags = 0
75
+ @state = RUNNING
76
+
77
+ # Stats
78
+ @cycles = 0
79
+ @instruction_count = 0
80
+ end
81
+
82
+ # Load bytecode into memory starting at address 0
83
+ def load(bytecode_string)
84
+ bytecode = bytecode_string.dup.force_encoding('BINARY')
85
+ raise FluxRuntimeError, 'Bytecode too large' if bytecode.bytesize > @memory_size
86
+
87
+ bytecode.bytes.each_with_index do |byte, i|
88
+ @memory[i] = byte
89
+ end
90
+
91
+ @pc = 0
92
+ @sp = @memory_size
93
+ self
94
+ end
95
+
96
+ # Run the VM until halt/panic/yield or max_cycles reached
97
+ def run(max_cycles: nil)
98
+ @state = RUNNING
99
+
100
+ loop do
101
+ break if @state != RUNNING
102
+ break if max_cycles && @cycles >= max_cycles
103
+
104
+ step
105
+ end
106
+
107
+ self
108
+ end
109
+
110
+ # Execute one instruction
111
+ def step
112
+ raise FluxRuntimeError, 'VM not running' unless @state == RUNNING
113
+
114
+ opcode = fetch8
115
+ execute_opcode(opcode)
116
+
117
+ @instruction_count += 1
118
+ @cycles += 1
119
+
120
+ self
121
+ end
122
+
123
+ # Reset VM state
124
+ def reset
125
+ @memory = [0] * @memory_size
126
+ @gp = [0] * 16
127
+ @fp = [0.0] * 16
128
+ @vr = Array.new(16) { "\x00" * 256 }
129
+ @pc = 0
130
+ @sp = @memory_size
131
+ @fp_reg = 0
132
+ @flags = 0
133
+ @state = RUNNING
134
+ @cycles = 0
135
+ @instruction_count = 0
136
+ self
137
+ end
138
+
139
+ # Get register values as hash
140
+ def regs
141
+ {
142
+ pc: @pc,
143
+ sp: @sp,
144
+ fp: @fp_reg,
145
+ flags: @flags,
146
+ state: @state,
147
+ gp: @gp.dup,
148
+ fp_regs: @fp.dup,
149
+ vr: @vr.dup
150
+ }
151
+ end
152
+
153
+ # Current VM state
154
+ def state
155
+ @state
156
+ end
157
+
158
+ # Execution statistics
159
+ def stats
160
+ {
161
+ cycles: @cycles,
162
+ instruction_count: @instruction_count,
163
+ memory_used: @pc
164
+ }
165
+ end
166
+
167
+ # Memory read helpers
168
+ def read_memory(addr, bytes)
169
+ return nil if addr < 0 || addr + bytes > @memory_size
170
+
171
+ (0...bytes).map { |i| @memory[addr + i] }
172
+ end
173
+
174
+ def read_memory_u8(addr)
175
+ return nil if addr < 0 || addr >= @memory_size
176
+
177
+ @memory[addr] & 0xFF
178
+ end
179
+
180
+ def read_memory_i32(addr)
181
+ return nil if addr < 0 || addr + 4 > @memory_size
182
+
183
+ val = (@memory[addr] & 0xFF) |
184
+ ((@memory[addr + 1] & 0xFF) << 8) |
185
+ ((@memory[addr + 2] & 0xFF) << 16) |
186
+ ((@memory[addr + 3] & 0xFF) << 24)
187
+
188
+ # Sign extend
189
+ (val << 24) >> 24
190
+ end
191
+
192
+ def read_memory_u32(addr)
193
+ return nil if addr < 0 || addr + 4 > @memory_size
194
+
195
+ (@memory[addr] & 0xFF) |
196
+ ((@memory[addr + 1] & 0xFF) << 8) |
197
+ ((@memory[addr + 2] & 0xFF) << 16) |
198
+ ((@memory[addr + 3] & 0xFF) << 24)
199
+ end
200
+
201
+ def read_memory_i16(addr)
202
+ return nil if addr < 0 || addr + 2 > @memory_size
203
+
204
+ val = (@memory[addr] & 0xFF) | ((@memory[addr + 1] & 0xFF) << 8)
205
+ (val << 16) >> 16 # Sign extend
206
+ end
207
+
208
+ def read_memory_u16(addr)
209
+ return nil if addr < 0 || addr + 2 > @memory_size
210
+
211
+ (@memory[addr] & 0xFF) | ((@memory[addr + 1] & 0xFF) << 8)
212
+ end
213
+
214
+ # Memory write helpers
215
+ def write_memory(addr, bytes, value)
216
+ return if addr < 0 || addr + bytes > @memory_size
217
+
218
+ case bytes
219
+ when 1
220
+ @memory[addr] = value & 0xFF
221
+ when 2
222
+ @memory[addr] = value & 0xFF
223
+ @memory[addr + 1] = (value >> 8) & 0xFF
224
+ when 4
225
+ @memory[addr] = value & 0xFF
226
+ @memory[addr + 1] = (value >> 8) & 0xFF
227
+ @memory[addr + 2] = (value >> 16) & 0xFF
228
+ @memory[addr + 3] = (value >> 24) & 0xFF
229
+ when 8
230
+ (0..7).each { |i| @memory[addr + i] = (value >> (i * 8)) & 0xFF }
231
+ end
232
+ end
233
+
234
+ # Flag setting helpers
235
+ def set_z(result)
236
+ @flags = (@flags & ~FLAG_Z) | (result.zero? ? FLAG_Z : 0)
237
+ end
238
+
239
+ def set_s(result)
240
+ @flags = (@flags & ~FLAG_S) | ((result < 0) ? FLAG_S : 0)
241
+ end
242
+
243
+ def set_c(result)
244
+ @flags = (@flags & ~FLAG_C) | (result < 0 ? FLAG_C : 0)
245
+ end
246
+
247
+ def set_v(result)
248
+ @flags = (@flags & ~FLAG_V) | (result < 0 ? FLAG_V : 0)
249
+ end
250
+
251
+ def set_zn(result)
252
+ set_z(result)
253
+ set_s(result)
254
+ end
255
+
256
+ def set_znv(result)
257
+ set_z(result)
258
+ set_s(result)
259
+ set_v(result)
260
+ set_c(result)
261
+ end
262
+
263
+ # Stack helpers
264
+ def push(value)
265
+ @sp -= 4
266
+ raise FluxRuntimeError, 'Stack overflow' if @sp < 0
267
+ write_memory(@sp, 4, value)
268
+ end
269
+
270
+ def pop
271
+ raise FluxRuntimeError, 'Stack underflow' if @sp >= @memory_size
272
+ val = read_memory_i32(@sp)
273
+ @sp += 4
274
+ val
275
+ end
276
+
277
+ private
278
+
279
+ def fetch8
280
+ val = read_memory_u8(@pc)
281
+ @pc += 1
282
+ val
283
+ end
284
+
285
+ def fetch16
286
+ lo = fetch8
287
+ hi = fetch8
288
+ (lo | (hi << 8)).tap { |v| (v << 16) >> 16 } # Sign-extend
289
+ end
290
+
291
+ def fetchu16
292
+ lo = fetch8
293
+ hi = fetch8
294
+ lo | (hi << 8)
295
+ end
296
+
297
+ def fetchu32
298
+ lo = fetch16
299
+ hi = fetch16
300
+ lo | (hi << 16)
301
+ end
302
+
303
+ def fetch32
304
+ val = fetchu32
305
+ (val << 24) >> 24
306
+ end
307
+
308
+ def execute_opcode(opcode)
309
+ # Check runtime opcodes first
310
+ if @@runtime_opcodes[opcode]
311
+ @@runtime_opcodes[opcode].execute(self)
312
+ return
313
+ end
314
+
315
+ case opcode
316
+ # Format A: opcode only (1 byte)
317
+ when 0x00 then opHalt
318
+ when 0x01 then opNop
319
+ when 0x02 then opRet
320
+ when 0x08 then opYield
321
+ when 0x09 then opPanic
322
+ when 0x0A then opUnreachable
323
+
324
+ # Format B: opcode + Rd + Rs (3 bytes)
325
+ when 0x10 then rd = fetch8; rs = fetch8; opPush(rd, rs)
326
+ when 0x11 then rd = fetch8; rs = fetch8; opPop(rd, rs)
327
+ when 0x12 then rd = fetch8; rs = fetch8; opDup(rd, rs)
328
+ when 0x13 then ra = fetch8; rb = fetch8; opSwap(ra, rb)
329
+ when 0x20 then rd = fetch8; rs = fetch8; opIMov(rd, rs)
330
+ when 0x40 then rd = fetch8; rs = fetch8; opFMov(rd, rs)
331
+
332
+ # Format C: opcode + Rd + Ra + Rb (4 bytes)
333
+ when 0x21 then rd = fetch8; ra = fetch8; rb = fetch8; opIAdd(rd, ra, rb)
334
+ when 0x22 then rd = fetch8; ra = fetch8; rb = fetch8; opISub(rd, ra, rb)
335
+ when 0x23 then rd = fetch8; ra = fetch8; rb = fetch8; opIMul(rd, ra, rb)
336
+ when 0x24 then rd = fetch8; ra = fetch8; rb = fetch8; opIDiv(rd, ra, rb)
337
+ when 0x25 then rd = fetch8; ra = fetch8; rb = fetch8; opIMod(rd, ra, rb)
338
+ when 0x26 then rd = fetch8; ra = fetch8; rb = fetch8; opINeg(rd, ra, rb)
339
+ when 0x27 then rd = fetch8; ra = fetch8; rb = fetch8; opIAbs(rd, ra, rb)
340
+ when 0x28 then rd = fetch8; opIInc(rd)
341
+ when 0x29 then rd = fetch8; opIDec(rd)
342
+ when 0x2A then rd = fetch8; ra = fetch8; rb = fetch8; opIMin(rd, ra, rb)
343
+ when 0x2B then rd = fetch8; ra = fetch8; rb = fetch8; opIMax(rd, ra, rb)
344
+ when 0x2C then rd = fetch8; ra = fetch8; rb = fetch8; opIAnd(rd, ra, rb)
345
+ when 0x2D then rd = fetch8; ra = fetch8; rb = fetch8; opIOr(rd, ra, rb)
346
+ when 0x2E then rd = fetch8; ra = fetch8; rb = fetch8; opIXor(rd, ra, rb)
347
+ when 0x2F then rd = fetch8; ra = fetch8; rb = fetch8; opIShl(rd, ra, rb)
348
+ when 0x30 then rd = fetch8; ra = fetch8; rb = fetch8; opIShr(rd, ra, rb)
349
+ when 0x31 then rd = fetch8; ra = fetch8; rb = fetch8; opINot(rd, ra, rb)
350
+ when 0x32 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpEq(rd, ra, rb)
351
+ when 0x33 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpNe(rd, ra, rb)
352
+ when 0x34 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpLt(rd, ra, rb)
353
+ when 0x35 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpLe(rd, ra, rb)
354
+ when 0x36 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpGt(rd, ra, rb)
355
+ when 0x37 then rd = fetch8; ra = fetch8; rb = fetch8; opICmpGe(rd, ra, rb)
356
+
357
+ # Float Arithmetic
358
+ when 0x41 then rd = fetch8; ra = fetch8; rb = fetch8; opFAdd(rd, ra, rb)
359
+ when 0x42 then rd = fetch8; ra = fetch8; rb = fetch8; opFSub(rd, ra, rb)
360
+ when 0x43 then rd = fetch8; ra = fetch8; rb = fetch8; opFMul(rd, ra, rb)
361
+ when 0x44 then rd = fetch8; ra = fetch8; rb = fetch8; opFDiv(rd, ra, rb)
362
+ when 0x45 then rd = fetch8; ra = fetch8; rb = fetch8; opFMod(rd, ra, rb)
363
+ when 0x46 then rd = fetch8; ra = fetch8; rb = fetch8; opFNeg(rd, ra, rb)
364
+ when 0x47 then rd = fetch8; ra = fetch8; rb = fetch8; opFAbs(rd, ra, rb)
365
+ when 0x48 then rd = fetch8; ra = fetch8; rb = fetch8; opFSqrt(rd, ra, rb)
366
+ when 0x49 then rd = fetch8; ra = fetch8; rb = fetch8; opFFloor(rd, ra, rb)
367
+ when 0x4A then rd = fetch8; ra = fetch8; rb = fetch8; opFCeil(rd, ra, rb)
368
+ when 0x4B then rd = fetch8; ra = fetch8; rb = fetch8; opFRound(rd, ra, rb)
369
+ when 0x4C then rd = fetch8; ra = fetch8; rb = fetch8; opFMin(rd, ra, rb)
370
+ when 0x4D then rd = fetch8; ra = fetch8; rb = fetch8; opFMax(rd, ra, rb)
371
+ when 0x4E then rd = fetch8; ra = fetch8; rb = fetch8; opFSin(rd, ra, rb)
372
+ when 0x4F then rd = fetch8; ra = fetch8; rb = fetch8; opFCos(rd, ra, rb)
373
+ when 0x50 then rd = fetch8; ra = fetch8; rb = fetch8; opFExp(rd, ra, rb)
374
+ when 0x51 then rd = fetch8; ra = fetch8; rb = fetch8; opFLog(rd, ra, rb)
375
+ when 0x52 then rd = fetch8; ra = fetch8; rb = fetch8; opFClamp(rd, ra, rb)
376
+ when 0x53 then rd = fetch8; ra = fetch8; rb = fetch8; opFLerp(rd, ra, rb)
377
+ when 0x54 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpEq(rd, ra, rb)
378
+ when 0x55 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpNe(rd, ra, rb)
379
+ when 0x56 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpLt(rd, ra, rb)
380
+ when 0x57 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpLe(rd, ra, rb)
381
+ when 0x58 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpGt(rd, ra, rb)
382
+ when 0x59 then rd = fetch8; ra = fetch8; rb = fetch8; opFCmpGe(rd, ra, rb)
383
+
384
+ # Conversions
385
+ when 0x60 then rd = fetch8; ra = fetch8; rb = fetch8; opIToF(rd, ra, rb)
386
+ when 0x61 then rd = fetch8; ra = fetch8; rb = fetch8; opFToI(rd, ra, rb)
387
+ when 0x62 then rd = fetch8; ra = fetch8; rb = fetch8; opBToI(rd, ra, rb)
388
+ when 0x63 then rd = fetch8; ra = fetch8; rb = fetch8; opIToB(rd, ra, rb)
389
+
390
+ # Type/Meta
391
+ when 0x90 then rd = fetch8; ra = fetch8; rb = fetch8; opCast(rd, ra, rb)
392
+ when 0x91 then rd = fetch8; ra = fetch8; rb = fetch8; opSizeOf(rd, ra, rb)
393
+ when 0x92 then rd = fetch8; ra = fetch8; rb = fetch8; opTypeOf(rd, ra, rb)
394
+
395
+ # Bitwise
396
+ when 0xA0 then rd = fetch8; ra = fetch8; rb = fetch8; opBAnd(rd, ra, rb)
397
+ when 0xA1 then rd = fetch8; ra = fetch8; rb = fetch8; opBOr(rd, ra, rb)
398
+ when 0xA2 then rd = fetch8; ra = fetch8; rb = fetch8; opBXor(rd, ra, rb)
399
+ when 0xA3 then rd = fetch8; ra = fetch8; rb = fetch8; opBShl(rd, ra, rb)
400
+ when 0xA4 then rd = fetch8; ra = fetch8; rb = fetch8; opBShr(rd, ra, rb)
401
+ when 0xA5 then rd = fetch8; ra = fetch8; rb = fetch8; opBNot(rd, ra, rb)
402
+
403
+ # Vector
404
+ when 0xB0 then rd = fetch8; rb = fetch8; off = fetchu16; opVLoad(rd, rb, off)
405
+ when 0xB1 then rs = fetch8; rb = fetch8; off = fetchu16; opVStore(rs, rb, off)
406
+ when 0xB2 then rd = fetch8; ra = fetch8; rb = fetch8; opVAdd(rd, ra, rb)
407
+ when 0xB3 then rd = fetch8; ra = fetch8; rb = fetch8; opVMul(rd, ra, rb)
408
+ when 0xB4 then rd = fetch8; ra = fetch8; rb = fetch8; opVDot(rd, ra, rb)
409
+
410
+ # Memory (Format E)
411
+ when 0x70 then rd = fetch8; rb = fetch8; off = fetchu16; opLoad8(rd, rb)
412
+ when 0x71 then rd = fetch8; rb = fetch8; off = fetchu16; opLoad16(rd, rb)
413
+ when 0x72 then rd = fetch8; rb = fetch8; off = fetchu16; opLoad32(rd, rb)
414
+ when 0x73 then rd = fetch8; rb = fetch8; off = fetchu16; opLoad64(rd, rb)
415
+ when 0x74 then rs = fetch8; rb = fetch8; off = fetchu16; opStore8(rs, rb)
416
+ when 0x75 then rs = fetch8; rb = fetch8; off = fetchu16; opStore16(rs, rb)
417
+ when 0x76 then rs = fetch8; rb = fetch8; off = fetchu16; opStore32(rs, rb)
418
+ when 0x77 then rs = fetch8; rb = fetch8; off = fetchu16; opStore64(rs, rb)
419
+ when 0x78 then rd = fetch8; rb = fetch8; off = fetchu16; opLoadAddr(rd, rb)
420
+ when 0x79 then rd = fetch8; opStackAlloc(rd)
421
+
422
+ else
423
+ raise UnknownOpcodeError.new(opcode, @pc - 1)
424
+ end
425
+ end
426
+
427
+ public
428
+
429
+ # Format A opcodes (1 byte)
430
+ def opHalt
431
+ @state = HALTED
432
+ end
433
+
434
+ def opNop
435
+ # No operation
436
+ end
437
+
438
+ def opRet
439
+ @pc = @gp[LR]
440
+ end
441
+
442
+ def opYield
443
+ @state = YIELDED
444
+ end
445
+
446
+ def opPanic
447
+ @state = PANICKED
448
+ raise PanicError.new(pc: @pc - 1)
449
+ end
450
+
451
+ def opUnreachable
452
+ raise FluxRuntimeError.new('Unreachable instruction executed', opcode: :Unreachable, pc: @pc - 1)
453
+ end
454
+
455
+ # Format G opcodes (variable)
456
+ def opJump
457
+ length = fetch8
458
+ offset = fetch16
459
+ @pc = @pc &+ offset
460
+ end
461
+
462
+ def opJumpIf
463
+ length = fetch8
464
+ rd = fetch8
465
+ offset = fetch16
466
+ @pc = @pc &+ offset if @gp[rd] != 0
467
+ end
468
+
469
+ def opJumpIfNot
470
+ length = fetch8
471
+ rd = fetch8
472
+ offset = fetch16
473
+ @pc = @pc &+ offset if @gp[rd] == 0
474
+ end
475
+
476
+ def opCall
477
+ length = fetch8
478
+ func_idx = fetchu16
479
+ push(@pc)
480
+ @gp[LR] = @pc
481
+ @pc = func_idx
482
+ end
483
+
484
+ def opCallIndirect
485
+ length = fetch8
486
+ reg = fetch8
487
+ push(@pc)
488
+ @gp[LR] = @pc
489
+ @pc = @gp[reg]
490
+ end
491
+
492
+ # Format B opcodes (3 bytes: opcode + Rd + Rs)
493
+ def opPush(rd, rs)
494
+ push(@gp[rs])
495
+ end
496
+
497
+ def opPop(rd, rs)
498
+ @gp[rd] = pop
499
+ end
500
+
501
+ def opDup(rd, rs)
502
+ @gp[rd] = @gp[rs]
503
+ end
504
+
505
+ def opSwap(ra, rb)
506
+ @gp[ra], @gp[rb] = @gp[rb], @gp[ra]
507
+ end
508
+
509
+ def opIMov(rd, rs)
510
+ @gp[rd] = @gp[rs]
511
+ end
512
+
513
+ def opFMov(rd, rs)
514
+ @fp[rd] = @fp[rs]
515
+ end
516
+
517
+ # Format D opcodes (4 bytes: opcode + Rd + imm16)
518
+ def opIInc(rd)
519
+ imm = fetch16
520
+ result = @gp[rd] + imm
521
+ set_znv(result)
522
+ @gp[rd] = result
523
+ end
524
+
525
+ def opIDec(rd)
526
+ imm = fetch16
527
+ result = @gp[rd] - imm
528
+ set_znv(result)
529
+ @gp[rd] = result
530
+ end
531
+
532
+ def opStackAlloc(rd)
533
+ size = fetch16
534
+ @sp -= size
535
+ raise FluxRuntimeError, 'Stack overflow' if @sp < 0
536
+ @gp[rd] = @sp
537
+ end
538
+
539
+ # Format E opcodes (5 bytes: opcode + Rd + Rbase + off16)
540
+ def opLoad8(rd, rb)
541
+ off = fetchu16
542
+ addr = @gp[rb] + off
543
+ @gp[rd] = read_memory_u8(addr) || 0
544
+ end
545
+
546
+ def opLoad16(rd, rb)
547
+ off = fetchu16
548
+ addr = @gp[rb] + off
549
+ @gp[rd] = read_memory_u16(addr) || 0
550
+ end
551
+
552
+ def opLoad32(rd, rb)
553
+ off = fetchu16
554
+ addr = @gp[rb] + off
555
+ @gp[rd] = read_memory_i32(addr) || 0
556
+ end
557
+
558
+ def opLoad64(rd, rb)
559
+ off = fetchu16
560
+ addr = @gp[rb] + off
561
+ @gp[rd] = read_memory_u32(addr) || 0
562
+ end
563
+
564
+ def opStore8(rs, rb)
565
+ off = fetchu16
566
+ addr = @gp[rb] + off
567
+ write_memory(addr, 1, @gp[rs])
568
+ end
569
+
570
+ def opStore16(rs, rb)
571
+ off = fetchu16
572
+ addr = @gp[rb] + off
573
+ write_memory(addr, 2, @gp[rs])
574
+ end
575
+
576
+ def opStore32(rs, rb)
577
+ off = fetchu16
578
+ addr = @gp[rb] + off
579
+ write_memory(addr, 4, @gp[rs])
580
+ end
581
+
582
+ def opStore64(rs, rb)
583
+ off = fetchu16
584
+ addr = @gp[rb] + off
585
+ write_memory(addr, 8, @gp[rs])
586
+ end
587
+
588
+ def opLoadAddr(rd, rb)
589
+ off = fetchu16
590
+ @gp[rd] = @gp[rb] + off
591
+ end
592
+
593
+ # Integer Arithmetic (Format C)
594
+ def opIAdd(rd, ra, rb)
595
+ result = @gp[ra] + @gp[rb]
596
+ set_znv(result)
597
+ @gp[rd] = result
598
+ end
599
+
600
+ def opISub(rd, ra, rb)
601
+ result = @gp[ra] - @gp[rb]
602
+ set_znv(result)
603
+ @gp[rd] = result
604
+ end
605
+
606
+ def opIMul(rd, ra, rb)
607
+ result = @gp[ra] * @gp[rb]
608
+ set_znv(result)
609
+ @gp[rd] = result
610
+ end
611
+
612
+ def opIDiv(rd, ra, rb)
613
+ raise DivideByZeroError.new(@pc - 1) if @gp[rb] == 0
614
+ result = @gp[ra] / @gp[rb]
615
+ set_znv(result)
616
+ @gp[rd] = result
617
+ end
618
+
619
+ def opIMod(rd, ra, rb)
620
+ raise DivideByZeroError.new(@pc - 1) if @gp[rb] == 0
621
+ result = @gp[ra] % @gp[rb]
622
+ set_znv(result)
623
+ @gp[rd] = result
624
+ end
625
+
626
+ def opINeg(rd, ra, rb)
627
+ result = -@gp[ra]
628
+ set_zn(result)
629
+ @gp[rd] = result
630
+ end
631
+
632
+ def opIAbs(rd, ra, rb)
633
+ result = @gp[ra].abs
634
+ set_zn(result)
635
+ @gp[rd] = result
636
+ end
637
+
638
+ def opIMin(rd, ra, rb)
639
+ result = [@gp[ra], @gp[rb]].min
640
+ set_zn(result)
641
+ @gp[rd] = result
642
+ end
643
+
644
+ def opIMax(rd, ra, rb)
645
+ result = [@gp[ra], @gp[rb]].max
646
+ set_zn(result)
647
+ @gp[rd] = result
648
+ end
649
+
650
+ def opIAnd(rd, ra, rb)
651
+ result = @gp[ra] & @gp[rb]
652
+ set_zn(result)
653
+ @gp[rd] = result
654
+ end
655
+
656
+ def opIOr(rd, ra, rb)
657
+ result = @gp[ra] | @gp[rb]
658
+ set_zn(result)
659
+ @gp[rd] = result
660
+ end
661
+
662
+ def opIXor(rd, ra, rb)
663
+ result = @gp[ra] ^ @gp[rb]
664
+ set_zn(result)
665
+ @gp[rd] = result
666
+ end
667
+
668
+ def opIShl(rd, ra, rb)
669
+ result = @gp[ra] << (@gp[rb] & 31)
670
+ set_z(result)
671
+ set_c((@gp[ra] << (@gp[rb] & 31)) != result ? 1 : 0)
672
+ @gp[rd] = result
673
+ end
674
+
675
+ def opIShr(rd, ra, rb)
676
+ result = @gp[ra] >> (@gp[rb] & 31)
677
+ set_z(result)
678
+ @gp[rd] = result
679
+ end
680
+
681
+ def opINot(rd, ra, rb)
682
+ result = ~@gp[ra]
683
+ set_zn(result)
684
+ @gp[rd] = result
685
+ end
686
+
687
+ # Integer Comparisons
688
+ def opICmpEq(rd, ra, rb)
689
+ @gp[rd] = (@gp[ra] == @gp[rb]) ? 1 : 0
690
+ set_z(@gp[rd])
691
+ end
692
+
693
+ def opICmpNe(rd, ra, rb)
694
+ @gp[rd] = (@gp[ra] != @gp[rb]) ? 1 : 0
695
+ set_z(1 - @gp[rd])
696
+ end
697
+
698
+ def opICmpLt(rd, ra, rb)
699
+ @gp[rd] = (@gp[ra] < @gp[rb]) ? 1 : 0
700
+ set_z(@gp[rd])
701
+ end
702
+
703
+ def opICmpLe(rd, ra, rb)
704
+ @gp[rd] = (@gp[ra] <= @gp[rb]) ? 1 : 0
705
+ set_z(@gp[rd])
706
+ end
707
+
708
+ def opICmpGt(rd, ra, rb)
709
+ @gp[rd] = (@gp[ra] > @gp[rb]) ? 1 : 0
710
+ set_z(@gp[rd])
711
+ end
712
+
713
+ def opICmpGe(rd, ra, rb)
714
+ @gp[rd] = (@gp[ra] >= @gp[rb]) ? 1 : 0
715
+ set_z(@gp[rd])
716
+ end
717
+
718
+ # Float Arithmetic
719
+ def opFAdd(rd, ra, rb)
720
+ result = @fp[ra] + @fp[rb]
721
+ @fp[rd] = result
722
+ end
723
+
724
+ def opFSub(rd, ra, rb)
725
+ result = @fp[ra] - @fp[rb]
726
+ @fp[rd] = result
727
+ end
728
+
729
+ def opFMul(rd, ra, rb)
730
+ result = @fp[ra] * @fp[rb]
731
+ @fp[rd] = result
732
+ end
733
+
734
+ def opFDiv(rd, ra, rb)
735
+ result = @fp[ra] / @fp[rb]
736
+ @fp[rd] = result
737
+ end
738
+
739
+ def opFMod(rd, ra, rb)
740
+ result = @fp[ra] % @fp[rb]
741
+ @fp[rd] = result
742
+ end
743
+
744
+ def opFNeg(rd, ra, rb)
745
+ @fp[rd] = -@fp[ra]
746
+ end
747
+
748
+ def opFAbs(rd, ra, rb)
749
+ @fp[rd] = @fp[ra].abs
750
+ end
751
+
752
+ def opFSqrt(rd, ra, rb)
753
+ @fp[rd] = Math.sqrt(@fp[ra])
754
+ end
755
+
756
+ def opFFloor(rd, ra, rb)
757
+ @fp[rd] = @fp[ra].floor
758
+ end
759
+
760
+ def opFCeil(rd, ra, rb)
761
+ @fp[rd] = @fp[ra].ceil
762
+ end
763
+
764
+ def opFRound(rd, ra, rb)
765
+ @fp[rd] = @fp[ra].round
766
+ end
767
+
768
+ def opFMin(rd, ra, rb)
769
+ @fp[rd] = [@fp[ra], @fp[rb]].min
770
+ end
771
+
772
+ def opFMax(rd, ra, rb)
773
+ @fp[rd] = [@fp[ra], @fp[rb]].max
774
+ end
775
+
776
+ def opFSin(rd, ra, rb)
777
+ @fp[rd] = Math.sin(@fp[ra])
778
+ end
779
+
780
+ def opFCos(rd, ra, rb)
781
+ @fp[rd] = Math.cos(@fp[ra])
782
+ end
783
+
784
+ def opFExp(rd, ra, rb)
785
+ @fp[rd] = Math.exp(@fp[ra])
786
+ end
787
+
788
+ def opFLog(rd, ra, rb)
789
+ @fp[rd] = Math.log(@fp[ra])
790
+ end
791
+
792
+ def opFClamp(rd, ra, rb)
793
+ # FP[Rd] = clamp(FP[Ra], FP[Rb], Rc) - simplified, just min of ra,rb
794
+ @fp[rd] = [@fp[ra], @fp[rb]].min
795
+ end
796
+
797
+ def opFLerp(rd, ra, rb)
798
+ # FP[Rd] = FP[Ra] * t + FP[Rb] * (1-t), with t in Rc
799
+ # Simplified: use FP[Ra] as t, FP[Rb] as base
800
+ t = @fp[ra]
801
+ base = @fp[rb]
802
+ @fp[rd] = base * (1 - t) + base * t
803
+ end
804
+
805
+ # Float Comparisons
806
+ def opFCmpEq(rd, ra, rb)
807
+ @gp[rd] = (@fp[ra] == @fp[rb]) ? 1 : 0
808
+ set_z(@gp[rd])
809
+ end
810
+
811
+ def opFCmpNe(rd, ra, rb)
812
+ @gp[rd] = (@fp[ra] != @fp[rb]) ? 1 : 0
813
+ set_z(1 - @gp[rd])
814
+ end
815
+
816
+ def opFCmpLt(rd, ra, rb)
817
+ @gp[rd] = (@fp[ra] < @fp[rb]) ? 1 : 0
818
+ set_z(@gp[rd])
819
+ end
820
+
821
+ def opFCmpLe(rd, ra, rb)
822
+ @gp[rd] = (@fp[ra] <= @fp[rb]) ? 1 : 0
823
+ set_z(@gp[rd])
824
+ end
825
+
826
+ def opFCmpGt(rd, ra, rb)
827
+ @gp[rd] = (@fp[ra] > @fp[rb]) ? 1 : 0
828
+ set_z(@gp[rd])
829
+ end
830
+
831
+ def opFCmpGe(rd, ra, rb)
832
+ @gp[rd] = (@fp[ra] >= @fp[rb]) ? 1 : 0
833
+ set_z(@gp[rd])
834
+ end
835
+
836
+ # Conversions
837
+ def opIToF(rd, ra, rb)
838
+ @fp[rd] = @gp[ra].to_f
839
+ end
840
+
841
+ def opFToI(rd, ra, rb)
842
+ @gp[rd] = @fp[ra].to_i
843
+ end
844
+
845
+ def opBToI(rd, ra, rb)
846
+ @gp[rd] = (@gp[ra] != 0) ? 1 : 0
847
+ end
848
+
849
+ def opIToB(rd, ra, rb)
850
+ @gp[rd] = (@gp[ra] != 0) ? 1 : 0
851
+ end
852
+
853
+ # Type/Meta
854
+ def opCast(rd, ra, rb)
855
+ # Type-cast: simplified implementation
856
+ @gp[rd] = @gp[ra]
857
+ end
858
+
859
+ def opSizeOf(rd, ra, rb)
860
+ # Size of type in GP[Ra], result in GP[Rd]
861
+ @gp[rd] = 4 # Default size
862
+ end
863
+
864
+ def opTypeOf(rd, ra, rb)
865
+ # Runtime type tag
866
+ @gp[rd] = 0 # Integer type
867
+ end
868
+
869
+ # Bitwise
870
+ def opBAnd(rd, ra, rb)
871
+ result = @gp[ra] & @gp[rb]
872
+ set_zn(result)
873
+ @gp[rd] = result
874
+ end
875
+
876
+ def opBOr(rd, ra, rb)
877
+ result = @gp[ra] | @gp[rb]
878
+ set_zn(result)
879
+ @gp[rd] = result
880
+ end
881
+
882
+ def opBXor(rd, ra, rb)
883
+ result = @gp[ra] ^ @gp[rb]
884
+ set_zn(result)
885
+ @gp[rd] = result
886
+ end
887
+
888
+ def opBShl(rd, ra, rb)
889
+ result = @gp[ra] << (@gp[rb] & 31)
890
+ set_zn(result)
891
+ @gp[rd] = result
892
+ end
893
+
894
+ def opBShr(rd, ra, rb)
895
+ result = @gp[ra] >> (@gp[rb] & 31)
896
+ set_zn(result)
897
+ @gp[rd] = result
898
+ end
899
+
900
+ def opBNot(rd, ra, rb)
901
+ result = ~@gp[ra]
902
+ set_zn(result)
903
+ @gp[rd] = result
904
+ end
905
+
906
+ # Vector Operations
907
+ def opVLoad(rd, rb, off)
908
+ addr = @gp[rb] + off
909
+ (0...256).each do |i|
910
+ @vr[rd][i] = read_memory_u8(addr + i) || 0
911
+ end
912
+ end
913
+
914
+ def opVStore(rs, rb, off)
915
+ addr = @gp[rb] + off
916
+ @vr[rs].bytes.each_with_index do |byte, i|
917
+ write_memory(addr + i, 1, byte)
918
+ end
919
+ end
920
+
921
+ def opVAdd(rd, ra, rb)
922
+ # Component-wise addition
923
+ (0...256).each do |i|
924
+ a = @vr[ra][i].ord
925
+ b = @vr[rb][i].ord
926
+ result = (a + b) & 0xFF
927
+ @vr[rd][i] = result.chr
928
+ end
929
+ end
930
+
931
+ def opVMul(rd, ra, rb)
932
+ # Component-wise multiplication
933
+ (0...256).each do |i|
934
+ a = @vr[ra][i].ord
935
+ b = @vr[rb][i].ord
936
+ result = (a * b) & 0xFF
937
+ @vr[rd][i] = result.chr
938
+ end
939
+ end
940
+
941
+ def opVDot(rd, ra, rb)
942
+ # Dot product -> scalar in F0
943
+ sum = 0.0
944
+ (0...256).each do |i|
945
+ a = @vr[ra][i].ord
946
+ b = @vr[rb][i].ord
947
+ sum += a * b
948
+ end
949
+ @fp[rd] = sum
950
+ end
951
+
952
+ # A2A Opcodes (stub implementations)
953
+ def opASend
954
+ length = fetch8
955
+ agent_id = fetch8
956
+ reg = fetch8
957
+ $stderr.puts "A2A: ASend agent_id=#{agent_id} reg=#{reg}"
958
+ end
959
+
960
+ def opARecv
961
+ length = fetch8
962
+ agent_id = fetch8
963
+ reg = fetch8
964
+ $stderr.puts "A2A: ARecv agent_id=#{agent_id} reg=#{reg}"
965
+ end
966
+
967
+ def opAAsk
968
+ length = fetch8
969
+ agent_id = fetch8
970
+ reg = fetch8
971
+ $stderr.puts "A2A: AAsk agent_id=#{agent_id} reg=#{reg}"
972
+ end
973
+
974
+ def opATell
975
+ length = fetch8
976
+ agent_id = fetch8
977
+ reg = fetch8
978
+ $stderr.puts "A2A: ATell agent_id=#{agent_id} reg=#{reg}"
979
+ end
980
+
981
+ def opADelegate
982
+ length = fetch8
983
+ agent_id = fetch8
984
+ bc_start = fetchu16
985
+ $stderr.puts "A2A: ADelegate agent_id=#{agent_id} bc_start=#{bc_start}"
986
+ end
987
+
988
+ def opABroadcast
989
+ length = fetch8
990
+ reg = fetch8
991
+ $stderr.puts "A2A: ABroadcast reg=#{reg}"
992
+ end
993
+
994
+ def opASubscribe
995
+ length = fetch8
996
+ channel_id = fetch8
997
+ $stderr.puts "A2A: ASubscribe channel_id=#{channel_id}"
998
+ end
999
+
1000
+ def opAWait
1001
+ length = fetch8
1002
+ cond_reg = fetch8
1003
+ $stderr.puts "A2A: AWait cond_reg=#{cond_reg}"
1004
+ end
1005
+
1006
+ def opATrust
1007
+ length = fetch8
1008
+ agent_id = fetch8
1009
+ level = fetch8
1010
+ $stderr.puts "A2A: ATrust agent_id=#{agent_id} level=#{level}"
1011
+ end
1012
+
1013
+ def opAVerify
1014
+ length = fetch8
1015
+ agent_id = fetch8
1016
+ result_reg = fetch8
1017
+ $stderr.puts "A2A: AVerify agent_id=#{agent_id} result_reg=#{result_reg}"
1018
+ end
1019
+ end
1020
+ end