0% found this document useful (0 votes)
96 views24 pages

Basic Instructions

The document provides instructions for basic RISC-V assembly language instructions including arithmetic, logical, comparison, data transfer, control flow, and floating point instructions. It lists over 70 individual instructions along with a brief description of what each instruction does.

Uploaded by

António Gaspar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
96 views24 pages

Basic Instructions

The document provides instructions for basic RISC-V assembly language instructions including arithmetic, logical, comparison, data transfer, control flow, and floating point instructions. It lists over 70 individual instructions along with a brief description of what each instruction does.

Uploaded by

António Gaspar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 24

Basic Instructions:

• add t1,t2,t3 Addition: set t1 to (t2 plus t3)


• addi t1,t2,-100 Addition immediate: set t1 to (t2 plus signed 12-bit immediate)
• and t1,t2,t3 Bitwise AND : Set t1 to bitwise AND of t2 and t3
• andi t1,t2,-100 Bitwise AND immediate : Set t1 to bitwise AND of t2 and sign-
extended 12-bit immediate
• auipc t1,100000 Add upper immediate to pc: set t1 to (pc plus an upper 20-bit
immediate)
• beq t1,t2,label Branch if equal : Branch to statement at label's address if t1 and
t2 are equal
• bge t1,t2,label Branch if greater than or equal: Branch to statement at label's
address if t1 is greater than or equal to t2
• bgeu t1,t2,label 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)
• blt t1,t2,label Branch if less than: Branch to statement at label's address if t1 is
less than t2
• bltu t1,t2,label Branch if less than (unsigned): Branch to statement at label's
address if t1 is less than t2 (with an unsigned interpretation)
• bne t1,t2,label Branch if not equal : Branch to statement at label's address if t1
and t2 are not equal
• csrrc t0, fcsr, t1 Atomic Read/Clear CSR: read from the CSR into t0 and clear
bits of the CSR according to t1
• csrrci t0, fcsr, 10 Atomic Read/Clear CSR Immediate: read from the CSR into t0
and clear bits of the CSR according to a constant
• csrrs t0, fcsr, t1 Atomic Read/Set CSR: read from the CSR into t0 and logical or
t1 into the CSR
• csrrsi t0, fcsr, 10 Atomic Read/Set CSR Immediate: read from the CSR into t0
and logical or a constant into the CSR
• csrrw t0, fcsr, t1 Atomic Read/Write CSR: read from the CSR into t0 and write
t1 into the CSR
• csrrwi t0, fcsr, 10 Atomic Read/Write CSR Immediate: read from the CSR into
t0 and write a constant into the CSR
• div t1,t2,t3 Division: set t1 to the result of t2/t3
• divu t1,t2,t3 Division: set t1 to the result of t2/t3 using unsigned division
• ebreak Pause execution
• ecall Issue a system call : Execute the system call specified by value in a7
• fadd.d f1, f2, f3, dyn Floating ADD (64 bit): assigns f1 to f2 + f3
• fadd.s f1, f2, f3, dyn Floating ADD: assigns f1 to f2 + f3
• fclass.d t1, f1 Classify a floating point number (64 bit)
• fclass.s t1, f1 Classify a floating point number
• fcvt.d.s f1, f2, dyn Convert a float to a double: Assigned the value of f2 to f1
• fcvt.d.w f1, t1, dyn Convert double from integer: Assigns the value of t1 to f1
• fcvt.d.wu f1, t1, dyn Convert double from unsigned integer: Assigns the value of
t1 to f1
• fcvt.s.d f1, f2, dyn Convert a double to a float: Assigned the value of f2 to f1
• fcvt.s.w f1, t1, dyn Convert float from integer: Assigns the value of t1 to f1
• fcvt.s.wu f1, t1, dyn Convert float from unsigned integer: Assigns the value of t1
to f1
• fcvt.w.d t1, f1, dyn Convert integer from double: Assigns the value of f1
(rounded) to t1
• fcvt.w.s t1, f1, dyn Convert integer from float: Assigns the value of f1 (rounded)
to t1
• fcvt.wu.d t1, f1, dyn Convert unsinged integer from double: Assigns the value of
f1 (rounded) to t1
• fcvt.wu.s t1, f1, dyn Convert unsinged integer from float: Assigns the value of f1
(rounded) to t1
• fdiv.d f1, f2, f3, dyn Floating DIVide (64 bit): assigns f1 to f2 / f3
• fdiv.s f1, f2, f3, dyn Floating DIVide: assigns f1 to f2 / f3
• fence 1, 1 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 t1, f1, f2 Floating EQuals (64 bit): if f1 = f2, set t1 to 1, else set t1 to 0
• feq.s t1, f1, f2 Floating EQuals: if f1 = f2, set t1 to 1, else set t1 to 0
• fld f1, -100(t1) Load a double from memory
• fle.d t1, f1, f2 Floating Less than or Equals (64 bit): if f1 <= f2, set t1 to 1, else
set t1 to 0
• fle.s t1, f1, f2 Floating Less than or Equals: if f1 <= f2, set t1 to 1, else set t1 to 0
• flt.d t1, f1, f2 Floating Less Than (64 bit): if f1 < f2, set t1 to 1, else set t1 to 0
• flt.s t1, f1, f2 Floating Less Than: if f1 < f2, set t1 to 1, else set t1 to 0
• flw f1, -100(t1) Load a float from memory
• fmadd.d f1, f2, f3, f4, dynFused Multiply Add (64 bit): Assigns f2*f3+f4 to f1
• fmadd.s f1, f2, f3, f4, dynFused Multiply Add: Assigns f2*f3+f4 to f1
• fmax.d f1, f2, f3 Floating MAXimum (64 bit): assigns f1 to the larger of f1 and
f3
• fmax.s f1, f2, f3 Floating MAXimum: assigns f1 to the larger of f1 and f3
• fmin.d f1, f2, f3 Floating MINimum (64 bit): assigns f1 to the smaller of f1 and
f3
• fmin.s f1, f2, f3 Floating MINimum: assigns f1 to the smaller of f1 and f3
• fmsub.d f1, f2, f3, f4, dynFused Multiply Subatract: Assigns f2*f3-f4 to f1
• fmsub.s f1, f2, f3, f4, dynFused Multiply Subatract: Assigns f2*f3-f4 to f1
• fmul.d f1, f2, f3, dyn Floating MULtiply (64 bit): assigns f1 to f2 * f3
• fmul.s f1, f2, f3, dyn Floating MULtiply: assigns f1 to f2 * f3
• fmv.s.x f1, t1 Move float: move bits representing a float from an integer register
• fmv.x.s t1, f1 Move float: move bits representing a float to an integer register
• fnmadd.d f1, f2, f3, f4, dynFused Negate Multiply Add (64 bit): Assigns -
(f2*f3+f4) to f1
• fnmadd.s f1, f2, f3, f4, dynFused Negate Multiply Add: Assigns -(f2*f3+f4) to
f1
• fnmsub.d f1, f2, f3, f4, dynFused Negated Multiply Subatract: Assigns -(f2*f3-
f4) to f1
• fnmsub.s f1, f2, f3, f4, dynFused Negated Multiply Subatract: Assigns -(f2*f3-
f4) to f1
• fsd f1, -100(t1) Store a double to memory
• fsgnj.d f1, f2, f3 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 f1, f2, f3 Floating point sign injection: replace the sign bit of f2 with the
sign bit of f3 and assign it to f1
• fsgnjn.d f1, f2, f3 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 f1, f2, f3 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 f1, f2, f3 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 f1, f2, f3 Floating point sign injection (xor): xor the sign bit of f2 with
the sign bit of f3 and assign it to f1
• fsqrt.d f1, f2, dyn Floating SQuare RooT (64 bit): Assigns f1 to the square root
of f2
• fsqrt.s f1, f2, dyn Floating SQuare RooT: Assigns f1 to the square root of f2
• fsub.d f1, f2, f3, dyn Floating SUBtract (64 bit): assigns f1 to f2 - f3
• fsub.s f1, f2, f3, dyn Floating SUBtract: assigns f1 to f2 - f3
• fsw f1, -100(t1) Store a float to memory
• jal t1, target Jump and link : Set t1 to Program Counter (return address) then
jump to statement at target address
• jalr t1, t2, -100 Jump and link register: Set t1 to Program Counter (return
address) then jump to statement at t2 + immediate
• lb t1, -100(t2) Set t1 to sign-extended 8-bit value from effective memory byte
address
• lbu t1, -100(t2) Set t1 to zero-extended 8-bit value from effective memory byte
address
• lh t1, -100(t2) Set t1 to sign-extended 16-bit value from effective memory
halfword address
• lhu t1, -100(t2) Set t1 to zero-extended 16-bit value from effective memory
halfword address
• lui t1,100000 Load upper immediate: set t1 to 20-bit followed by 12 0s
• lw t1, -100(t2) Set t1 to contents of effective memory word address
• mul t1,t2,t3 Multiplication: set t1 to the lower 32 bits of t2*t3
• mulh t1,t2,t3 Multiplication: set t1 to the upper 32 bits of t2*t3 using signed
multiplication
• mulhsu t1,t2,t3 Multiplication: set t1 to the upper 32 bits of t2*t3 where t2 is
signed and t3 is unsigned
• mulhu t1,t2,t3 Multiplication: set t1 to the upper 32 bits of t2*t3 using unsigned
multiplication
• or t1,t2,t3 Bitwise OR : Set t1 to bitwise OR of t2 and t3
• ori t1,t2,-100 Bitwise OR immediate : Set t1 to bitwise OR of t2 and sign-
extended 12-bit immediate
• rem t1,t2,t3 Remainder: set t1 to the remainder of t2/t3
• remu t1,t2,t3 Remainder: set t1 to the remainder of t2/t3 using unsigned division
• sb t1, -100(t2) Store byte : Store the low-order 8 bits of t1 into the effective
memory byte address
• sh t1, -100(t2) Store halfword : Store the low-order 16 bits of t1 into the
effective memory halfword address
• sll t1,t2,t3 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 t1,t2,10 Shift left logical : Set t1 to result of shifting t2 left by number of bits
specified by immediate
• slt t1,t2,t3 Set less than : If t2 is less than t3, then set t1 to 1 else set t1 to 0
• slti t1,t2,-100 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 t1,t2,-100 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 t1,t2,t3 Set less than : If t2 is less than t3 using unsigned comparision, then
set t1 to 1 else set t1 to 0
• sra t1,t2,t3 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 t1,t2,10 Shift right arithmetic : Set t1 to result of sign-extended shifting t2
right by number of bits specified by immediate
• srl t1,t2,t3 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 t1,t2,10 Shift right logical : Set t1 to result of shifting t2 right by number of
bits specified by immediate
• sub t1,t2,t3 Subtraction: set t1 to (t2 minus t3)
• sw t1, -100(t2) Store word : Store contents of t1 into effective memory word
address
• uret Return from handling an interrupt or exception (to uepc)
• wfi Wait for Interrupt
• xor t1,t2,t3 Bitwise XOR : Set t1 to bitwise XOR of t2 and t3
• xori t1,t2,-100 Bitwise XOR immediate : Set t1 to bitwise XOR of t2 and sign-
extended 12-bit immediate

Extended (pseudo) Instructions:

• addi t1,t2,%lo(label) Load Lower Address : Set t1 to t2 + lower 12-bit label's


address
• b label Branch : Branch to statement at label unconditionally
• beqz t1,label Branch if EQual Zero : Branch to statement at label if t1 == 0
• bgez t1,label Branch if Greater than or Equal to Zero : Branch to statement at
label if t1 >= 0
• bgt t1,t2,label Branch if Greater Than : Branch to statement at label if t1 > t2
• bgtu t1,t2,label Branch if Greater Than Unsigned: Branch to statement at label if
t1 > t2 (unsigned compare)
• bgtz t1,label Branch if Greater Than Zero: Branch to statement at label if t1 > 0
• ble t1,t2,label Branch if Less or Equal : Branch to statement at label if t1 <= t2
• bleu t1,t2,label Branch if Less or Equal Unsigned : Branch to statement at label
if t1 <= t2 (unsigned compare)
• blez t1,label Branch if Less than or Equal to Zero : Branch to statement at label
if t1 <= 0
• bltz t1,label Branch if Less Than Zero : Branch to statement at label if t1 < 0
• bnez t1,label Branch if Not Equal Zero : Branch to statement at label if t1 != 0
• call label CALL: call a far-away subroutine
• csrc t1, fcsr Clear bits in control and status register
• csrci fcsr, 100 Clear bits in control and status register
• csrr t1, fcsr Read control and status register
• csrs t1, fcsr Set bits in control and status register
• csrsi fcsr, 100 Set bits in control and status register
• csrw t1, fcsr Write control and status register
• csrwi fcsr, 100 Write control and status register
• fabs.d f1, f2 Set f1 to the absolute value of f2 (64 bit)
• fabs.s f1, f2 Set f1 to the absolute value of f2
• fadd.d f1, f2, f3 Floating ADD (64 bit): assigns f1 to f2 + f3
• fadd.s f1, f2, f3 Floating ADD: assigns f1 to f2 + f3
• fcvt.d.s f1, f2 Convert float to double: Assigned the value of f2 to f1
• fcvt.d.w f1, t1 Convert double from signed integer: Assigns the value of t1 to f1
• fcvt.d.wu f1, t1 Convert double from unsigned integer: Assigns the value of t1 to
f1
• fcvt.s.d f1, f2 Convert double to float: Assigned the value of f2 to f1
• fcvt.s.w f1, t1 Convert float from signed integer: Assigns the value of t1 to f1
• fcvt.s.wu f1, t1 Convert float from unsigned integer: Assigns the value of t1 to
f1
• fcvt.w.d t1, f1 Convert signed integer from double: Assigns the value of f1
(rounded) to t1
• fcvt.w.s t1, f1 Convert signed integer from float: Assigns the value of f1
(rounded) to t1
• fcvt.wu.d t1, f1 Convert unsigned integer from double: Assigns the value of f1
(rounded) to t1
• fcvt.wu.s t1, f1 Convert unsigned integer from float: Assigns the value of f1
(rounded) to t1
• fdiv.d f1, f2, f3 Floating DIVide (64 bit): assigns f1 to f2 / f3
• fdiv.s f1, f2, f3 Floating DIVide: assigns f1 to f2 / f3
• fge.d t1, f2, f3 Floating Greater Than or Equal (64 bit): if f2 >= f3, set t1 to 1,
else set t1 to 0
• fge.s t1, f2, f3 Floating Greater Than or Equal: if f2 >= f3, set t1 to 1, else set t1
to 0
• fgt.d t1, f2, f3 Floating Greater Than (64 bit): if f2 > f3, set t1 to 1, else set t1 to
0
• fgt.s t1, f2, f3 Floating Greater Than: if f2 > f3, set t1 to 1, else set t1 to 0
• fld f1,%lo(label)(t2) Load from Address
• fld f1,(t2) Load Word: Set f1 to 64-bit value from effective memory word
address
• fld f1,-100 Load Word: Set f1 to 64-bit value from effective memory word
address
• fld f1,10000000,t3 Load Word: Set f1 to 64-bit value from effective memory
word address using t3 as a temporary
• fld f1,label, t3 Load Word: Set f1 to 64-bit value from effective memory word
address using t3 as a temporary
• flw f1,%lo(label)(t2) Load from Address
• flw f1,(t2) Load Word Coprocessor 1 : Set f1 to 32-bit value from effective
memory word address
• flw f1,-100 Load Word Coprocessor 1 : Set f1 to 32-bit value from effective
memory word address
• flw f1,10000000,t3 Load Word Coprocessor 1 : Set f1 to 32-bit value from
effective memory word address using t3 as a temporary
• flw f1,label, t3 Load Word Coprocessor 1 : Set f1 to 32-bit value from effective
memory word address using t3 as a temporary
• fmadd.d f1, f2, f3, f4 Fused Multiply Add (64 bit): Assigns f2*f3+f4 to f1
• fmadd.s f1, f2, f3, f4 Fused Multiply Add: Assigns f2*f3+f4 to f1
• fmsub.d f1, f2, f3, f4 Fused Multiply Subatract (64 bit): Assigns f2*f3-f4 to f1
• fmsub.s f1, f2, f3, f4 Fused Multiply Subatract: Assigns f2*f3-f4 to f1
• fmul.d f1, f2, f3 Floating MULtiply (64 bit): assigns f1 to f2 * f3
• fmul.s f1, f2, f3 Floating MULtiply: assigns f1 to f2 * f3
• fmv.d f1, f2 Move the value of f2 to f1 (64 bit)
• fmv.s f1, f2 Move the value of f2 to f1
• fmv.w.x f1, t1 Move float (New mnemonic): move bits representing a float from
an integer register
• fmv.x.w t1, f1 Move float (New mnemonic): move bits representing a float to an
integer register
• fneg.d f1, f2 Set f1 to the negation of f2 (64 bit)
• fneg.s f1, f2 Set f1 to the negation of f2
• fnmadd.d f1, f2, f3, f4 Fused Negate Multiply Add (64 bit): Assigns -(f2*f3+f4)
to f1
• fnmadd.s f1, f2, f3, f4 Fused Negate Multiply Add: Assigns -(f2*f3+f4) to f1
• fnmsub.d f1, f2, f3, f4 Fused Negated Multiply Subatract (64 bit): Assigns -
(f2*f3-f4) to f1
• fnmsub.s f1, f2, f3, f4 Fused Negated Multiply Subatract: Assigns -(f2*f3-f4) to
f1
• frcsr t1 Read FP control/status register
• frflags t1 Read FP exception flags
• frrm t1 Read FP rounding mode
• frsr t1 Alias for frcsr t1
• fscsr t1 Write FP control/status register
• fscsr t1, t2 Swap FP control/status register
• fsd f1,(t2) Store Word: Store 64-bit value from f1 to effective memory word
address
• fsd f1,-100 Store Word: Store 64-bit value from f1 to effective memory word
address
• fsd f1,10000000,t3 Store Word: Store 64-bit value from f1 to effective memory
word address using t3 as a temporary
• fsd f1,label, t3 Store Word: Store 64-bit value from f1 to effective memory word
address using t3 as a temporary
• fsflags t1 Write FP exception flags
• fsflags t1, t2 Swap FP exception flags
• fsqrt.d f1, f2 Floating SQuare RooT (64 bit): Assigns f1 to the square root of f2
• fsqrt.s f1, f2 Floating SQuare RooT: Assigns f1 to the square root of f2
• fsrm t1 Write FP rounding mode
• fsrm t1, t2 Swap FP rounding mode
• fssr t1 Alias for fscsr t1
• fssr t1, t2 Alias for fscsr t1, t2
• fsub.d f1, f2, f3 Floating SUBtract (64 bit): assigns f1 to f2 - f3
• fsub.s f1, f2, f3 Floating SUBtract: assigns f1 to f2 - f3
• fsw f1,(t2) Store Word Coprocessor 1 : Store 32-bit value from f1 to effective
memory word address
• fsw f1,-100 Store Word Coprocessor 1 : Store 32-bit value from f1 to effective
memory word address
• fsw f1,10000000,t3 Store Word Coprocessor 1 : Store 32-bit value from f1 to
effective memory word address using t3 as a temporary
• fsw f1,label, t3 Store Word Coprocessor 1 : Store 32-bit value from f1 to
effective memory word address using t3 as a temporary
• j label Jump : Jump to statement at label
• jal label Jump And Link: Jump to statement at label and set the return address to
ra
• jalr t0 Jump And Link Register: Jump to address in t0 and set the return address
to ra
• jalr t0, -100 Jump And Link Register: Jump to address in t0 and set the return
address to ra
• jalr t0,-100(t1) Jump And Link Register: Jump to address in t1 and set the return
address to t0
• jr t0 Jump Register: Jump to address in t0
• jr t0, -100 Jump Register: Jump to address in t0
• la t1,label Load Address : Set t1 to label's address
• lb t1,%lo(label)(t2) Load from Address
• lb t1,(t2) Load Byte : Set t1 to sign-extended 8-bit value from effective memory
byte address
• lb t1,-100 Load Byte : Set $1 to sign-extended 8-bit value from effective
memory byte address
• lb t1,10000000 Load Byte : Set $t1 to sign-extended 8-bit value from effective
memory byte address
• lb t1,label Load Byte : Set $t1 to sign-extended 8-bit value from effective
memory byte address
• lbu t1,(t2) Load Byte Unsigned : Set $t1 to zero-extended 8-bit value from
effective memory byte address
• lbu t1,-100 Load Byte Unsigned : Set $t1 to zero-extended 8-bit value from
effective memory byte address
• lbu t1,10000000 Load Byte Unsigned : Set t1 to zero-extended 8-bit value from
effective memory byte address
• lbu t1,label Load Byte Unsigned : Set t1 to zero-extended 8-bit value from
effective memory byte address
• lh t1,%lo(label)(t2) Load from Address
• lh t1,(t2) Load Halfword : Set t1 to sign-extended 16-bit value from effective
memory halfword address
• lh t1,-100 Load Halfword : Set t1 to sign-extended 16-bit value from effective
memory halfword address
• lh t1,10000000 Load Halfword : Set t1 to sign-extended 16-bit value from
effective memory halfword address
• lh t1,label Load Halfword : Set t1 to sign-extended 16-bit value from effective
memory halfword address
• lhu t1,(t2) Load Halfword Unsigned : Set t1 to zero-extended 16-bit value from
effective memory halfword address
• lhu t1,-100 Load Halfword Unsigned : Set t1 to zero-extended 16-bit value from
effective memory halfword address
• lhu t1,10000000 Load Halfword Unsigned : Set t1 to zero-extended 16-bit value
from effective memory halfword address
• lhu t1,label Load Halfword Unsigned : Set t1 to zero-extended 16-bit value from
effective memory halfword address
• li t1,-100 Load Immediate : Set t1 to 12-bit immediate (sign-extended)
• li t1,10000000 Load Immediate : Set t1 to 32-bit immediate
• lui t1,%hi(label) Load Upper Address : Set t1 to upper 20-bit label's address
• lw t1,%lo(label)(t2) Load from Address
• lw t1,(t2) Load Word : Set t1 to contents of effective memory word address
• lw t1,-100 Load Word : Set t1 to contents of effective memory word address
• lw t1,10000000 Load Word : Set t1 to contents of effective memory word
address
• lw t1,label Load Word : Set t1 to contents of memory word at label's address
• mv t1,t2 MoVe : Set t1 to contents of t2
• neg t1,t2 NEGate : Set t1 to negation of t2
• nop NO OPeration
• not t1,t2 Bitwise NOT (bit inversion)
• rdcycle t1 Read from cycle
• rdcycleh t1 Read from cycleh
• rdinstret t1 Read from instret
• rdinstreth t1 Read from instreth
• rdtime t1 Read from time
• rdtimeh t1 Read from timeh
• ret Return: return from a subroutine
• sb t1,(t2) Store Byte : Store the low-order 8 bits of t1 into the effective memory
byte address
• sb t1,-100 Store Byte : Store the low-order 8 bits of $t1 into the effective
memory byte address
• sb t1,10000000,t2 Store Byte : Store the low-order 8 bits of $t1 into the effective
memory byte address
• sb t1,label,t2 Store Byte : Store the low-order 8 bits of $t1 into the effective
memory byte address
• seqz t1,t2 Set EQual to Zero : if t2 == 0 then set t1 to 1 else 0
• sext.b t1, t2 Sign EXTend Byte: extract the low byte from t2 and sign extend it
into t1
• sext.h t1, t2 Sign EXTend Half: extract the low 16 bit from t2 and sign extend it
into t1
• sgt t1,t2,t3 Set Greater Than : if t2 greater than t3 then set t1 to 1 else 0
• sgtu t1,t2,t3 Set Greater Than Unsigned : if t2 greater than t3 (unsigned
compare) then set t1 to 1 else 0
• sgtz t1,t2 Set Greater Than Zero : if t2 > 0 then set t1 to 1 else 0
• sh t1,(t2) Store Halfword : Store the low-order 16 bits of $1 into the effective
memory halfword address
• sh t1,-100 Store Halfword : Store the low-order 16 bits of $t1 into the effective
memory halfword address
• sh t1,10000000,t2 Store Halfword : Store the low-order 16 bits of t1 into the
effective memory halfword address using t2 as a temporary
• sh t1,label,t2 Store Halfword : Store the low-order 16 bits of t1 into the effective
memory halfword address using t2 as a temporary
• sltz t1,t2 Set Less Than Zero : if t2 < 0 then set t1 to 1 else 0
• snez t1,t2 Set Not Equal to Zero : if t2 != 0 then set t1 to 1 else 0
• sw t1,(t2) Store Word : Store t1 contents into effective memory word address
• sw t1,-100 Store Word : Store $t1 contents into effective memory word address
• sw t1,10000000,t2 Store Word : Store $t1 contents into effective memory word
address using t2 as a temporary
• sw t1,label,t2 Store Word : Store $t1 contents into memory word at label's
address using t2 as a temporary
• tail label TAIL call: tail call (call without saving return address)a far-away
subroutine
• zext.b t1, t2 Zero EXTend Byte: extract the low byte into t1
• zext.h t1, t2 Zero EXTend Half: extract the low 16 bit into t1

Directives:

• .align Align next data item on specified byte boundary (0=byte, 1=half, 2=word,
3=double)
• .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
• .byte Store the listed value(s) as 8 bit bytes
• .data Subsequent items stored in Data segment at next available address
• .double Store the listed value(s) as double precision floating point
• .dword Store the listed value(s) as 64 bit double-word on word boundary
• .end_macro End macro definition. See .macro
• .eqv Substitute second operand for first. First operand is symbol, second operand
is expression (like #define)
• .extern Declare the listed label and byte length to be a global data field
• .float Store the listed value(s) as single precision floating point
• .global Declare the listed label(s) as global to enable referencing from other files
• .globl Declare the listed label(s) as global to enable referencing from other files
• .half Store the listed value(s) as 16 bit halfwords on halfword boundary
• .include Insert the contents of the specified file. Put filename in quotes.
• .macro Begin macro definition. See .end_macro
• .section Allows specifying sections without .text or .data directives. Included for
gcc comparability
• .space Reserve the next specified number of bytes in Data segment
• .string Alias for .asciz
• .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

Syscalls:

SYSCALL functions available in RARS

Introduction

A number of system services, mainly for input and output, are available for use by your
program. They are described in the table below.

Register contents are not affected by a system call, except for result registers as
specified in the table below.

How to use SYSCALL system services


Step 1. Load the service number in register a7.
Step 2. Load argument values, if any, in a0, a1, a2, a3, fa0, ... as specified.
Step 3. Issue the ECALL instruction.
Step 4. Retrieve return values, if any, from result registers as specified.
Example: display the value stored in $t0 on the console
li a7, 1 # service 1 is print integer
add a0, t0, zero # load desired value into argument register a0,
using pseudo-op
ecall

Table of Available Services

Name Number Description Inputs Ouputs


a0 = integer to
PrintInt 1 Prints an integer N/A
print
Prints a floating fa0 = float to
PrintFloat 2 N/A
point number print
Prints a double
fa0 = double to
PrintDouble 3 precision floating N/A
print
point number
Prints a null- a0 = the
PrintString 4 terminated string address of the N/A
to the console string
Reads an int from
ReadInt 5 N/A a0 = the int
input console
Reads a float from
ReadFloat 6 N/A fa0 = the float
input console
Reads a double
ReadDouble 7 from input N/A fa0 = the double
console
a0 = address of
input buffer
Reads a string a1 = maximum
ReadString 8 N/A
from the console number of
characters to
read
a0 = amount of a0 = address to
Allocate heap
Sbrk 9 memory in the allocated
memory
bytes block
Exits the program
Exit 10 N/A N/A
with code 0
a0 = character
Prints an ascii to print (only
PrintChar 11 N/A
character lowest byte is
considered)
Reads a character
a0 = the
ReadChar 12 from input N/A
character
console
Writes the path of a0 = the buffer
a0 = -1 if the
the current to write into
GetCWD 17 path is longer
working directory a1 = the length
than the buffer
into a buffer of the buffer
Get the current a0 = low order
time (milliseconds 32 bits
Time 30 N/A
since 1 January a1=high order 32
1970) bits
Outputs simulated
MIDI tone to
See MIDI note
MidiOut 31 sound card (does N/A
below
not wait for sound
to end).
Set the current
a0 = time to
thread to sleep for
Sleep 32 sleep in N/A
a time (not
milliseconds
precise)
Outputs simulated
MIDI tone to
sound card, then See MIDI note
MidiOutSync 33 N/A
waits until the below
sound finishes
playing.
Prints an integer
(in hexdecimal a0 = integer to
PrintIntHex 34 N/A
format left-padded print
with zeroes)
Prints an integer
(in binary format a0 = integer to
PrintIntBinary 35 N/A
left-padded with print
zeroes)
Prints an integer a0 = integer to
PrintIntUnsigned 36 N/A
(unsigned) print
a0 = index of
Set seed for the
pseudorandom
underlying Java
RandSeed 40 number N/A
pseudorandom
generator
number generator
a1 = the seed
a0 = index of
Get a random pseudorandom a0 = random
RandInt 41
integer number integer
generator
a0 = index of
pseudorandom
number a0 = uniformly
Get a random
RandIntRange 42 generator selectect from
bounded integer
a1 = upper [0,bound]
bound for
random number
a0 = index of fa0 = uniformly
pseudorandom randomly
RandFloat 43 Get a random float
number selected from
generator from [0,1]
a0 = index of
Get a random
pseudorandom fa0 = the next
RandDouble 44 double from the
number pseudorandom
range 0.0-1.0
generator
a0 = address of
Service to display null-terminated a0 = Yes (0), No
ConfirmDialog 50
a message to user string that is the (1), or Cancel(2)
message to user
InputDialogInt 51 N/A N/A N/A
InputDialogFloat 52 N/A N/A N/A
InputDialogDouble 53 N/A N/A N/A
a1 contains
status value.
0: OK status.
Buffer contains
the input string.
-2: Cancel was
a0 = address of chosen. No
null-terminated change to buffer.
string that is the -3: OK was
message to user chosen but no
Service to display a1 = address of data had been
a message to a input buffer input into field.
InputDialogString 54
user and request a a2 = maximum No change to
string input number of buffer.
characters to -4: length of the
read (including input string
the terminating exceeded the
null) specified
maximum.
Buffer contains
the maximum
allowable input
string terminated
with null.
a0 = address of
null-terminated
string that is the
message to user
a1 = the type of
the message to
the user, which
is one of:
Service to display 0: error
MessageDialog 55 N/A
a message to user message
1: information
message
2: warning
message
3: question
message
other: plain
message
a0 = address of
Service to display null-terminated
a message string that is the
MessageDialogInt 56 N/A
followed by a int message to user
to user a1 = the int to
display
a0 = the file
Close 57 Close a file descriptor to N/A
close
a0 = address of
null-terminated
Service to display
string that is the
MessageDialogDouble 58 message followed N/A
message to user
by a double
fa0 = the
double
a0 = address of
null-terminated
Service to display
string that is the
a message
MessageDialogString 59 message to user N/A
followed by a
a1 = address of
string to user
the second
string to display
a0 = address of
Service to display null-terminated
a message string that is the
MessageDialogFloat 60 N/A
followed by a message to user
float to user fa1 = the float
to display
Seek to a position a0 = the file a0 = the selected
LSeek 62
in a file descriptor position from the
a1 = the offset beginning of the
for the base file or -1 is an
a2 is the error occurred
begining of the
file (0), the
current position
(1), or the end
of the file (2)}
a0 = the file
descriptor
Read from a file a0 = the length
a1 = address of
Read 63 descriptor into a read or -1 if
the buffer
buffer error
a2 = maximum
length to read
a0 = the file
descriptor
Write to a a0 = the number
a1 = the buffer
Write 64 filedescriptor from of charcters
address
a buffer written
a2 = the length
to write
a0 = the
Exits the program
Exit2 93 number to exit N/A
with a code
with
Opens a file from
a path
Only supported
flags (a1) are
read-only (0),
write-only (1) and a0 = Null
write-append (9). terminated a0 = the file
Open 1024 write-only flag string for the decriptor or -1 if
creates file if it path an error occurred
does not exist, so a1 = flags
it is technically
write-create.
write-append will
start writing at end
of existing file.

Example of File I/O

The sample program below will open a new file for writing, write text to it from a
memory buffer, then close it. The file will be created in the directory in which RARS
was run.
# Sample program that writes to a new file.
# by Kenneth Vollmar and Pete Sanderson

.data
fout: .asciz "testout.txt" # filename for output
buffer: .asciz "The quick brown fox jumps over the lazy dog."
.text
###############################################################
# Open (for writing) a file that does not exist
li a7, 1024 # system call for open file
la a0, fout # output file name
li a1, 1 # Open for writing (flags are 0: read, 1: write)
ecall # open a file (file descriptor returned in a0)
mv s6, a0 # save the file descriptor
###############################################################
# Write to file just opened
li a7, 64 # system call for write to file
mv a0, s6 # file descriptor
la a1, buffer # address of buffer from which to write
li a2, 44 # hardcoded buffer length
ecall # write to file
###############################################################
# Close the file
li a7, 57 # system call for close file
mv a0, s6 # file descriptor to close
ecall # close file
###############################################################

Using MIDI output

These system services are unique to RARS, and provide a means of producing sound.
MIDI output is simulated by your system sound card, and the simulation is provided by
the javax.sound.midi package.

This service requires four parameters as follows:

pitch (a0)

• Accepts a positive byte value (0-127) that denotes a pitch as it would be represented
in MIDI
• Each number is one semitone / half-step in the chromatic scale.
• 0 represents a very low C and 127 represents a very high G (a standard 88 key piano
begins at 9-A and ends at 108-C).
• If the parameter value is outside this range, it applies a default value 60 which is the
same as middle C on a piano.
• From middle C, all other pitches in the octave are as follows:

• 61 = C# or Db • 65 = E# or F • 69 = A
• 62 = D • 66 = F# or Gb • 70 = A# or Bb
• 63 = D# or Eb • 67 = G • 71 = B or Cb
• 64 = E or Fb • 68 = G# or Ab • 72 = B# or C

• To produce these pitches in other octaves, add or subtract multiples of 12.


duration in milliseconds (a1)

• Accepts a positive integer value that is the length of the tone in milliseconds.
• If the parameter value is negative, it applies a default value of one second (1000
milliseconds).

instrument (a2)

• Accepts a positive byte value (0-127) that denotes the General MIDI "patch" used to
play the tone.
• If the parameter is outside this range, it applies a default value 0 which is an
Acoustic Grand Piano.
• General MIDI standardizes the number associated with each possible instrument
(often referred to as program change numbers), however it does not determine how
the tone will sound. This is determined by the synthesizer that is producing the
sound. Thus a Tuba (patch 58) on one computer may sound different than that same
patch on another computer.
• The 128 available patches are divided into instrument families of 8:

0-7 Piano 64-71 Reed


8-15 Chromatic Percussion 72-79 Pipe
16-23 Organ 80-87 Synth Lead
24-31 Guitar 88-95 Synth Pad
32-39 Bass 96-103 Synth Effects
40-47 Strings 104-111 Ethnic
48-55 Ensemble 112-119 Percussion
56-63 Brass 120-127 Sound Effects

• Note that outside of Java, General MIDI usually refers to patches 1-128. When
referring to a list of General MIDI patches, 1 must be subtracted to play the correct
patch. For a full list of General MIDI instruments, see www.midi.org/about-
midi/gm/gm1sound.shtml. The General MIDI channel 10 percussion key map is not
relevant to the toneGenerator method because it always defaults to MIDI channel 1.

volume (a3)

• Accepts a positive byte value (0-127) where 127 is the loudest and 0 is silent. This
value denotes MIDI velocity which refers to the initial attack of the tone.
• If the parameter value is outside this range, it applies a default value 100.
• MIDI velocity measures how hard a note on (or note off) message is played, perhaps
on a MIDI controller like a keyboard. Most MIDI synthesizers will translate this into
volume on a logarithmic scale in which the difference in amplitude decreases as the
velocity value increases.
• Note that velocity value on more sophisticated synthesizers can also affect the
timbre of the tone (as most instruments sound different when they are played louder
or softer).

MIDI Output was developed and documented by Otterbein student Tony Brock in July
2007.

Exceptions:

Writing and Using RISC-V user mode exception handlers in RARS

Introduction

Interrupts are a way to break away from the current execution path and deal with a
potentially time sensitive issue and then return to the previous execution path. Interrupts
fall into three categories: software, timer, and external. Software interrupts are triggered
by setting a bit in the interrupt pending CSR. Timer interrupts are triggered by a timer.
External interrupts come from outside the CPU. Both timer and external interrupts can
be caused from tools from the tools menu. Synchronous traps are caused by code not
being able to continue on its current path without having additional actiion taken. This
can be because the code is incorrect (e.g. load access misaligned) or because action
needs to be taken by the OS (ecall, ebreak, page fault, etc). Exception handlers allow
programs to handle both of these cases. Every trap must be handled immediately either
by the program, or by RARS. System calls and breakpoints are normally handled by
RARS, but other faults are generally handled by printing an error message to the
console. The program's exception handler doesn't have to print to console though.
Below is a simple handler which just skips over instructions generating traps:
.text
main:
la t0,handler
csrrw zero, 5, t0 # set utvec (5) to the handlers address
csrrsi zero, 0, 1 # set interrupt enable bit in ustatus (0)
lw zero, 0 # trigger trap for Load access fault
j main

handler: # Just ignore it by moving epc (65) to the next instruction


csrrw t0, 65, zero
addi t0, t0, 4
csrrw zero, 65, t0
uret
In order to have a working exception handler, the program must:

• Set utvec to the address of the handler code (the lowest two bits are special)
• Set the bits corresponding to the handled interrupts in uie
• Set the interrupt enable (lowest) bit in ustatus to enable the handler

And inside the handler, :


• The exception handler can return control to the program using the uret
instruction. This will place the uepc CSR value into the Program Counter, so be
sure to increment it by 4 before returning if you want to skip over the instruction
that caused the exception. It also resets some other state to before the exception,
so jumping to the value in uepc instead is not recommended
• ucause contains the reason that the exception handler was called.
• Exception types declared in rars.SimulatorException, but not necessarily
implemented, are INSTRUCTION_ADDR_MISALIGNED (0),
INSTRUCTION_ACCESS_FAULT (1), ILLEGAL_INSTRUCTION(2),
LOAD_ADDRESS_MISALIGNED(4), LOAD_ACCESS_FAULT(5),
STORE_ADDRESS_MISALIGNED(6), STORE_ACCESS_FAULT(7), and
ENVIRONMENT_CALL(8)
• When writing a non-trivial exception handler, your handler must first save
general purpose register contents, then restore them before returning.

The implementation of user-mode exception handlers in RARS is not complete and


does not fully conform to the RISC-V privilidged specification, but it is based upon that
and contributions to make it be more conformant would be appreciated.

For more details, refer to the RISC-V priviledged specification


https://riscv.org/specifications/privileged-isa/; contributions to pull in more details
into this page would be appreciated.

Macros:

Introduction to macros

Patterson and Hennessy define a macro as a pattern-matching and replacement facility


that provides a simple mechanism to name a frequently used sequence of instructions
[1]. This permits the programmer to specify the instruction sequence by invoking the
macro. This requires only one line of code for each use instead of repeatedly typing in
the instruction sequence each time. It follows the axiom "define once, use many times,"
which not only reduces the chance for error but also facilitates program maintenance.

Macros are like procedures (subroutines) in this sense but operate differently than
procedures. Procedures in assembly language follow particular protocols for procedure
definition, call and return. Macros operate by substituting the macro body for each use
at the time of assembly. This substitution is called macro expansion.. They do not
require the protocols and execution overhead of procedures.

As a simple example, you may want to terminate your program from a number of
locations. If you are running from the RARS IDE, you will use system call 10, exit.
The instruction sequence is pretty easy

li a7, 10
ecall
but still tedious. You can define a macro, let's call it done, to represent this sequence
.macro done
li a7, 10
ecall
.end_macro
then invoke it whenever you wish with the statement
done
At assembly time, the assembler will replace each occurrence of the statement done
with the two-statement sequence
li a7, 10
ecall
This is the macro expansion. The runtime simulator is unaware of macros or macro
expansion.

If running RARS from the command line, perhaps you want to return a termination
value. This can be done with syscall 93, exit2, which takes the termination value as an
argument. An equivalent macro, let's call it terminate would be

.macro terminate (%termination_value)


li a0, %termination_value
li a7, 93
ecall
.end_macro
This macro defines a formal parameter to represent the termination value. You would
invoke it with the statement
terminate (1)
to terminate with value 1. Upon assembly, the statement terminate (1) would be
replaced by the three-statement sequence
li a0, 1
li a7, 17
ecall
The argument value, 1, is substituted wherever the formal parameter
%termination_value appears in the macro body. This is a textual substitution. Note
that in this example the argument value must be an integer, not a register name or a
label, because the parameter is used as the second operand in the Load Immediate
operation.

In RARS, a macro is similar to an extended (pseudo) instruction. They are distinguished


in that the expansion of extended instructions is supported by an internally-defined
specification language and mechanism which can manipulate argument values. The
macro facility can only substitute argument values as given, and it uses a separate
mechanism from extended instructions.

Additional examples and details follow.

How to define macros

The first line begins with a .macro directive followed by an optional list of formal
parameters. Placing commas between parameters and parentheses around the list is
optional.

Each formal parameter is an identifier that begins with a % character. For compatibility
with the SPIM preprocessor APP, it may alternatively begin with $.
The lines that follow define the body of the macro. Use the formal parameters as
appropriate. The body may contain data segments as well as text segments.

The macro definition finishes with a .end_macro directive.

See the Notes below for additional information.

How to use macros

To invoke a macro, form a statement consisting of the macro name and then one token
for each argument to be substituted for its corresponding formal parameter by the
assembler. The argument list may optionally be surrounded by parentheses. Arguments
may be separated either by spaces or commas.

Macro expansion is a pre-processing task for assemblers.

Notes

• A macro definition must appear before its use. No forward references.


• All macro definitions are local in each file and they cannot be global.
• Nested macro definitions are not supported. No .macro directive should appear
inside body of a macro definition.
• A macro definition can contain a call to a previously-defined macro. Only
backward references are allowed.
• Labels placed in the body of a macro definition will not have same name after
macro expansion. During expansion, their name will be followed by "_M#"
where # will be a unique number for each macro expansion.
• Two macros with the same name but different number of parameters are
considered different and both can be used.
• A macro defined with the same name and same number of parameters as another
macro defined before it will be ignored.
• Each argument in a macro call can only be a single language element (token).
For instance "4($t0)" cannot be an argument.
• Macros are a part of the assembler, not the ISA. So the syntax might be different
with other assemblers. For compatibility with the SPIM simulator, SPIM-style
macros are also supported in RARS. SPIM-style macros are same as RARS but
formal parameters begin with "$" instead of "%".

Examples

• Printing an integer (argument may be either an immediate value or register


name):
• .macro print_int (%x)
• li a7, 1
• add $a0, $zero, %x
• syscall
• .end_macro

• print_int ($s0)
print_int (10)
• Printing a string (macro will first assign a label to its parameter in data segment
then print it):
• .macro print_str (%str)
• .data
• myLabel: .asciiz %str
• .text
• li a7, 4
• la $a0, myLabel
• syscall
• .end_macro

• print_str ("test1") #"test1" will be labeled with name
"myLabel_M0"
print_str ("test2") #"test2" will be labeled with name
"myLabel_M1"

• Implementing a simple for-loop:


• # generic looping mechanism
• .macro for (%regIterator, %from, %to, %bodyMacroName)
• add %regIterator, $zero, %from
• Loop:
• %bodyMacroName ()
• add %regIterator, %regIterator, 1
• ble %regIterator, %to, Loop
• .end_macro

• #print an integer
• .macro body()
• print_int $t0
• print_str "\n"
• .end_macro

• #printing 1 to 10:
for ($t0, 1, 10, body)

The for macro has 4 parameters. %regIterator should be the name of a


register which iterates from %from to %to and in each iteration %bodyMacroName
will be expanded and run. Arguments for %from and %to can be either a register
name or an immediate value, and %bodyMacroName should be name of a macro
that has no parameters.

Macro source line numbers

For purpose of error messaging and Text Segment display, RARS attempts to display
line numbers for both the definition and use of the pertinent macro statement. If an error
message shows the line number in the form "X->Y" (e.g. "20->4"), then X is the line
number in the expansion (use) where the error was detected and Y is the line number in
the macro definition. In the Text Segment display of source code, the macro definition
line number will be displayed within brackets, e.g. "<4>", at the point of expansion.
Line numbers should correspond to the numbers you would see in the text editor.

The .eqv directive


The .eqv directive (short for "equivalence") is similar to #define in C or C++. It is
used to substitute an arbitrary string for an identifier. It is useful but much less powerful
than macros. It was developed independently of the macro facility.

Using .eqv, you can specify simple substitutions that provide "define once, use many
times" capability at assembly pre-processing time. For example, once you define

.eqv LIMIT 20
.eqv CTR t2
.eqv CLEAR_CTR add CTR, zero, 0
then you can refer to them in subsequent code:
li a7,1
CLEAR_CTR
loop: move a0, CTR
syscall
add CTR, CTR, 1
blt CTR, LIMIT, loop
CLEAR_CTR
During assembly pre-processing, the .eqv substitutions will be applied. The resulting
code is
li a7,1
add t2, zero, 0
loop: move a0, t2
syscall
add t2, t2, 1
blt t2, 20, loop
add t2, zero, 0
which when run will display the values 0 through 19 on one line with no intervening
spaces.

Note that the substitution string is not limited to a single token. Like .macro, .eqv is
local to the file in which it is defined, and must be defined prior to use. Macro bodies
can contain references to .eqv directives.

The .include directive

The .include directive has one operand, a quoted filename. When the directive is
carried out, the contents of the specified file are substituted for the directive. This occurs
during assembly preprocessing. It is like #include in C or C++.

.include is designed to make macro and equivalence (.eqv directive) use more
convenient. Both macro definitions and equivalence definitions are local, which means
they can be used only in the same file where defined. Without .include, you would
have to repeat their definitions in every file where you want to use them. Besides being
tedious, this is poor programming practice; remember "define once, use many times."
Now you can define macros and equivalences in a separate file, then include it in any
file where you want to use them.

The .include preprocessor will detect and flag any circular includes (file that includes
itself, directly or indirectly).

The use of .include presents some challenges for error messaging and for source code
numbering in the Text Segment display. If a file being included has any assembly
errors, the filename and line number in the error message should refer to the file being
included, not the file it was substituted into. Similarly, the line number given in the Text
Segment source code display refers to the line in the file being included. Thus the
displayed line numbers do not monotonically increase - this is also the case when using
the "assemble all" setting. Line numbers should correspond to the numbers you would
see in the text editor.

As a simple example, you could define the done macro (and others) in a separate file
then include it wherever you need it. Suppose "macros.asm" contains the following:

.macro done
li a7,10
syscall
.end_macro
You could then include it in a different source file something like this:
.include "macros.asm"
.data
value: .word 13
.text
li a7, 1
lw a0, value
syscall
done

During assembly preprocessing, this would be expanded to

.macro done
li a7,10
syscall
.end_macro
.data
value: .word 13
.text
li a7, 1
lw a0, value
syscall
done

The assembler will then perform the appropriate macro expansion.

Acknowledgements

All of the RARS macro functionality comes straight from MARS.The MARS macro
facility was developed in 2012 by Mohammad Hossein Sekhavat,
[email protected], while an engineering student at Sharif University in Tehran.
MARS creators Pete and Ken are incredibly grateful for his contribution! Pete
developed .eqv and .include at about the same time.

References

[1] Computer Organization and Design: The Hardware/Software Interface, Fourth


Edition, Patterson and Hennessy, Morgan Kauffman Publishers, 2009.

You might also like