RISC-V Complete Documentation
Instructions
add reg, reg, reg
Addition: set t1 to (t2 plus t3)
add.uw reg, reg, reg
Add unsigned word : Set t1 to the low 32 bits of t2, zero extended, plus t3. Adds an unsigned 32 bit index to a 64 bit base without a separate zero extension (64bit)
addi reg, reg, [imm / %lo(id)]
Addition immediate: set t1 to (t2 plus signed 12-bit immediate)
Load Lower Address : Set t1 to t2 + lower 12-bit label's address
addiw reg, reg, imm
Addition immediate: set t1 to (t2 plus signed 12-bit immediate) using only the lower 32 bits (64bit)
addw reg, reg, reg
Addition: set t1 to (t2 plus t3) using only the lower 32 bits (64bit)
amoadd.d reg, reg, (reg)
Atomic add : Set t1 to the double word at the address in t3, and store that value plus t2 there. Useful for a shared counter that several agents increment (64bit)
amoadd.w reg, reg, (reg)
Atomic add : Set t1 to the word at the address in t3, and store that value plus t2 there. Useful for a shared counter that several agents increment
amoand.d reg, reg, (reg)
Atomic and : Set t1 to the double word at the address in t3, and store the bitwise AND of that value and t2 there. Clears a bit mask in shared memory (64bit)
amoand.w reg, reg, (reg)
Atomic and : Set t1 to the word at the address in t3, and store the bitwise AND of that value and t2 there. Clears a bit mask in shared memory
amomax.d reg, reg, (reg)
Atomic maximum (signed) : Set t1 to the double word at the address in t3, and store the larger of that value and t2 there, comparing as signed (64bit)
amomax.w reg, reg, (reg)
Atomic maximum (signed) : Set t1 to the word at the address in t3, and store the larger of that value and t2 there, comparing as signed
amomaxu.d reg, reg, (reg)
Atomic maximum (unsigned) : Set t1 to the double word at the address in t3, and store the larger of that value and t2 there, comparing as unsigned (64bit)
amomaxu.w reg, reg, (reg)
Atomic maximum (unsigned) : Set t1 to the word at the address in t3, and store the larger of that value and t2 there, comparing as unsigned
amomin.d reg, reg, (reg)
Atomic minimum (signed) : Set t1 to the double word at the address in t3, and store the smaller of that value and t2 there, comparing as signed (64bit)
amomin.w reg, reg, (reg)
Atomic minimum (signed) : Set t1 to the word at the address in t3, and store the smaller of that value and t2 there, comparing as signed
amominu.d reg, reg, (reg)
Atomic minimum (unsigned) : Set t1 to the double word at the address in t3, and store the smaller of that value and t2 there, comparing as unsigned (64bit)
amominu.w reg, reg, (reg)
Atomic minimum (unsigned) : Set t1 to the word at the address in t3, and store the smaller of that value and t2 there, comparing as unsigned
amoor.d reg, reg, (reg)
Atomic or : Set t1 to the double word at the address in t3, and store the bitwise OR of that value and t2 there. Sets a bit mask in shared memory (64bit)
amoor.w reg, reg, (reg)
Atomic or : Set t1 to the word at the address in t3, and store the bitwise OR of that value and t2 there. Sets a bit mask in shared memory
amoswap.d reg, reg, (reg)
Atomic swap : Set t1 to the double word at the address in t3, and store t2 there. The exchange is indivisible, which makes this the natural way to implement a spinlock (64bit)
amoswap.w reg, reg, (reg)
Atomic swap : Set t1 to the word at the address in t3, and store t2 there. The exchange is indivisible, which makes this the natural way to implement a spinlock
amoxor.d reg, reg, (reg)
Atomic xor : Set t1 to the double word at the address in t3, and store the bitwise XOR of that value and t2 there (64bit)
amoxor.w reg, reg, (reg)
Atomic xor : Set t1 to the word at the address in t3, and store the bitwise XOR of that value and t2 there
and reg, reg, reg
Bitwise AND : Set t1 to bitwise AND of t2 and t3
andi reg, reg, imm
Bitwise AND immediate : Set t1 to bitwise AND of t2 and sign-extended 12-bit immediate
andn reg, reg, reg
AND with inverted operand : Set t1 to the bitwise AND of t2 and the bitwise NOT of t3. Clears in t2 every bit that is set in t3
auipc reg, imm
Add upper immediate to pc: set t1 to (pc plus an upper 20-bit immediate)
b id
Branch : Branch to statement at label unconditionally
bclr reg, reg, reg
Clear bit : Set t1 to t2 with the single bit numbered by the low bits of t3 cleared
bclri reg, reg, imm
Clear bit by immediate : Set t1 to t2 with the single bit numbered by the immediate cleared
beq reg, reg, id
Branch if equal : Branch to statement at label's address if t1 and t2 are equal
beqz reg, id
Branch if EQual Zero : Branch to statement at label if t1 == 0
bext reg, reg, reg
Extract bit : Set t1 to the single bit of t2 numbered by the low bits of t3, as 0 or 1
bexti reg, reg, imm
Extract bit by immediate : Set t1 to the single bit of t2 numbered by the immediate, as 0 or 1
bge reg, reg, id
Branch if greater than or equal: Branch to statement at label's address if t1 is greater than or equal to t2
bgeu reg, reg, id
Branch if greater than or equal to (unsigned): Branch to statement at label's address if t1 is greater than or equal to t2 (with an unsigned interpretation)
bgez reg, id
Branch if Greater than or Equal to Zero : Branch to statement at label if t1 >= 0
bgt reg, reg, id
Branch if Greater Than : Branch to statement at label if t1 > t2
bgtu reg, reg, id
Branch if Greater Than Unsigned: Branch to statement at label if t1 > t2 (unsigned compare)
bgtz reg, id
Branch if Greater Than Zero: Branch to statement at label if t1 > 0
binv reg, reg, reg
Invert bit : Set t1 to t2 with the single bit numbered by the low bits of t3 flipped
binvi reg, reg, imm
Invert bit by immediate : Set t1 to t2 with the single bit numbered by the immediate flipped
ble reg, reg, id
Branch if Less or Equal : Branch to statement at label if t1 <= t2
bleu reg, reg, id
Branch if Less or Equal Unsigned : Branch to statement at label if t1 <= t2 (unsigned compare)
blez reg, id
Branch if Less than or Equal to Zero : Branch to statement at label if t1 <= 0
blt reg, reg, id
Branch if less than: Branch to statement at label's address if t1 is less than t2
bltu reg, reg, id
Branch if less than (unsigned): Branch to statement at label's address if t1 is less than t2 (with an unsigned interpretation)
bltz reg, id
Branch if Less Than Zero : Branch to statement at label if t1 < 0
bne reg, reg, id
Branch if not equal : Branch to statement at label's address if t1 and t2 are not equal
bnez reg, id
Branch if Not Equal Zero : Branch to statement at label if t1 != 0
bset reg, reg, reg
Set bit : Set t1 to t2 with the single bit numbered by the low bits of t3 set
bseti reg, reg, imm
Set bit by immediate : Set t1 to t2 with the single bit numbered by the immediate set
call id
CALL: call a far-away subroutine
clz reg, reg
Count leading zeros : Set t1 to the number of 0 bits above the most significant 1 bit of t2, which is the full register width when t2 is zero
clzw reg, reg
Count leading zeros (32 bit) : Set t1 to the number of 0 bits above the most significant 1 bit of the low 32 bits of t2, which is 32 when they are zero (64bit)
cpop reg, reg
Count set bits : Set t1 to the number of 1 bits in t2, also known as the population count
cpopw reg, reg
Count set bits (32 bit) : Set t1 to the number of 1 bits in the low 32 bits of t2 (64bit)
csrc reg, csr
Clear bits in control and status register
csrci csr, imm
Clear bits in control and status register
csrr reg, csr
Read control and status register
csrrc reg, csr, reg
Atomic Read/Clear CSR: read from the CSR into t0 and clear bits of the CSR according to t1
csrrci reg, csr, imm
Atomic Read/Clear CSR Immediate: read from the CSR into t0 and clear bits of the CSR according to a constant
csrrs reg, csr, reg
Atomic Read/Set CSR: read from the CSR into t0 and logical or t1 into the CSR
csrrsi reg, csr, imm
Atomic Read/Set CSR Immediate: read from the CSR into t0 and logical or a constant into the CSR
csrrw reg, csr, reg
Atomic Read/Write CSR: read from the CSR into t0 and write t1 into the CSR
csrrwi reg, csr, imm
Atomic Read/Write CSR Immediate: read from the CSR into t0 and write a constant into the CSR
csrs reg, csr
Set bits in control and status register
csrsi csr, imm
Set bits in control and status register
csrw reg, csr
Write control and status register
csrwi csr, imm
Write control and status register
ctz reg, reg
Count trailing zeros : Set t1 to the number of 0 bits below the least significant 1 bit of t2, which is the full register width when t2 is zero
ctzw reg, reg
Count trailing zeros (32 bit) : Set t1 to the number of 0 bits below the least significant 1 bit of the low 32 bits of t2, which is 32 when they are zero (64bit)
div reg, reg, reg
Division: set t1 to the result of t2/t3
divu reg, reg, reg
Division: set t1 to the result of t2/t3 using unsigned division
divuw reg, reg, reg
Division: set t1 to the result of t2/t3 using unsigned division limited to 32 bits (64bit)
divw reg, reg, reg
Division: set t1 to the result of t2/t3 using only the lower 32 bits (64bit)
ebreak
Pause execution
ecall
Issue a system call : Execute the system call specified by value in a7
fabs.d freg, freg
Set f1 to the absolute value of f2 (64 bit)
fabs.s freg, freg
Set f1 to the absolute value of f2
fadd.d freg, freg, freg, rounding
Floating ADD (64 bit): assigns f1 to f2 + f3
fadd.s freg, freg, freg, rounding
Floating ADD: assigns f1 to f2 + f3
fclass.d reg, freg
Classify a floating point number (64 bit)
fclass.s reg, freg
Classify a floating point number
fcvt.d.l freg, reg, rounding
Convert double from long: Assigns the value of t1 to f1 (64bit)
Convert double from signed 64 bit integer: Assigns the value of t1 to f1 (64bit)
fcvt.d.lu freg, reg, rounding
Convert double from unsigned long: Assigns the value of t1 to f1 (64bit)
Convert double from unsigned 64 bit integer: Assigns the value of t1 to f1 (64bit)
fcvt.d.s freg, freg, rounding
Convert a float to a double: Assigned the value of f2 to f1
Convert float to double: Assigned the value of f2 to f1
fcvt.d.w freg, reg, rounding
Convert double from integer: Assigns the value of t1 to f1
Convert double from signed integer: Assigns the value of t1 to f1
fcvt.d.wu freg, reg, rounding
Convert double from unsigned integer: Assigns the value of t1 to f1
fcvt.l.d reg, freg, rounding
Convert 64 bit integer from double: Assigns the value of f1 (rounded) to t1 (64bit)
Convert signed 64 bit integer from double: Assigns the value of f1 (rounded) to t1 (64bit)
fcvt.l.s reg, freg, rounding
Convert 64 bit integer from float: Assigns the value of f1 (rounded) to t1 (64bit)
Convert signed 64 bit integer from float: Assigns the value of f1 (rounded) to t1 (64bit)
fcvt.lu.d reg, freg, rounding
Convert unsigned 64 bit integer from double: Assigns the value of f1 (rounded) to t1 (64bit)
fcvt.lu.s reg, freg, rounding
Convert unsigned 64 bit integer from float: Assigns the value of f1 (rounded) to t1 (64bit)
fcvt.s.d freg, freg, rounding
Convert a double to a float: Assigned the value of f2 to f1
Convert double to float: Assigned the value of f2 to f1
fcvt.s.l freg, reg, rounding
Convert float from long: Assigns the value of t1 to f1 (64bit)
Convert float from signed 64 bit integer: Assigns the value of t1 to f1 (64bit)
fcvt.s.lu freg, reg, rounding
Convert float from unsigned long: Assigns the value of t1 to f1 (64bit)
Convert float from unsigned 64 bit integer: Assigns the value of t1 to f1 (64bit)
fcvt.s.w freg, reg, rounding
Convert float from integer: Assigns the value of t1 to f1
Convert float from signed integer: Assigns the value of t1 to f1
fcvt.s.wu freg, reg, rounding
Convert float from unsigned integer: Assigns the value of t1 to f1
fcvt.w.d reg, freg, rounding
Convert integer from double: Assigns the value of f1 (rounded) to t1
Convert signed integer from double: Assigns the value of f1 (rounded) to t1
fcvt.w.s reg, freg, rounding
Convert integer from float: Assigns the value of f1 (rounded) to t1
Convert signed integer from float: Assigns the value of f1 (rounded) to t1
fcvt.wu.d reg, freg, rounding
Convert unsinged integer from double: Assigns the value of f1 (rounded) to t1
Convert unsigned integer from double: Assigns the value of f1 (rounded) to t1
fcvt.wu.s reg, freg, rounding
Convert unsinged integer from float: Assigns the value of f1 (rounded) to t1
Convert unsigned integer from float: Assigns the value of f1 (rounded) to t1
fdiv.d freg, freg, freg, rounding
Floating DIVide (64 bit): assigns f1 to f2 / f3
fdiv.s freg, freg, freg, rounding
Floating DIVide: assigns f1 to f2 / f3
fence imm, imm
Ensure that IO and memory accesses before the fence happen before the following IO and memory accesses as viewed by a different thread
fence.i
Ensure that stores to instruction memory are visible to instruction fetches
feq.d reg, freg, freg
Floating EQuals (64 bit): if f1 = f2, set t1 to 1, else set t1 to 0
feq.s reg, freg, freg
Floating EQuals: if f1 = f2, set t1 to 1, else set t1 to 0
fge.d reg, freg, freg
Floating Greater Than or Equal (64 bit): if f2 >= f3, set t1 to 1, else set t1 to 0
fge.s reg, freg, freg
Floating Greater Than or Equal: if f2 >= f3, set t1 to 1, else set t1 to 0
fgt.d reg, freg, freg
Floating Greater Than (64 bit): if f2 > f3, set t1 to 1, else set t1 to 0
fgt.s reg, freg, freg
Floating Greater Than: if f2 > f3, set t1 to 1, else set t1 to 0
fld freg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Load a double from memory
Load Word: Set f1 to 64-bit value from effective memory word address
Load Word: Set f1 to 64-bit value from effective memory word address using t3 as a temporary
Load from Address
fle.d reg, freg, freg
Floating Less than or Equals (64 bit): if f1 <= f2, set t1 to 1, else set t1 to 0
fle.s reg, freg, freg
Floating Less than or Equals: if f1 <= f2, set t1 to 1, else set t1 to 0
flt.d reg, freg, freg
Floating Less Than (64 bit): if f1 < f2, set t1 to 1, else set t1 to 0
flt.s reg, freg, freg
Floating Less Than: if f1 < f2, set t1 to 1, else set t1 to 0
flw freg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Load a float from memory
Load Word Coprocessor 1 : Set f1 to 32-bit value from effective memory word address
Load Word Coprocessor 1 : Set f1 to 32-bit value from effective memory word address using t3 as a temporary
Load from Address
fmadd.d freg, freg, freg, freg, rounding
Fused Multiply Add (64 bit): Assigns f2*f3+f4 to f1
fmadd.s freg, freg, freg, freg, rounding
Fused Multiply Add: Assigns f2*f3+f4 to f1
fmax.d freg, freg, freg
Floating MAXimum (64 bit): assigns f1 to the larger of f1 and f3
fmax.s freg, freg, freg
Floating MAXimum: assigns f1 to the larger of f1 and f3
fmin.d freg, freg, freg
Floating MINimum (64 bit): assigns f1 to the smaller of f1 and f3
fmin.s freg, freg, freg
Floating MINimum: assigns f1 to the smaller of f1 and f3
fmsub.d freg, freg, freg, freg, rounding
Fused Multiply Subatract: Assigns f2*f3-f4 to f1
Fused Multiply Subatract (64 bit): Assigns f2*f3-f4 to f1
fmsub.s freg, freg, freg, freg, rounding
Fused Multiply Subatract: Assigns f2*f3-f4 to f1
fmul.d freg, freg, freg, rounding
Floating MULtiply (64 bit): assigns f1 to f2 * f3
fmul.s freg, freg, freg, rounding
Floating MULtiply: assigns f1 to f2 * f3
fmv.d freg, freg
Move the value of f2 to f1 (64 bit)
fmv.d.x freg, reg
Move float: move bits representing a double from an 64 bit integer register (64bit)
fmv.s freg, freg
Move the value of f2 to f1
fmv.s.x freg, reg
Move float: move bits representing a float from an integer register
fmv.w.x freg, reg
Move float (New mnemonic): move bits representing a float from an integer register
fmv.x.d reg, freg
Move double: move bits representing a double to an 64 bit integer register (64bit)
fmv.x.s reg, freg
Move float: move bits representing a float to an integer register
fmv.x.w reg, freg
Move float (New mnemonic): move bits representing a float to an integer register
fneg.d freg, freg
Set f1 to the negation of f2 (64 bit)
fneg.s freg, freg
Set f1 to the negation of f2
fnmadd.d freg, freg, freg, freg, rounding
Fused Negate Multiply Add (64 bit): Assigns -(f2*f3+f4) to f1
fnmadd.s freg, freg, freg, freg, rounding
Fused Negate Multiply Add: Assigns -(f2*f3+f4) to f1
fnmsub.d freg, freg, freg, freg, rounding
Fused Negated Multiply Subatract: Assigns -(f2*f3-f4) to f1
Fused Negated Multiply Subatract (64 bit): Assigns -(f2*f3-f4) to f1
fnmsub.s freg, freg, freg, freg, rounding
Fused Negated Multiply Subatract: Assigns -(f2*f3-f4) to f1
frcsr reg
Read FP control/status register
frflags reg
Read FP exception flags
frrm reg
Read FP rounding mode
frsr reg
Alias for frcsr t1
fscsr reg, reg
Swap FP control/status register
Write FP control/status register
fsd freg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Store a double to memory
Store Word: Store 64-bit value from f1 to effective memory word address
Store Word: Store 64-bit value from f1 to effective memory word address using t3 as a temporary
Store to Address
fsflags reg, reg
Swap FP exception flags
Write FP exception flags
fsgnj.d freg, freg, freg
Floating point sign injection (64 bit): replace the sign bit of f2 with the sign bit of f3 and assign it to f1
fsgnj.s freg, freg, freg
Floating point sign injection: replace the sign bit of f2 with the sign bit of f3 and assign it to f1
fsgnjn.d freg, freg, freg
Floating point sign injection (inverted 64 bit): replace the sign bit of f2 with the opposite of sign bit of f3 and assign it to f1
fsgnjn.s freg, freg, freg
Floating point sign injection (inverted): replace the sign bit of f2 with the opposite of sign bit of f3 and assign it to f1
fsgnjx.d freg, freg, freg
Floating point sign injection (xor 64 bit): xor the sign bit of f2 with the sign bit of f3 and assign it to f1
fsgnjx.s freg, freg, freg
Floating point sign injection (xor): xor the sign bit of f2 with the sign bit of f3 and assign it to f1
fsqrt.d freg, freg, rounding
Floating SQuare RooT (64 bit): Assigns f1 to the square root of f2
fsqrt.s freg, freg, rounding
Floating SQuare RooT: Assigns f1 to the square root of f2
fsrm reg, reg
Swap FP rounding mode
Write FP rounding mode
fssr reg, reg
Alias for fscsr t1, t2
Alias for fscsr t1
fsub.d freg, freg, freg, rounding
Floating SUBtract (64 bit): assigns f1 to f2 - f3
fsub.s freg, freg, freg, rounding
Floating SUBtract: assigns f1 to f2 - f3
fsw freg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Store a float to memory
Store Word Coprocessor 1 : Store 32-bit value from f1 to effective memory word address
Store Word Coprocessor 1 : Store 32-bit value from f1 to effective memory word address using t3 as a temporary
Store to Address
j id
Jump : Jump to statement at label
jal [reg / id], id
Jump and link : Set t1 to Program Counter (return address) then jump to statement at target address
Jump And Link: Jump to statement at label and set the return address to ra
jalr reg, [reg / imm / imm(reg)], imm
Jump and link register: Set t1 to Program Counter (return address) then jump to statement at t2 + immediate
Jump And Link Register: Jump to address in t0 and set the return address to ra
Jump And Link Register: Jump to address in t1 and set the return address to t0
jr reg, imm
Jump Register: Jump to address in t0
la reg, id
Load Address : Set t1 to label's address
lb reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to sign-extended 8-bit value from effective memory byte address
Load Byte : Set t1 to sign-extended 8-bit value from effective memory byte address
Load Byte : Set $1 to sign-extended 8-bit value from effective memory byte address
Load Byte : Set $t1 to sign-extended 8-bit value from effective memory byte address
Load from Address
lbu reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to zero-extended 8-bit value from effective memory byte address
Load Byte Unsigned : Set $t1 to zero-extended 8-bit value from effective memory byte address
Load Byte Unsigned : Set t1 to zero-extended 8-bit value from effective memory byte address
Load from Address
ld reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to contents of effective memory double word address (64bit)
Load Double word : Set t1 to contents of effective memory word address (64bit)
Load Double word : Set t1 to contents of memory word at label's address (64bit)
Load from Address (64bit)
lh reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to sign-extended 16-bit value from effective memory halfword address
Load Halfword : Set t1 to sign-extended 16-bit value from effective memory halfword address
Load from Address
lhu reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to zero-extended 16-bit value from effective memory halfword address
Load Halfword Unsigned : Set t1 to zero-extended 16-bit value from effective memory halfword address
Load from Address
li reg, imm
Load Immediate : Set t1 to 12-bit immediate (sign-extended)
Load Immediate : Set t1 to 32-bit immediate
Load Immediate : Set t1 to 12-bit immediate (sign-extended) (64bit)
Load Immediate : Set t1 to 32-bit immediate (sign-extended) (64bit)
Load Immediate : Set t1 to 64-bit immediate (64bit)
lr.d reg, (reg)
Load reserved (double word) : Set t1 to the double word at the address in t2 and reserve that address. Pair with sc.d to build an atomic read-modify-write that can be retried (64bit)
lr.w reg, (reg)
Load reserved (word) : Set t1 to the word at the address in t2 and reserve that address. Pair with sc.w to build an atomic read-modify-write that can be retried
lui reg, [imm / %hi(id)]
Load upper immediate: set t1 to 20-bit followed by 12 0s
Load Upper Address : Set t1 to upper 20-bit label's address
lw reg, [imm(reg) / (reg) / imm / id / %lo(id)], (reg)
Set t1 to contents of effective memory word address
Load Word : Set t1 to contents of effective memory word address
Load Word : Set t1 to contents of memory word at label's address
Load from Address
lwu reg, [imm(reg) / %lo(id)], (reg)
Set t1 to contents of effective memory word address without sign-extension (64bit)
Load from Address (64bit)
max reg, reg, reg
Maximum (signed) : Set t1 to the larger of t2 and t3, comparing as signed. Branch free, unlike the usual blt and move
maxu reg, reg, reg
Maximum (unsigned) : Set t1 to the larger of t2 and t3, comparing as unsigned
min reg, reg, reg
Minimum (signed) : Set t1 to the smaller of t2 and t3, comparing as signed. Branch free, unlike the usual blt and move
minu reg, reg, reg
Minimum (unsigned) : Set t1 to the smaller of t2 and t3, comparing as unsigned
mul reg, reg, reg
Multiplication: set t1 to the lower 32 bits of t2*t3
mulh reg, reg, reg
Multiplication: set t1 to the upper 32 bits of t2*t3 using signed multiplication
mulhsu reg, reg, reg
Multiplication: set t1 to the upper 32 bits of t2*t3 where t2 is signed and t3 is unsigned
mulhu reg, reg, reg
Multiplication: set t1 to the upper 32 bits of t2*t3 using unsigned multiplication
mulw reg, reg, reg
Multiplication: set t1 to the lower 32 bits of t2*t3 using only the lower 32 bits of the input (64bit)
mv reg, reg
MoVe : Set t1 to contents of t2
neg reg, reg
NEGate : Set t1 to negation of t2
negw reg, reg
NEGate Word: Set t1 to negation of t2 (only lower 32 bits) (64bit)
nop
NO OPeration
not reg, reg
Bitwise NOT (bit inversion)
or reg, reg, reg
Bitwise OR : Set t1 to bitwise OR of t2 and t3
orc.b reg, reg
Bitwise OR combine within bytes : Set every byte of t1 to 0xFF when the matching byte of t2 has any bit set, and to 0 otherwise. Used to find a zero byte inside a word
ori reg, reg, imm
Bitwise OR immediate : Set t1 to bitwise OR of t2 and sign-extended 12-bit immediate
orn reg, reg, reg
OR with inverted operand : Set t1 to the bitwise OR of t2 and the bitwise NOT of t3
rdcycle reg
Read from cycle
rdcycleh reg
Read from cycleh
rdinstret reg
Read from instret
rdinstreth reg
Read from instreth
rdtime reg
Read from time
rdtimeh reg
Read from timeh
rem reg, reg, reg
Remainder: set t1 to the remainder of t2/t3
remu reg, reg, reg
Remainder: set t1 to the remainder of t2/t3 using unsigned division
remuw reg, reg, reg
Remainder: set t1 to the remainder of t2/t3 using unsigned division limited to 32 bits (64bit)
remw reg, reg, reg
Remainder: set t1 to the remainder of t2/t3 using only the lower 32 bits (64bit)
ret
Return: return from a subroutine
rev8 reg, reg
Reverse bytes : Set t1 to t2 with its bytes in the opposite order, converting between little and big endian
rol reg, reg, reg
Rotate left : Set t1 to t2 rotated left by the low bits of t3, so bits shifted off the top re-enter at the bottom
rolw reg, reg, reg
Rotate left (32 bit) : Set t1 to the low 32 bits of t2 rotated left by the low 5 bits of t3, sign extended (64bit)
ror reg, reg, reg
Rotate right : Set t1 to t2 rotated right by the low bits of t3, so bits shifted off the bottom re-enter at the top
rori reg, reg, imm
Rotate right by immediate : Set t1 to t2 rotated right by the immediate, so bits shifted off the bottom re-enter at the top
roriw reg, reg, imm
Rotate right by immediate (32 bit) : Set t1 to the low 32 bits of t2 rotated right by the immediate, sign extended (64bit)
rorw reg, reg, reg
Rotate right (32 bit) : Set t1 to the low 32 bits of t2 rotated right by the low 5 bits of t3, sign extended (64bit)
sb reg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Store byte : Store the low-order 8 bits of t1 into the effective memory byte address
Store Byte : Store the low-order 8 bits of t1 into the effective memory byte address
Store Byte : Store the low-order 8 bits of $t1 into the effective memory byte address
Store to Address
sc.d reg, reg, (reg)
Store conditional (double word) : Store t2 as a double word at the address in t3 only if that address is still reserved by an lr.d, then set t1 to 0 on success or 1 on failure. Always branch on t1 and retry from the lr.d when it fails (64bit)
sc.w reg, reg, (reg)
Store conditional (word) : Store t2 as a word at the address in t3 only if that address is still reserved by an lr.w, then set t1 to 0 on success or 1 on failure. Always branch on t1 and retry from the lr.w when it fails
sd reg, [imm(reg) / %lo(id)], (reg)
Store double word : Store contents of t1 into effective memory double word address (64bit)
Store to Address (64bit)
seqz reg, reg
Set EQual to Zero : if t2 == 0 then set t1 to 1 else 0
sext.b reg, reg
Sign extend byte : Set t1 to the low byte of t2 sign extended to the full register width
sext.h reg, reg
Sign extend half word : Set t1 to the low 16 bits of t2 sign extended to the full register width
sext.w reg, reg
Sign EXTend Word: extract the low 32-bits from t2 and sign extend it into t1 (64bit)
sgt reg, reg, reg
Set Greater Than : if t2 greater than t3 then set t1 to 1 else 0
sgtu reg, reg, reg
Set Greater Than Unsigned : if t2 greater than t3 (unsigned compare) then set t1 to 1 else 0
sgtz reg, reg
Set Greater Than Zero : if t2 > 0 then set t1 to 1 else 0
sh reg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Store halfword : Store the low-order 16 bits of t1 into the effective memory halfword address
Store Halfword : Store the low-order 16 bits of $1 into the effective memory halfword address
Store Halfword : Store the low-order 16 bits of $t1 into the effective memory halfword address
Store Halfword : Store the low-order 16 bits of t1 into the effective memory halfword address using t2 as a temporary
Store to Address
sh1add reg, reg, reg
Shift left by 1 and add : Set t1 to (t2 << 1) + t3. One instruction for stepping a pointer through an array of 2 byte elements
sh1add.uw reg, reg, reg
Shift unsigned word left by 1 and add : Set t1 to (zero extended low 32 bits of t2 << 1) + t3 (64bit)
sh2add reg, reg, reg
Shift left by 2 and add : Set t1 to (t2 << 2) + t3. One instruction for indexing an array of words
sh2add.uw reg, reg, reg
Shift unsigned word left by 2 and add : Set t1 to (zero extended low 32 bits of t2 << 2) + t3 (64bit)
sh3add reg, reg, reg
Shift left by 3 and add : Set t1 to (t2 << 3) + t3. One instruction for indexing an array of double words
sh3add.uw reg, reg, reg
Shift unsigned word left by 3 and add : Set t1 to (zero extended low 32 bits of t2 << 3) + t3 (64bit)
sll reg, reg, reg
Shift left logical: Set t1 to result of shifting t2 left by number of bits specified by value in low-order 5 bits of t3
slli reg, reg, imm
Shift left logical : Set t1 to result of shifting t2 left by number of bits specified by immediate (64bit)
slli.uw reg, reg, imm
Shift unsigned word left by immediate : Set t1 to the low 32 bits of t2, zero extended, shifted left by the immediate. Scales an unsigned 32 bit index without a separate zero extension (64bit)
slliw reg, reg, imm
Shift left logical (32 bit): Set t1 to result of shifting t2 left by number of bits specified by immediate (64bit)
sllw reg, reg, reg
Shift left logical (32 bit): Set t1 to result of shifting t2 left by number of bits specified by value in low-order 5 bits of t3 (64bit)
slt reg, reg, reg
Set less than : If t2 is less than t3, then set t1 to 1 else set t1 to 0
slti reg, reg, imm
Set less than immediate : If t2 is less than sign-extended 12-bit immediate, then set t1 to 1 else set t1 to 0
sltiu reg, reg, imm
Set less than immediate unsigned : If t2 is less than sign-extended 16-bit immediate using unsigned comparison, then set t1 to 1 else set t1 to 0
sltu reg, reg, reg
Set less than : If t2 is less than t3 using unsigned comparision, then set t1 to 1 else set t1 to 0
sltz reg, reg
Set Less Than Zero : if t2 < 0 then set t1 to 1 else 0
snez reg, reg
Set Not Equal to Zero : if t2 != 0 then set t1 to 1 else 0
sra reg, reg, reg
Shift right arithmetic: Set t1 to result of sign-extended shifting t2 right by number of bits specified by value in low-order 5 bits of t3
srai reg, reg, imm
Shift right arithmetic : Set t1 to result of sign-extended shifting t2 right by number of bits specified by immediate (64bit)
sraiw reg, reg, imm
Shift right arithmetic (32 bit): Set t1 to result of sign-extended shifting t2 right by number of bits specified by immediate (64bit)
sraw reg, reg, reg
Shift left logical (32 bit): Set t1 to result of shifting t2 left by number of bits specified by value in low-order 5 bits of t3 (64bit)
srl reg, reg, reg
Shift right logical: Set t1 to result of shifting t2 right by number of bits specified by value in low-order 5 bits of t3
srli reg, reg, imm
Shift right logical : Set t1 to result of shifting t2 right by number of bits specified by immediate (64bit)
srliw reg, reg, imm
Shift right logical (32 bit): Set t1 to result of shifting t2 right by number of bits specified by immediate (64bit)
srlw reg, reg, reg
Shift left logical (32 bit): Set t1 to result of shifting t2 left by number of bits specified by value in low-order 5 bits of t3 (64bit)
sub reg, reg, reg
Subtraction: set t1 to (t2 minus t3)
subw reg, reg, reg
Subtraction: set t1 to (t2 minus t3) using only the lower 32 bits (64bit)
sw reg, [imm(reg) / (reg) / imm / id / %lo(id)], [reg / (reg)]
Store word : Store contents of t1 into effective memory word address
Store Word : Store t1 contents into effective memory word address
Store Word : Store $t1 contents into effective memory word address
Store Word : Store $t1 contents into effective memory word address using t2 as a temporary
Store Word : Store $t1 contents into memory word at label's address using t2 as a temporary
Store to Address
tail id
TAIL call: tail call (call without saving return address)a far-away subroutine
uret
Return from handling an interrupt or exception (to uepc)
wfi
Wait for Interrupt
xnor reg, reg, reg
Exclusive NOR : Set t1 to the bitwise NOT of the XOR of t2 and t3, so a bit is set wherever t2 and t3 agree
xor reg, reg, reg
Bitwise XOR : Set t1 to bitwise XOR of t2 and t3
xori reg, reg, imm
Bitwise XOR immediate : Set t1 to bitwise XOR of t2 and sign-extended 12-bit immediate
zext.b reg, reg
Zero EXTend Byte: extract the low byte into t1
Zero EXTend Byte: extract the low byte into t1 (64bit)
zext.h reg, reg
Zero extend half word : Set t1 to the low 16 bits of t2 with the rest of the register cleared
zext.w reg, reg
Zero EXTend Word: extract the low 32-bits from t2 into t1 (64bit)
Directives
RISC-V directives are used to define the structure of the program. They are not instructions that are executed by the CPU, but rather instructions that are used by the assembler to define the structure of the program.
.data
Subsequent items stored in Data segment at next available address
.text
Subsequent items (instructions) stored in Text segment at next available address
.word
Store the listed value(s) as 32 bit words on word boundary
.dword
Store the listed value(s) as 64 bit double-word on word boundary
.ascii
Store the string in the Data segment but do not add null terminator
.asciz
Store the string in the Data segment and add null terminator
.string
Alias for .asciz
.byte
Store the listed value(s) as 8 bit bytes
.align
Align next data item on specified byte boundary (0=byte, 1=half, 2=word, 3=double)
.half
Store the listed value(s) as 16 bit halfwords on halfword boundary
.space
Reserve the next specified number of bytes in Data segment
.double
Store the listed value(s) as double precision floating point
.float
Store the listed value(s) as single precision floating point
.extern
Declare the listed label and byte length to be a global data field
.globl
Declare the listed label(s) as global to enable referencing from other files
.global
Declare the listed label(s) as global to enable referencing from other files
.eqv
Substitute second operand for first. First operand is symbol, second operand is expression (like #define)
.macro
Begin macro definition. See .end_macro
.end_macro
End macro definition. See .macro
.include
Insert the contents of the specified file. Put filename in quotes.
.section
Allows specifying sections without .text or .data directives. Included for gcc comparability
.bss
Subsequent items stored in the Data segment, which starts out zeroed
.sbss
Alias for .bss
.zero
Reserve the next specified number of bytes, which read as zero. Alias for .space
.comm
Reserve the given number of bytes for a global symbol, the way a C compiler declares an uninitialized global variable. Takes a symbol, a size in bytes and an optional alignment
.lcomm
Reserve the given number of bytes for a symbol local to this file, the way a C compiler declares an uninitialized static variable
.p2align
Align next data item on a 2^n byte boundary. Alias for .align
.balign
Align next data item on the given byte boundary, written directly rather than as a power of two
.2byte
Alias for .half
.4byte
Alias for .word
.8byte
Alias for .dword
Syscalls
RISC-V syscalls are used to make requests to the operating system. They are not instructions that are executed by the CPU, but rather instructions that are used by the simulator to make requests to the operating system.
Each syscall has a unique code that is used to identify it. You must put the syscall code inside
the the a7 register before calling the ecall instruction.
As an example:
# Load the integer 42 into register a0, which is
# the register that will be printed by the syscall
li a0, 42
# Set the syscall code for printing an integer
li a7, 1
ecall
1 - Print integer
Arguments
2 - Print float
Arguments
3 - Print double
Arguments
4 - Print string
Arguments
5 - Read integer
Result
6 - Read float
Result
7 - Read double
Result
8 - Read string
Service 8 - Follows semantics of UNIX 'fgets'. For specified length n, string can be no longer than n-1. If less than that, adds newline to end. In either case, then pads with null byte If n = 1, input is ignored and null byte placed at buffer address. If n < 1, input is ignored and nothing is written to the buffer.
Arguments
9 - Sbrk (allocate heap memory)
Result
Arguments
10 - Exit (terminate execution)
11 - Print character
Service 11 - Prints ASCII character corresponding to contents of low-order byte.
Arguments
12 - Read character
Result
17 - Get cwd
30 - Time (program time)
Service 30 - Milliseconds since the run started, rather than since 1 January 1970 as in RARS: it is the time the program can observe passing, and in a testcase it comes from a virtual clock that starts at zero and only advances through the waits of service 32.
Result
32 - Sleep
Service 32 - Lets that much program time pass before the next instruction. The editor stays responsive while it waits and the wait costs no instructions, so a program idling on the keyboard never reaches the execution limit; in a testcase it completes at once and advances the virtual clock instead.
Arguments
34 - Print integer in hexadecimal
Displayed value is 8 hexadecimal digits, left-padding with zeroes if necessary.
Arguments
35 - Print integer in binary
Displayed value is 32 bits, left-padding with zeroes if necessary.
Arguments
36 - Print integer as unsigned
Displayed as unsigned decimal value.
Arguments
41 - Random int
Each stream (identified by a0 contents) is modeled by a different Random object. There are no default seed values, so use the Set Seed service (40) if replicated random sequences are desired.
Result
Arguments
42 - Random int range
Each stream (identified by a0 contents) is modeled by a different Random object. There are no default seed values, so use the Set Seed service (40) if replicated random sequences are desired.
Result
Arguments
43 - Random float
Each stream (identified by a0 contents) is modeled by a different Random object. There are no default seed values, so use the Set Seed service (40) if replicated random sequences are desired.
Result
Arguments
44 - Random double
Each stream (identified by a0 contents) is modeled by a different Random object. There are no default seed values, so use the Set Seed service (40) if replicated random sequences are desired.
Result
Arguments
50 - ConfirmDialog
Result
Arguments
51 - InputDialogInt
Result
Arguments
52 - InputDialogFloat
Result
Arguments
53 - InputDialogDouble
Result
Arguments
54 - InputDialogString
See Service 8 note below table
Result
Arguments
55 - MessageDialog
Arguments
56 - MessageDialogInt
Arguments
57 - Close file
Arguments
58 - MessageDialogDouble
Arguments
59 - MessageDialogString
Arguments
60 - MessageDialogFloat
Arguments
63 - Read from file
Result
Arguments
64 - Write to file
Result
Arguments
93 - Exit2 (terminate with value)
Service 93 - If the RISCV program is run under control of the MARS graphical interface (GUI), the exit code in a0 is ignored.
Arguments
1024 - Open file
Service 1024 - MARS implements three flag values: 0 for read-only, 1 for write-only with create, and 9 for write-only with create and append. It ignores mode. The returned file descriptor will be negative if the operation failed. MARS maintains file descriptors internally and allocates them starting with 3. File descriptors 0, 1 and 2 are always open for: reading from standard input, writing to standard output, and writing to standard error, respectively (new in release 4.3).
Result
Arguments
Registers
The 32 general purpose registers, the 32 the floating point extension adds, and the control and status registers the simulator implements.
General purpose registers
RISC-V has 32 general purpose registers, and with one exception the hardware treats them all alike: x0 reads as zero whatever is written to it, and the other 31 are plain 32 bit words, or 64 bit on the RV64 target. Everything else is convention, written down in the ABI and followed by every program that means to call another one.
Each register has a number and a name, and the assembler accepts both: addi t0, zero, 1 and addi x5, x0, 1 assemble to the same instruction. The names are what the panel shows and what a program should use, because the name says what the register is for. The convention divides them into registers a called function may destroy, the temporaries and the arguments, and registers it has to give back unchanged, the saved ones.
zero (x0)
Always reads as 0, and writing to it is silently ignored. It is not a wasted register: having a guaranteed zero is what lets one instruction do the work of many, so mv a0, a1 is really add a0, zero, a1, beqz t0, label is beq t0, zero, label, and a store of a constant zero needs no register loaded first.
ra (x1)
Return address. jal and jalr write the address of the following instruction here before jumping, and a function returns by executing ret, which is jalr zero, ra, 0. A function that calls another one has to save ra on the stack first, because the inner call overwrites it.
sp (x2)
Stack pointer, pointing at the lowest used word of the stack. A function makes room by subtracting from it on entry and gives the room back by adding the same amount before returning. The ABI expects it to stay aligned to 16 bytes at every call.
gp (x3)
Global pointer. It is set up once before the program starts and never changed, so that a global variable near it can be reached in a single instruction with a signed 12 bit offset instead of the two an arbitrary address needs.
tp (x4)
Thread pointer, the base of the storage private to the running thread. A program here has one thread and no use for it, but the ABI reserves it, so nothing else should be kept in it.
t0 - t2 (x5 - x7)
Temporaries. A called function may overwrite them freely, so a caller that still needs a value after the call has to save it, or keep it in a saved register instead.
s0, also written fp (x8)
The first saved register, and by convention the frame pointer: the fixed handle on the current stack frame for functions whose stack pointer moves while they run. The assembler accepts both spellings for the same register.
s1 (x9)
A saved register. A function that uses it must put back the value it found, which is what makes it the right place for anything that has to survive a call.
a0 - a1 (x10 - x11)
The first two arguments of a call, and the registers a result comes back in: a0 carries a single return value and a1 the second half of a pair. The environment calls that this simulator implements also take their argument in a0 and leave their result there.
a2 - a7 (x12 - x17)
Six more argument registers; anything beyond the eighth argument is passed on the stack. a7 has a second job here: it holds the number of the environment call ecall is about to make.
s2 - s11 (x18 - x27)
Ten more saved registers, preserved across calls by whoever uses them. A loop whose body calls a function keeps its counter in one of these.
t3 - t6 (x28 - x31)
Four more temporaries, with the same rule as t0 to t2: free to use, gone after a call.
Floating point registers
The floating point extension adds 32 registers of its own, f0 to f31, with the same kind of ABI names as the general ones. They are a separate file: no arithmetic instruction reads one of each, and a value crosses over only through an explicit instruction. fcvt.s.w ft0, t0 converts the integer in a general register into a single precision number, fcvt.w.s t0, ft0 converts back, and fmv.x.w copies the raw bits of a floating point register into an integer one while fmv.w.x copies them back, neither of them converting anything. The older spellings fmv.x.s and fmv.s.x assemble to the same two instructions. A comparison is the other place the two files meet: feq.s, flt.s and fle.s test two floating point registers and write 1 or 0 into a general register, so a floating point comparison reaches a branch through bnez or beqz rather than through a flag of its own.
Every register here is 64 bits wide, on both the 32 and the 64 bit targets, because that is what a double needs. A single precision value is stored NaN-boxed: it occupies the low 32 bits and the upper 32 are all ones, a pattern that reads as a NaN if a double instruction looks at it. That is deliberate, and it is why the panel shows NaN in the single format for a register holding a genuine double: it is the same protection, running the other way. fadd.s adds singles and fadd.d doubles, flw and fsw load and store a single, fld and fsd a double, and fcvt.d.s widens a single into a double when the two have to meet.
ft0 - ft7 (f0 - f7)
Floating point temporaries, destroyed by a call like the integer ones. The first eight registers of the file, which is why an example that needs one register usually reaches for ft0.
fs0 - fs1 (f8 - f9)
The first two floating point saved registers, preserved across calls by the function that uses them.
fa0 - fa7 (f10 - f17)
The floating point arguments of a call, and in fa0 the floating point return value. They sit beside the integer argument registers rather than counting against them, so a function taking an integer and a double is passed a0 and fa0.
fs2 - fs11 (f18 - f27)
Ten more floating point saved registers. Note that the ABI names run out of order against the numbers: fs2 is f18, well after fa7. The reason is the compressed encoding, the 16 bit form of the instruction set that this simulator does not assemble but that the ABI was written around: an instruction that short has only three bits for a register, which reaches f8 to f15 and no further, so the ABI put the registers a program uses most there, fs0, fs1 and fa0 to fa5, and the saved registers that were left over landed at f18 and above.
ft8 - ft11 (f28 - f31)
Four more floating point temporaries, closing the file. They follow the same rule as ft0 to ft7, and as t3 to t6 on the integer side: free to use, and gone after a call.
Control and status registers
The control and status registers are the machine talking about itself: how the floating point unit is rounding, why an exception happened, how many instructions have run. They are not addressed like the other registers. csrr t0, fcsr reads one into a general register and csrw t0, fcsr writes it back: the general register comes first in both of them and the control register second. Each is a short form of one of the csrr* instructions, which read the old value and write a new one in a single step: csrr is csrrs with zero as the value to set, so nothing is written, and csrw is csrrw with zero as the destination, so the old value is thrown away. The forms that take a constant instead of a register, csrwi and csrsi, are the exception to the order and name the control register first, as in csrsi ustatus, 1.
This simulator implements the seventeen registers below, the user level subset. On the 32 bit target a counter that needs 64 bits is read as two registers, the name for the low half and the name ending in h for the high half; on RV64 the plain name holds all of it.
ustatus (0x000)
The user status register. Only two bits of it are writable here: the one that enables user level interrupts, and the one that remembers whether they were enabled before the current handler was entered, so that uret can put things back as it found them. Entering a handler copies the first into the second and clears the first, which is why a handler is not interrupted by the thing it is handling.
fflags (0x001)
The five floating point exception flags: invalid operation, divide by zero, overflow, underflow and inexact. Arithmetic sets them and nothing clears them, so they say what has happened since the program started, or since it last wrote a zero here. This is not a separate register but the low five bits of fcsr under their own name.
frm (0x002)
The rounding mode the floating point instructions use when they are not given one: round to nearest with ties to even by default, with truncation and the two directed roundings selectable. Like fflags, it is a window onto fcsr, bits 5 to 7.
fcsr (0x003)
The floating point control and status register, which is the two registers above in one place: fflags is its low five bits and frm the three above them. Writing it writes both, which is how a program resets the flags and chooses a rounding mode in a single instruction.
uie (0x004)
Which user level interrupts are enabled, one bit per source. An interrupt is delivered only when its bit is set here and interrupts are enabled in ustatus.
utvec (0x005)
The address of the user level trap handler: where the machine jumps when an exception or an enabled interrupt happens. A program that means to handle its own traps writes the address of its handler here and also sets the enable bit of ustatus, with csrsi ustatus, 1, because this simulator only enters the handler when that bit is set. A trap taken without both of them stops the program with the usual error message.
uscratch (0x040)
A word the handler may use as it likes. The classic use is to give the handler a stack pointer of its own, since the trap arrives with the program halfway through something and no register free to borrow.
uepc (0x041)
The address of the instruction the trap interrupted. uret returns to it, so a handler that has fixed the cause returns unchanged, and a handler that means to skip the offending instruction adds 4 to this register first.
ucause (0x042)
Why the trap happened: a code such as an illegal instruction or a misaligned address, with the top bit set when the cause was an interrupt rather than an exception. A handler serving several causes reads this first.
utval (0x043)
The detail that goes with the cause: the address a load or store failed on, or the instruction that could not be decoded. Meaningless for causes that carry no such value.
uip (0x044)
Which interrupts are pending, in the same bit positions as uie. A source can be pending and not enabled, in which case nothing happens until the program enables it.
cycle, time, instret (0xC00 - 0xC02)
The three counters, read only: elapsed cycles, elapsed time, and the number of instructions retired. A program times a piece of itself by reading one of them before and after and subtracting.
cycleh, timeh, instreth (0xC80 - 0xC82)
The high halves of the three counters, for the 32 bit target where a 64 bit count does not fit in one register. Reading the pair safely means reading the high half, then the low, then the high again, and starting over if it changed in between.
Screen and memory-mapped I/O
RISC-V programs draw and read input through memory, not through system calls: the screen is
a grid of words anywhere in memory, and the keyboard and the console are four words at 0xffff0000. Both are RARS's own tools, the bitmap display and the keyboard and display simulator, with the same parameters and the same
register layout, so a program written for RARS runs here unchanged.
Bitmap display
One word of memory is one pixel. Its low 24 bits are the color, red in bits 23-16, green in 15-8 and blue in 7-0; the top byte is ignored. Words run left to right and then top to bottom, so the pixel below a word is one row of words further on.
The screen panel's Display button configures it, with RARS's own five parameters, and a program can ask for them itself with the @screen comment below. The parameters are saved with the project, and testcases run with them too.
0x10000000 (global data), 0x10008000 ($gp), 0x10010000 (static data), 0x10040000 (heap), 0xffff0000 (memory map). Default 0x10010000 (static data), which
is where .data puts your first label. .data
display:.space 262144 # 256 * 256 words
.text
main:
la t0, display
li t1, 0x00ff8000 # low 24 bits: red 0xff, green 0x80, blue 0x00
sw t1, 0(t0) # the pixel at the top left
sw t1, 1024(t0) # 256 words further on: the one below it
Reserve the memory the grid covers, with .space or a label of your own: the
screen shows whatever those words hold, and a program that writes past what it reserved
is writing over something else. Undo walks the picture back with the code, because the
picture is the memory the emulator rolled back.
Configuring the screen from the program
A comment line naming @screen sets those five parameters at every Build, before the first instruction runs, so opening a program and
building it is all it takes. It is a comment, so the same file still assembles in
RARS, where you set the parameters in the tool's window as usual.
# @screen unit=1 width=256 height=256 base=display
.data
display:.space 262144 # 256 * 256 words
.text
main:
la t0, display
The display width in pixels, one of 64, 128, 256, 512, 1024.
The display height in pixels, one of 64, 128, 256, 512, 1024.
How many pixels wide and high one word is drawn, one of 1, 2, 4, 8, 16, 32. unitWidth and unitHeight set the two separately.
Where the grid starts: a label the program defines, which is the point of it — the program never has to know the address — or an address such as 0x10010000 or 268500992. A label not on a word boundary is rounded down to one.
- Order and spacing do not matter, commas are allowed between settings, and
unitWidth,unit-widthandunitwidthare the same name. - What the directive leaves out keeps the value it had, and a program with no
@screenline changes nothing at all: the configuration stays yours. - A value RARS has no entry for, an unknown setting or a label that does not exist is a warning on the directive's line, never an error: a comment cannot stop a program from assembling. A size off the list is replaced by the nearest one on it, and anything else is left as it was.
- Changing a parameter in the Display popover afterwards wins, until the next Build reads the comment again.
Keyboard and display registers
Four words carry one character each way. Click the screen panel to give the program the
keyboard; the ring around it says the editor's own shortcuts are off while it has focus.
What the program transmits is appended to the console transcript, the same one print services write to, which is also what testcases assert on.
Bit 0 is Ready: a typed character is waiting in the receiver data register. The device sets it and clears it; a program only reads it.
The typed character, in the low byte. Reading it takes that character; the next one, if any, appears at once and Ready stays set until the queue is empty.
Bit 0 is Ready, and here it is always set: the console never makes a program wait to print.
Storing a character in the low byte prints it on the console. ASCII 12, a form feed, clears the console instead.
li s0, 0xffff0000
poll:
lw t0, 0(s0) # receiver control
andi t0, t0, 1 # the Ready bit
bnez t0, take
li a7, 32 # nothing typed yet: a wait costs no instructions
li a0, 10
ecall
j poll
take:
lw t1, 4(s0) # receiver data, one character
sw t1, 12(s0) # transmitter data: print it
The receiver never loses a keystroke: what does not fit in the data register waits in a queue behind it, and Ready (1) stays set until that queue is empty. Bit 1 of either control register, RARS's interrupt-enable bit (2), stops the program with an error: this editor polls, it does not deliver device interrupts.
Program time
Service 30 (a7 before a ecall) answers
with the time in milliseconds, low word in a0 and high word in a1, and service 32 waits for the
milliseconds in a0. Time is counted from the start of the run
rather than from 1970, so a program differences two reads exactly as it did before, and
a wait costs no instructions — a program idling on the keyboard never reaches the
execution limit. In a testcase both run on a virtual clock that starts at zero and only
advances through the program's own waits, so a five second wait finishes at once and
elapsed-time output is the same on every machine.
Differences from RARS
- The display is always there: it is a panel next to the memory view rather than a tool you connect to the program before running it.
- Interrupt-driven I/O is not supported. Setting the interrupt-enable bit of a control register stops the program with an error naming the feature; poll the Ready bit instead.
- Time comes from the start of the run, and a testcase runs on a virtual clock. RARS answers with the host's wall clock.
- There is no mouse: neither simulator's tools have one.
- Programs live in
examples/risc-v/in the repository, one per feature, each naming the display it wants in an@screencomment; RARS has no such directive and ignores the line.