code wiki / _hdl_build / nx_rv64_asm.nx
nx_rv64_asm.nx source
↩ module page · 268 lines · 24208 B
1// nx_rv64_asm.nx -- SOVEREIGN RV64 BARE-METAL TEXT-ASSEMBLER. Two-pass (collect labels -> encode with resolved
2// branch/jump offsets) assembler emitting a FLAT binary whose entry is at offset 0 (loads at 0x80000000 via QEMU
3// `-bios`, no syscall trampoline; a guest halts via the SiFive test finisher). Reuses the RV64 format encoders proven
4// in nx_rv64_runproof_gate + adds U-type (lui) + B-type (branches). This is the tool for writing REAL device drivers
5// that boot in qemu-system-riscv64 -- readable .s text, correct bytes. license_tier: ORIGINAL
6// API: rvasm_assemble(src: *u8, n: i64, out: *u8, cap: i64) -> nbytes (or negative on error)
7// Supported: lui li addi add sub and or xor sll srl slli srli andi ori lw lbu lhu ld sw sb sh sd
8// beq bne blt bge bltu bgeu jal j mv ret nop ecall (branch/jal targets are LABELS)
9import "nx_syscalls.nx"
10const RA_MAGIC_2047: i64 = 2047
11const RA_MAGIC_2048: i64 = 2048
12// RV64A + FENCE field constants (LN34) -- declared above their first reader, as the parser requires
13const RA_AMO_ADD: i64 = 0
14const RA_AMO_SWAP: i64 = 1
15const RA_AMO_LR: i64 = 2
16const RA_AMO_SC: i64 = 3
17const RA_F3_W: i64 = 2
18const RA_F3_D: i64 = 3
19const RA_AQRL: i64 = 3 // aq<<1 | rl in funct7's low two bits
20const RA_OP_AMO: i64 = 0x2F
21const RA_OP_FENCE: i64 = 0x0F
22const RA_FENCE_I: i64 = 8
23const RA_FENCE_O: i64 = 4
24const RA_FENCE_R: i64 = 2
25const RA_FENCE_W: i64 = 1
26const RA_FENCE_IORW: i64 = 15
27
28func ra_isws(c: i64) -> i64 { if c==32 { return 1 } if c==9 { return 1 } if c==13 { return 1 } if c==10 { return 1 } return 0 }
29func ra_isdig(c: i64) -> i64 { if c>=48 { if c<=57 { return 1 } } return 0 }
30func ra_ishex(c: i64) -> i64 { if ra_isdig(c)==1 { return 1 } if c>=97 { if c<=102 { return 1 } } if c>=65 { if c<=70 { return 1 } } return 0 }
31func ra_hexval(c: i64) -> i64 { if c>=48 { if c<=57 { return c-48 } } if c>=97 { if c<=102 { return c-87 } } if c>=65 { if c<=70 { return c-55 } } return 0 }
32
33// ---- RV64 format encoders (R/I/S from the proven runproof gate; U/B added) ----
34func ra_r(op: i64, f3: i64, f7: i64, rd: i64, rs1: i64, rs2: i64) -> i64 { return (f7<<25)|((rs2&0x1F)<<20)|((rs1&0x1F)<<15)|(f3<<12)|((rd&0x1F)<<7)|op }
35func ra_i(op: i64, f3: i64, rd: i64, rs1: i64, imm: i64) -> i64 { return ((imm&0xFFF)<<20)|((rs1&0x1F)<<15)|(f3<<12)|((rd&0x1F)<<7)|op }
36func ra_s(op: i64, f3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm>>5)&0x7F)<<25)|((rs2&0x1F)<<20)|((rs1&0x1F)<<15)|(f3<<12)|((imm&0x1F)<<7)|op }
37func ra_u(op: i64, rd: i64, imm: i64) -> i64 { return ((imm&0xFFFFF)<<12)|((rd&0x1F)<<7)|op }
38func ra_b(f3: i64, rs1: i64, rs2: i64, imm: i64) -> i64 { return (((imm>>12)&1)<<31)|(((imm>>5)&0x3F)<<25)|((rs2&0x1F)<<20)|((rs1&0x1F)<<15)|((f3&7)<<12)|(((imm>>1)&0xF)<<8)|(((imm>>11)&1)<<7)|0x63 }
39func ra_j(rd: i64, imm: i64) -> i64 { return 0x6F|((rd&0x1F)<<7)|(((imm>>12)&0xFF)<<12)|(((imm>>11)&0x1)<<20)|(((imm>>1)&0x3FF)<<21)|(((imm>>20)&0x1)<<31) }
40func ra_put(out: *u8, o: i64, w: i64) -> i64 { out[o]=(w&0xff) as u8; out[o+1]=((w>>8)&0xff) as u8; out[o+2]=((w>>16)&0xff) as u8; out[o+3]=((w>>24)&0xff) as u8; return o+4 }
41// FAIL-LOUD guard: a 12-bit signed immediate (li/addi/andi/ori) must fit [-2048,2047] or the assembler REFUSES
42// (returns 1=ok / 0=out-of-range). This is the durable fix for the "li reg, 4096 silently truncates to 0" bug --
43// a too-big immediate now fails the assemble instead of emitting wrong code (use lui for values >= 4096).
44func ra_imm12(im: i64) -> i64 { if im > RA_MAGIC_2047 { return 0 } if im < (0 - RA_MAGIC_2048) { return 0 } return 1 }
45// shift amounts (slli/srli) must fit [0,63] on RV64.
46func ra_sh6(im: i64) -> i64 { if im < 0 { return 0 } if im > 63 { return 0 } return 1 }
47
48// ---- cursor parsing ----
49func ra_skipsep(s: *u8, cur: *i64) -> i64 { var p: i64=cur[0]; var go: i64=1; while go==1 { let c: i64=s[p] as i64; if c==44 { p=p+1 } else { if ra_isws(c)==1 { p=p+1 } else { go=0 } } } cur[0]=p; return p }
50func ra_num(s: *u8, cur: *i64) -> i64 { var p: i64=cur[0]; var v: i64=0; while ra_isdig(s[p] as i64)==1 { v=v*10+((s[p] as i64)-48); p=p+1 } cur[0]=p; return v }
51func ra_imm(s: *u8, cur: *i64) -> i64 {
52 ra_skipsep(s,cur); var p: i64=cur[0]; var neg: i64=0
53 if (s[p] as i64)==45 { neg=1; p=p+1 }
54 var v: i64=0
55 if (s[p] as i64)==48 { if (s[p+1] as i64)==120 { p=p+2; while ra_ishex(s[p] as i64)==1 { v=v*16+ra_hexval(s[p] as i64); p=p+1 } cur[0]=p; if neg==1 { return 0-v } return v } }
56 while ra_isdig(s[p] as i64)==1 { v=v*10+((s[p] as i64)-48); p=p+1 }
57 cur[0]=p; if neg==1 { return 0-v } return v
58}
59func ra_past(s: *u8, cur: *i64, ch: i64) -> i64 { var p: i64=cur[0]; var go: i64=1; while go==1 { let c: i64=s[p] as i64; if c==0 { go=0 } else { if c==ch { p=p+1; go=0 } else { p=p+1 } } } cur[0]=p; return p }
60// ABI register name -> number
61func ra_reg(s: *u8, cur: *i64) -> i64 {
62 ra_skipsep(s,cur); var p: i64=cur[0]
63 let c0: i64=s[p] as i64; let c1: i64=s[p+1] as i64
64 if c0==115 { if c1==112 { cur[0]=p+2; return 2 } } // sp
65 if c0==114 { if c1==97 { cur[0]=p+2; return 1 } } // ra
66 if c0==122 { cur[0]=p+4; return 0 } // zero
67 if c0==102 { if c1==112 { cur[0]=p+2; return 8 } } // fp
68 cur[0]=p+1
69 let n: i64=ra_num(s,cur)
70 if c0==97 { return 10+n } // aN
71 if c0==116 { if n<=2 { return 5+n } return 25+n } // tN
72 if c0==115 { if n==0 { return 8 } if n==1 { return 9 } return 16+n } // sN
73 return 0
74}
75func ra_word_is(s: *u8, p: i64, lit: *u8) -> i64 { var i: i64=0; while (lit[i] as i64)!=0 { if (s[p+i] as i64)!=(lit[i] as i64) { return 0 } i=i+1 } let nc: i64=s[p+i] as i64; if ra_isws(nc)==1 { return 1 } if nc==0 { return 1 } return 0 }
76func ra_afterword(s: *u8, p: i64) -> i64 { var q: i64=p; var go: i64=1; while go==1 { let c: i64=s[q] as i64; if c==0 { go=0 } else { if ra_isws(c)==1 { go=0 } else { q=q+1 } } } return q }
77
78// label table (module-shared via params)
79func ra_find_label(s: *u8, ts: i64, tlen: i64, lst: *i64, lln: *i64, lab: *i64, nl: i64) -> i64 {
80 var j: i64=0
81 while j<nl { if lln[j]==tlen { var k: i64=0; var eq: i64=1; while k<tlen { if (s[ts+k] as i64)!=(s[(lst[j])+k] as i64) { eq=0; k=tlen } else { k=k+1 } } if eq==1 { return lab[j] } } j=j+1 }
82 return 0-1
83}
84// parse a label operand (name) at cur -> its address (or -1)
85func ra_label_target(s: *u8, cur: *i64, lst: *i64, lln: *i64, lab: *i64, nl: i64) -> i64 {
86 ra_skipsep(s,cur); let ts: i64=cur[0]; let te: i64=ra_afterword(s,ts); cur[0]=te
87 return ra_find_label(s, ts, te-ts, lst, lln, lab, nl)
88}
89
90// encode ONE instruction at line-offset fp, whose address is `abs`. returns the 32-bit word or -1.
91func ra_encode(s: *u8, fp: i64, abs: i64, lst: *i64, lln: *i64, lab: *i64, nl: i64) -> i64 {
92 let cur: *i64=sys_mmap(8) as *i64; cur[0]=ra_afterword(s,fp)
93 if ra_word_is(s,fp,"lui" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); return ra_u(0x37,d,im) }
94 if ra_word_is(s,fp,"li" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,0,d,0,im) }
95 if ra_word_is(s,fp,"addi" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,0,d,a,im) }
96 if ra_word_is(s,fp,"andi" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,7,d,a,im) }
97 if ra_word_is(s,fp,"ori" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,6,d,a,im) }
98 if ra_word_is(s,fp,"slli" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_sh6(im)==0 { return 0-1 } return ra_i(0x13,1,d,a,im) }
99 if ra_word_is(s,fp,"srli" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_sh6(im)==0 { return 0-1 } return ra_i(0x13,5,d,a,im) }
100 if ra_word_is(s,fp,"add" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,0,0,d,a,b) }
101 if ra_word_is(s,fp,"sub" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,0,0x20,d,a,b) }
102 if ra_word_is(s,fp,"and" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,7,0,d,a,b) }
103 if ra_word_is(s,fp,"or" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,6,0,d,a,b) }
104 if ra_word_is(s,fp,"xor" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,4,0,d,a,b) }
105 if ra_word_is(s,fp,"sll" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,1,0,d,a,b) }
106 if ra_word_is(s,fp,"srl" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,5,0,d,a,b) }
107 // compare + arithmetic-shift family the compiler backend emits for every `<` / `>>` (LN34, 2026-09-03: the
108 // backend's own loop `slt s4, t4, t5 / bnez t4, .L..` was the first thing the end-to-end gate refused).
109 if ra_word_is(s,fp,"slt" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,2,0,d,a,b) }
110 if ra_word_is(s,fp,"sltu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,3,0,d,a,b) }
111 if ra_word_is(s,fp,"sra" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,5,0x20,d,a,b) }
112 if ra_word_is(s,fp,"slti" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,2,d,a,im) }
113 if ra_word_is(s,fp,"sltiu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,3,d,a,im) }
114 if ra_word_is(s,fp,"xori" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x13,4,d,a,im) }
115 if ra_word_is(s,fp,"srai" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_sh6(im)==0 { return 0-1 } return ra_i(0x13,5,d,a,0x400|im) }
116 // zero-compare branch pseudo-instructions: `bnez rs, L` = `bne rs, zero, L`, `beqz rs, L` = `beq rs, zero, L`
117 if ra_word_is(s,fp,"bnez" as *u8)==1 { let a: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(1,a,0,t-abs) }
118 if ra_word_is(s,fp,"beqz" as *u8)==1 { let a: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(0,a,0,t-abs) }
119 // M extension (funct7=0x01): the assembler now emits everything the emulator executes (ISA-consistent toolchain).
120 if ra_word_is(s,fp,"mulhu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,3,0x01,d,a,b) }
121 if ra_word_is(s,fp,"mulhsu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,2,0x01,d,a,b) }
122 if ra_word_is(s,fp,"mulh" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,1,0x01,d,a,b) }
123 if ra_word_is(s,fp,"mul" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,0,0x01,d,a,b) }
124 if ra_word_is(s,fp,"divu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,5,0x01,d,a,b) }
125 if ra_word_is(s,fp,"div" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,4,0x01,d,a,b) }
126 if ra_word_is(s,fp,"remu" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,7,0x01,d,a,b) }
127 if ra_word_is(s,fp,"rem" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x33,6,0x01,d,a,b) }
128 // *W (32-bit word) ops -- OP-32 (0x3B) register + OP-IMM-32 (0x1B) immediate. ISA-consistent: fk/JIT execute these.
129 if ra_word_is(s,fp,"addiw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); if ra_imm12(im)==0 { return 0-1 } return ra_i(0x1B,0,d,a,im) }
130 if ra_word_is(s,fp,"slliw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); return ra_i(0x1B,1,d,a,im) }
131 if ra_word_is(s,fp,"srliw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); return ra_i(0x1B,5,d,a,im) }
132 if ra_word_is(s,fp,"sraiw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); return ra_i(0x1B,5,d,a,0x400|im) }
133 if ra_word_is(s,fp,"addw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,0,0,d,a,b) }
134 if ra_word_is(s,fp,"subw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,0,0x20,d,a,b) }
135 if ra_word_is(s,fp,"sllw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,1,0,d,a,b) }
136 if ra_word_is(s,fp,"srlw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,5,0,d,a,b) }
137 if ra_word_is(s,fp,"sraw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,5,0x20,d,a,b) }
138 if ra_word_is(s,fp,"mulw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,0,0x01,d,a,b) }
139 if ra_word_is(s,fp,"divuw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,5,0x01,d,a,b) }
140 if ra_word_is(s,fp,"divw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,4,0x01,d,a,b) }
141 if ra_word_is(s,fp,"remuw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,7,0x01,d,a,b) }
142 if ra_word_is(s,fp,"remw" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); return ra_r(0x3B,6,0x01,d,a,b) }
143 if ra_word_is(s,fp,"lw" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_i(0x03,2,d,a,im) }
144 if ra_word_is(s,fp,"lbu" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_i(0x03,4,d,a,im) }
145 if ra_word_is(s,fp,"lhu" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_i(0x03,5,d,a,im) }
146 if ra_word_is(s,fp,"ld" as *u8)==1 { let d: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_i(0x03,3,d,a,im) }
147 if ra_word_is(s,fp,"sw" as *u8)==1 { let v: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_s(0x23,2,a,v,im) }
148 if ra_word_is(s,fp,"sb" as *u8)==1 { let v: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_s(0x23,0,a,v,im) }
149 if ra_word_is(s,fp,"sh" as *u8)==1 { let v: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_s(0x23,1,a,v,im) }
150 if ra_word_is(s,fp,"sd" as *u8)==1 { let v: i64=ra_reg(s,cur); let im: i64=ra_imm(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur); return ra_s(0x23,3,a,v,im) }
151 if ra_word_is(s,fp,"beq" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(0,a,b,t-abs) }
152 if ra_word_is(s,fp,"bne" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(1,a,b,t-abs) }
153 if ra_word_is(s,fp,"blt" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(4,a,b,t-abs) }
154 if ra_word_is(s,fp,"bge" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(5,a,b,t-abs) }
155 if ra_word_is(s,fp,"bltu" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(6,a,b,t-abs) }
156 if ra_word_is(s,fp,"bgeu" as *u8)==1 { let a: i64=ra_reg(s,cur); let b: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_b(7,a,b,t-abs) }
157 if ra_word_is(s,fp,"jal" as *u8)==1 { let d: i64=ra_reg(s,cur); let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_j(d,t-abs) }
158 if ra_word_is(s,fp,"j" as *u8)==1 { let t: i64=ra_label_target(s,cur,lst,lln,lab,nl); return ra_j(0,t-abs) }
159 if ra_word_is(s,fp,"mv" as *u8)==1 { let d: i64=ra_reg(s,cur); let a: i64=ra_reg(s,cur); return ra_i(0x13,0,d,a,0) }
160 if ra_word_is(s,fp,"ret" as *u8)==1 { return ra_i(0x67,0,0,1,0) }
161 if ra_word_is(s,fp,"nop" as *u8)==1 { return ra_i(0x13,0,0,0,0) }
162 if ra_word_is(s,fp,"ecall" as *u8)==1 { return ra_i(0x73,0,0,0,0) }
163 // RV64A (LN34, 2026-09-03): the atomics the compiler backend lowers OP_ATOMIC_* to. R-type, opcode 0x2F,
164 // funct3 = 3 (.d) / 2 (.w), funct7 = funct5<<2 | aq<<1 | rl. Operand shape `rd, rs2, (rs1)`; lr has no rs2.
165 // The .aqrl forms are what the backend emits (sequentially consistent by construction); the bare forms are
166 // kept so hand-written drivers can relax them. The sim (rv64im_min_sim) decodes both and ignores aq/rl.
167 if ra_word_is(s,fp,"amoadd.d.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_ADD,RA_F3_D,RA_AQRL) }
168 if ra_word_is(s,fp,"amoadd.d" as *u8)==1 { return ra_amo(s,cur,RA_AMO_ADD,RA_F3_D,0) }
169 if ra_word_is(s,fp,"amoadd.w.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_ADD,RA_F3_W,RA_AQRL) }
170 if ra_word_is(s,fp,"amoadd.w" as *u8)==1 { return ra_amo(s,cur,RA_AMO_ADD,RA_F3_W,0) }
171 if ra_word_is(s,fp,"amoswap.d.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SWAP,RA_F3_D,RA_AQRL) }
172 if ra_word_is(s,fp,"amoswap.d" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SWAP,RA_F3_D,0) }
173 if ra_word_is(s,fp,"amoswap.w.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SWAP,RA_F3_W,RA_AQRL) }
174 if ra_word_is(s,fp,"amoswap.w" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SWAP,RA_F3_W,0) }
175 if ra_word_is(s,fp,"sc.d.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SC,RA_F3_D,RA_AQRL) }
176 if ra_word_is(s,fp,"sc.d" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SC,RA_F3_D,0) }
177 if ra_word_is(s,fp,"sc.w.aqrl" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SC,RA_F3_W,RA_AQRL) }
178 if ra_word_is(s,fp,"sc.w" as *u8)==1 { return ra_amo(s,cur,RA_AMO_SC,RA_F3_W,0) }
179 if ra_word_is(s,fp,"lr.d.aqrl" as *u8)==1 { return ra_lr(s,cur,RA_F3_D,RA_AQRL) }
180 if ra_word_is(s,fp,"lr.d" as *u8)==1 { return ra_lr(s,cur,RA_F3_D,0) }
181 if ra_word_is(s,fp,"lr.w.aqrl" as *u8)==1 { return ra_lr(s,cur,RA_F3_W,RA_AQRL) }
182 if ra_word_is(s,fp,"lr.w" as *u8)==1 { return ra_lr(s,cur,RA_F3_W,0) }
183 // FENCE (opcode 0x0F, funct3 0): imm = pred<<4 | succ over the iorw bit-set (i=8 o=4 r=2 w=1). A bare
184 // `fence` is `fence iorw, iorw` per the ISA manual; `fence rw, rw` / `fence r, rw` / `fence rw, w` are the
185 // sequentially-consistent load/store mappings the backend emits.
186 if ra_word_is(s,fp,"fence" as *u8)==1 { return ra_fence(s,cur) }
187 return 0-1
188}
189// `amo<op>.<w|d>[.aqrl] rd, rs2, (rs1)` and `sc.<w|d>[.aqrl] rd, rs2, (rs1)`
190func ra_amo(s: *u8, cur: *i64, funct5: i64, f3: i64, aqrl: i64) -> i64 {
191 let d: i64=ra_reg(s,cur); let v: i64=ra_reg(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur)
192 return ra_r(RA_OP_AMO, f3, (funct5<<2)|aqrl, d, a, v)
193}
194// `lr.<w|d>[.aqrl] rd, (rs1)` -- rs2 is zero by the ISA
195func ra_lr(s: *u8, cur: *i64, f3: i64, aqrl: i64) -> i64 {
196 let d: i64=ra_reg(s,cur); ra_past(s,cur,40); let a: i64=ra_reg(s,cur)
197 return ra_r(RA_OP_AMO, f3, (RA_AMO_LR<<2)|aqrl, d, a, 0)
198}
199// one iorw set at the cursor -> its 4-bit mask (stops at ',' / whitespace / end); 0 letters = no set present
200func ra_fence_set(s: *u8, cur: *i64) -> i64 {
201 ra_skipsep(s,cur); var p: i64=cur[0]; var m: i64=0
202 var go: i64=1
203 while go==1 {
204 let c: i64=s[p] as i64
205 if c==105 { m=m|RA_FENCE_I; p=p+1 } else { if c==111 { m=m|RA_FENCE_O; p=p+1 } else { if c==114 { m=m|RA_FENCE_R; p=p+1 } else { if c==119 { m=m|RA_FENCE_W; p=p+1 } else { go=0 } } } }
206 }
207 cur[0]=p
208 return m
209}
210func ra_fence(s: *u8, cur: *i64) -> i64 {
211 var pred: i64=ra_fence_set(s,cur)
212 var succ: i64=0
213 if pred==0 { pred=RA_FENCE_IORW; succ=RA_FENCE_IORW } else { succ=ra_fence_set(s,cur); if succ==0 { return 0-1 } }
214 return ra_i(RA_OP_FENCE, 0, 0, 0, (pred<<4)|succ)
215}
216
217const RA_MAXSYM: i64 = 256
218// A line starting with '.' is an assembler DIRECTIVE (.globl/.text/.word, skipped) UNLESS it is a LABEL --
219// the compiler backend names every block `.L<fn>_bb<n>:` and every branch targets one, so a '.'-prefixed
220// line whose last non-blank byte is ':' must reach the label table (LN34, 2026-09-03; before this every
221// backend label was silently dropped and every branch read as an unresolved target).
222func ra_is_directive(s: *u8, fp: i64, le: i64) -> i64 {
223 if (s[fp] as i64)!=46 { return 0 }
224 var q: i64=le-1; var go: i64=1
225 while go==1 { if q<fp { go=0 } else { if ra_isws(s[q] as i64)==1 { q=q-1 } else { go=0 } } }
226 if q>=fp { if (s[q] as i64)==58 { return 0 } }
227 return 1
228}
229// assemble src -> flat binary in out. returns byte length, or negative on error.
230func rvasm_assemble(s: *u8, n: i64, out: *u8, cap: i64) -> i64 {
231 let lst: *i64=sys_mmap(8*RA_MAXSYM) as *i64; let lln: *i64=sys_mmap(8*RA_MAXSYM) as *i64; let lab: *i64=sys_mmap(8*RA_MAXSYM) as *i64
232 var nl: i64=0
233 // ---- pass 1: collect labels ----
234 var pos: i64=0; var abs: i64=0
235 while pos<n {
236 var le: i64=pos; var f1: i64=1; while f1==1 { if le>=n { f1=0 } else { if (s[le] as i64)==10 { f1=0 } else { le=le+1 } } }
237 var fp: i64=pos; var f2: i64=1; while f2==1 { if fp>=le { f2=0 } else { if ra_isws(s[fp] as i64)==1 { fp=fp+1 } else { f2=0 } } }
238 if fp<le { let c0: i64=s[fp] as i64
239 if c0==35 { } else { if ra_is_directive(s,fp,le)==1 { } else {
240 var q: i64=le-1; var f3: i64=1; while f3==1 { if q<fp { f3=0 } else { if ra_isws(s[q] as i64)==1 { q=q-1 } else { f3=0 } } }
241 if (s[q] as i64)==58 { if nl<RA_MAXSYM { lst[nl]=fp; lln[nl]=q-fp; lab[nl]=abs; nl=nl+1 } }
242 else { abs=abs+4 }
243 } }
244 }
245 pos=le+1
246 }
247 // ---- pass 2: encode ----
248 pos=0; abs=0
249 while pos<n {
250 var le: i64=pos; var g1: i64=1; while g1==1 { if le>=n { g1=0 } else { if (s[le] as i64)==10 { g1=0 } else { le=le+1 } } }
251 var fp: i64=pos; var g2: i64=1; while g2==1 { if fp>=le { g2=0 } else { if ra_isws(s[fp] as i64)==1 { fp=fp+1 } else { g2=0 } } }
252 if fp<le { let c0: i64=s[fp] as i64
253 if c0==35 { } else { if ra_is_directive(s,fp,le)==1 { } else {
254 var q: i64=le-1; var g3: i64=1; while g3==1 { if q<fp { g3=0 } else { if ra_isws(s[q] as i64)==1 { q=q-1 } else { g3=0 } } }
255 if (s[q] as i64)==58 { } else {
256 let w: i64=ra_encode(s, fp, abs, lst, lln, lab, nl)
257 if w==(0-1) { return 0-2 }
258 if abs+4>cap { return 0-3 }
259 ra_put(out, abs, w); abs=abs+4
260 }
261 } }
262 }
263 pos=le+1
264 }
265 return abs
266}
267// convenience: assemble a null-terminated source string (computes length internally).
268func rvasm_assemble_str(s: *u8, out: *u8, cap: i64) -> i64 { var n: i64=0; while (s[n] as i64)!=0 { n=n+1 } return rvasm_assemble(s, n, out, cap) }