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) }