diff --git a/docs/Assembler-Directives.md b/docs/Assembler-Directives.md new file mode 100644 index 00000000..1e4a6206 --- /dev/null +++ b/docs/Assembler-Directives.md @@ -0,0 +1,23 @@ +The currently supported directives are: + +| Name | Description| +|------|------------| +|.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| +|.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| +|.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| diff --git a/docs/Calling-Convention.md b/docs/Calling-Convention.md new file mode 100644 index 00000000..18f6873d --- /dev/null +++ b/docs/Calling-Convention.md @@ -0,0 +1,67 @@ +The last part of hello world that has yet to be explained is how to make a system call. + +## System calls + +System calls are a way to tell the Operating System (OS) that your program would like to do something such as print text to the screen, play a sound or open a file. To do that we generally have to cross the [kernel-user boundary](https://blog.codinghorror.com/understanding-user-and-kernel-mode/). + +The way to actually cross the boundary is with system calls. By executing the `ecall` instruction we transition to kernel mode and then the OS can handle our request and then return control to the program. + +Note: On Windows you don't use system calls directly; instead you call a function which makes the system call for you. However, RARS tries to match Linux's behavior regarding system calls. + +If you want to issue a system call, first you look in the [supported system call list](Environment-Calls.md), find its system call number. + +That number will need to be saved into register `a7`/`x17`; the OS needs to know what you are trying to do. Then if the system call needs inputs those will be put in `a0`-`a6`. With that in place, when `ecall` is executed, the OS will execute the call and if there is output put it in `a0`. + +## Function calls + +Function calls are similar to system calls, but we don't need to cross the kernel boundary. Instead, we save our current location and jump to the beginning of a function; when that function is done it will jump back to that saved location. + +A simple function might look like: +``` +add_one: # has a C declaration of: int add_one(int); + addi a0, a0, 1 + jalr zero, ra, 0 # Alternatively psuedo-op ret + +main: + li a0, 2 + jal ra, add_one # Alternatively psuedo-op "jal add_one" or "call add_one" + jal ra, add_one + # a0 should now be 4 +``` + +The new instructions are `jal` and `jalr`. They stand for "Jump And Link" and "Jump And Link Register" respectively. + +`jal` works by saving the current address into its register argument and jumping to the label argument. `jalr` is similar but it jumps to the address stored in its second argument added with some offset. + +So `jal ra, add_one` saves the current address into `ra`, and jumps to the `add_one` function. The body of the function is executed and then `jalr zero, ra, 0` jumps back to the saved location without saving the current location. + +## Register Names + +While registers can be used for pretty much anything, there is a standard on how to use them so code written by different people will work together. Table 18.2 from the RISC-V standard: + +| Register | ABI name | Description | Saver | +|----------|----------|-------------|-------| +|x0|zero|Hard-wired zero|| +|x1|ra|Return address|Caller| +|x2|sp|Stack pointer|Callee| +|x3|gp|Global pointer|| +|x4|tp|Thread pointer|| +|x5–7|t0–2|Temporaries|Caller| +|x8|s0/fp|Saved register/frame pointer|Callee| +|x9|s1|Saved register|Callee| +|x10–11|a0–1|Function arguments/return values|Caller| +|x12–17|a2–7|Function arguments|Caller| +|x18–27|s2–11|Saved registers|Callee| +|x28–31|t3–6|Temporaries|Caller| + +The saver column is referring to who should save a register to memory when calling a function. If its Caller saved, then if you want to keep the register's value you need to save it before you call a function. And if its Callee saved then you need to save it before you overwrite it, when your function is being called. Some examples of proper calling convention will be shown in future tutorials. + +`zero`, `gp` and `tp` don't have a saver because they are intended to stay the same across function calls. + +## Using the stack + +The stack pointer provides a way for functions to store data while letting called functions use registers or to store extra data that can't fit in registers. + +The general idea is that functions can move the pointer to reserve space in memory to write and then when the function is ready to return move the pointer back where it started. + +More precisely, the register `x2` / `sp` represents an available pointer aligned to at least a word boundary (more in some situations); this means that `sw zero, 0(sp)` would not write over any data in the stack. diff --git a/docs/Creating-Hello-World.md b/docs/Creating-Hello-World.md new file mode 100644 index 00000000..213f4892 --- /dev/null +++ b/docs/Creating-Hello-World.md @@ -0,0 +1,71 @@ + +The most basic hello world that could be made in C is something like: +```C +int main(){ + puts("Hello World!\n"); + return 0; +} +``` + +We don't have access to the C standard library, though, so let us change puts into a call to `write(fd,buffer,len)` +which directly maps to a Linux system call. Let us also lift the definition of the string into a global variable because assembly language doesn't allow strings as arguments. + +This leaves us with some directly translatable C code: +```C +char* str = "Hello World!\n" +int main(){ + write(1,str,13); + return 0; +} +``` + +The first line can be translated into assembly as: +```assembly +.data # Tell the assembler we are defining data not code +str: # Label this position in memory so it can be referred to in our code +.string "Hello World!\n" # Copy the string "Hello World!\n" into memory +``` + +To start defining the main function we will use the code: + +```assembly +.text # Tell the assembler that we are writing code (text) now +main: # Make a label to say where our program should start from +``` + +The body of main is a little harder to directly translate because you have to set up each of the arguments to the system call one by one. In total `write(1,str,13)` will take 5 instructions: + +```assembly +li a0, 1 # li means to Load Immediate and we want to load the value 1 into register a0 +la a1, str # la is similar to li, but works for loading addresses +li a2, 13 # like the first line, but with 13. This is the final argument to the system call +li a7, 64 # a7 is what determines which system call we are calling and we what to call write (64) +ecall # actually issue the call +``` + +`return 0` is going to need to be changed a little before we can translate it. To exit cleanly we will need to use the `exit` system call. + +```assembly +li a0, 0 # The exit code we will be returning is 0 +li a7, 93 # Again we need to indicate what system call we are making and this time we are calling exit(93) +ecall +``` +Putting all of those snippets together we get the code: +```assembly +.data # Tell the assembler we are defining data not code +str: # Label this position in memory so it can be referred to in our code + .string "Hello World!\n" # Copy the string "Hello World!\n" into memory + +.text # Tell the assembler that we are writing code (text) now +main: # Make a label to say where our program should start from + + li a0, 1 # li means to Load Immediate and we want to load the value 1 into register a0 + la a1, str # la is similar to li, but works for loading addresses + li a2, 13 # like the first line, but with 13. This is the final argument to the system call + li a7, 64 # a7 is what determines which system call we are calling and we what to call write (64) + ecall # actually issue the call + + li a0, 0 # The exit code we will be returning is 0 + li a7, 93 # Again we need to indicate what system call we are making and this time we are calling exit(93) + ecall +``` \ No newline at end of file diff --git a/docs/Differences-Between-32-and-64-bit-Modes.md b/docs/Differences-Between-32-and-64-bit-Modes.md new file mode 100644 index 00000000..273dba72 --- /dev/null +++ b/docs/Differences-Between-32-and-64-bit-Modes.md @@ -0,0 +1,52 @@ +Unfortunately, 64 bit RISC-V is not directly compatible with 32 bit RISC-V. The semantics of `add`, and other arithmetic instructions change to work on the whole 64 bit registers rather than just the 32 bits that the 32 bit version would operate on. The "w" suffix instructions, mimic the behavior of the 32 bit versions of instructions and sign extend the top 32 bits. + +Shifts are also a little different. Shifts consider 1 more bit to determine how far to shift. This allows them to shift an extra 32 bits. Immediate shifts allow encoding an additional bit as well. + +### 64 bit only instructions + +| Example Usage | Description | +|---------------|-------------| +|addiw t1,t2,-100|Addition immediate: set t1 to (t2 plus signed 12-bit immediate) using only the lower 32 bits| +|addw t1,t2,t3|Addition: set t1 to (t2 plus t3) using only the lower 32 bits| +|divuw t1,t2,t3|Division: set t1 to the result of t2/t3 using unsigned division limited to 32 bits| +|divw t1,t2,t3|Division: set t1 to the result of t2/t3 using only the lower 32 bits| +|fcvt.d.l f1, t1, dyn|Convert double from long: Assigns the value of t1 to f1| +|fcvt.d.lu f1, t1, dyn|Convert double from unsigned long: Assigns the value of t1 to f1| +|fcvt.l.d t1, f1, dyn|Convert 64 bit integer from double: Assigns the value of f1 (rounded) to t1| +|fcvt.l.s t1, f1, dyn|Convert 64 bit integer from float: Assigns the value of f1 (rounded) to t1| +|fcvt.lu.d t1, f1, dyn|Convert unsigned 64 bit integer from double: Assigns the value of f1 (rounded) to t1| +|fcvt.lu.s t1, f1, dyn|Convert unsigned 64 bit integer from float: Assigns the value of f1 (rounded) to t1| +|fcvt.s.l f1, t1, dyn|Convert float from long: Assigns the value of t1 to f1| +|fcvt.s.lu f1, t1, dyn|Convert float from unsigned long: Assigns the value of t1 to f1| +|fmv.d.x f1, t1|Move float: move bits representing a double from an 64 bit integer register| +|fmv.x.d t1, f1|Move double: move bits representing a double to an 64 bit integer register| +|ld t1, -100(t2)|Set t1 to contents of effective memory double word address| +|lwu t1, -100(t2)|Set t1 to contents of effective memory word address without sign-extension| +|mulw t1,t2,t3|Multiplication: set t1 to the lower 32 bits of t2*t3 using only the lower 32 bits of the input| +|remuw t1,t2,t3|Remainder: set t1 to the remainder of t2/t3 using unsigned division limited to 32 bits| +|remw t1,t2,t3|Remainder: set t1 to the remainder of t2/t3 using only the lower 32 bits| +|sd t1, -100(t2)|Store double word : Store contents of t1 into effective memory double word address| +|slli t1,t2,33|Shift left logical : Set t1 to result of shifting t2 left by number of bits specified by immediate| +|slliw t1,t2,10|Shift left logical (32 bit): Set t1 to result of shifting t2 left by number of bits specified by immediate| +|sllw t1,t2,t3|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| +|srai t1,t2,33|Shift right arithmetic : Set t1 to result of sign-extended shifting t2 right by number of bits specified by immediate| +|sraiw t1,t2,10|Shift right arithmetic (32 bit): Set t1 to result of sign-extended shifting t2 right by number of bits specified by immediate| +|sraw t1,t2,t3|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| +|srli t1,t2,33|Shift right logical : Set t1 to result of shifting t2 right by number of bits specified by immediate| +|srliw t1,t2,10|Shift right logical (32 bit): Set t1 to result of shifting t2 right by number of bits specified by immediate| +|srlw t1,t2,t3|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| +|subw t1,t2,t3|Subtraction: set t1 to (t2 minus t3) using only the lower 32 bits| + +### 64 bit only psuedo-instructions: + +| Example Usage | Description | +|---------------|-------------| +|fcvt.d.l f1, t1 |Convert double from signed 64 bit integer: Assigns the value of t1 to f1| +|fcvt.d.lu f1, t1 |Convert double from unsigned 64 bit integer: Assigns the value of t1 to f1| +|fcvt.l.d t1, f1 |Convert signed 64 bit integer from double: Assigns the value of f1 (rounded) to t1| +|fcvt.l.s t1, f1 |Convert signed 64 bit integer from float: Assigns the value of f1 (rounded) to t1| +|fcvt.lu.d t1, f1 |Convert unsigned 64 bit integer from double: Assigns the value of f1 (rounded) to t1| +|fcvt.lu.s t1, f1 |Convert unsigned 64 bit integer from float: Assigns the value of f1 (rounded) to t1| +|fcvt.s.l f1, t1 |Convert float from signed 64 bit integer: Assigns the value of t1 to f1| +|fcvt.s.lu f1, t1 |Convert float from unsigned 64 bit integer: Assigns the value of t1 to f1| +|li t1,1000000000000000 |Load Immediate : Set t1 to 64-bit immediate| \ No newline at end of file diff --git a/docs/Environment-Calls.md b/docs/Environment-Calls.md new file mode 100644 index 00000000..5f6227a8 --- /dev/null +++ b/docs/Environment-Calls.md @@ -0,0 +1,98 @@ +RARS currently supports system calls that MARS originally supported and system calls compatible with Linux tooling (gcc, [riscv-pk](https://github.com/riscv/riscv-pk), etc.). Only a handful are compatible with other education simulators, but it is configurable, so the call numbers can be changed with a simple config file. + +They can be called by loading the call number into `a7`, any other arguments into `a0`-`a6` and calling `ecall`. Currently, when in 64 bit mode, only the lower 32 bits will be considered and outputs will be sign extended from 32 to 64 bits. The following exits the program with the code 42. + +```assembly +li a0, 42 +li a7, 93 +ecall +``` + +Note: all registers besides the output are guaranteed not to change. + +All supported system calls are shown below. + +| Name | Call Number (a7) | Description | Inputs | Outputs | +|------|------------------|-------------|--------|---------| +|PrintInt|1|Prints an integer|a0 = integer to print|N/A| +|PrintFloat|2|Prints a floating point number|fa0 = float to print|N/A| +|PrintDouble|3|Prints a double precision floating point number|fa0 = double to print|N/A| +|PrintString|4|Prints a null-terminated string to the console|a0 = the address of the string|N/A| +|ReadInt|5|Reads an int from input console|N/A|a0 = the int| +|ReadFloat|6|Reads a float from input console|N/A|fa0 = the float| +|ReadDouble|7|Reads a double from input console|N/A|fa0 = the double| +|ReadString|8|Reads a string from the console|a0 = address of input buffer
a1 = maximum number of characters to read|N/A| +|Sbrk|9|Allocate heap memory|a0 = amount of memory in bytes|a0 = address to the allocated block| +|Exit|10|Exits the program with code 0|N/A|N/A| +|PrintChar|11|Prints an ascii character|a0 = character to print (only lowest byte is considered)|N/A| +|ReadChar|12|Reads a character from input console|N/A|a0 = the character| +|GetCWD|17|Writes the path of the current working directory into a buffer|a0 = the buffer to write into
a1 = the length of the buffer|a0 = -1 if the path is longer than the buffer| +|Time|30|Get the current time (milliseconds since 1 January 1970)|N/A|a0 = low order 32 bits
a1=high order 32 bits| +|MidiOut|31|Outputs simulated MIDI tone to sound card (does not wait for sound to end).|See MIDI note below|N/A| +|Sleep|32|Set the current thread to sleep for a time (not precise)|a0 = time to sleep in milliseconds|N/A| +|MidiOutSync|33|Outputs simulated MIDI tone to sound card, then waits until the sound finishes playing.|See MIDI note below|N/A| +|PrintIntHex|34|Prints an integer (in hexdecimal format left-padded with zeroes)|a0 = integer to print|N/A| +|PrintIntBinary|35|Prints an integer (in binary format left-padded with zeroes) |a0 = integer to print|N/A| +|PrintIntUnsigned|36|Prints an integer (unsigned)|a0 = integer to print|N/A| +|RandSeed|40|Set seed for the underlying Java pseudorandom number generator|a0 = index of pseudorandom number generator
a1 = the seed|N/A| +|RandInt|41|Get a random integer|a0 = index of pseudorandom number generator|a0 = random integer| +|RandIntRange|42|Get a random bounded integer|a0 = index of pseudorandom number generator
a1 = upper bound for random number|a0 = uniformly selectect from [0,bound]| +|RandFloat|43|Get a random float|a0 = index of pseudorandom number generator|fa0 = uniformly randomly selected from from [0,1]| +|RandDouble|44|Get a random double from the range 0.0-1.0|a0 = index of pseudorandom number generator|fa0 = the next pseudorandom| +|ConfirmDialog|50|Service to display a message to user|a0 = address of null-terminated string that is the message to user|a0 = Yes (0), No (1), or Cancel(2)| +|InputDialogInt|51|N/A|N/A|N/A| +|InputDialogFloat|52|N/A|N/A|N/A| +|InputDialogDouble|53|N/A|N/A|N/A| +|InputDialogString|54|Service to display a message to a user and request a string input|a0 = address of null-terminated string that is the message to user
a1 = address of input buffer
a2 = maximum number of characters to read (including the terminating null)|a1 contains status value.
0: OK status. Buffer contains the input string.
-2: Cancel was chosen. No change to buffer.
-3: OK was chosen but no data had been input into field. No change to buffer.
-4: length of the input string exceeded the specified maximum. Buffer contains the maximum allowable input string terminated with null.| +|MessageDialog|55|Service to display a message to user|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:
0: error message
1: information message
2: warning message
3: question message
other: plain message|N/A| +|MessageDialogInt|56|Service to display a message followed by a int to user|a0 = address of null-terminated string that is the message to user
a1 = the int to display|N/A| +|Close|57|Close a file|a0 = the file descriptor to close|N/A| +|MessageDialogDouble|58|Service to display message followed by a double|a0 = address of null-terminated string that is the message to user
fa0 = the double|N/A| +|MessageDialogString|59|Service to display a message followed by a string to user|a0 = address of null-terminated string that is the message to user
a1 = address of the second string to display|N/A| +|MessageDialogFloat|60|Service to display a message followed by a float to user|a0 = address of null-terminated string that is the message to user
fa1 = the float to display|N/A| +|LSeek|62|Seek to a position in a file|a0 = the file descriptor
a1 = the offset for the base
a2 is the begining of the file (0), the current position (1), or the end of the file (2)}|a0 = the selected position from the beginning of the file or -1 is an error occurred| +|Read|63|Read from a file descriptor into a buffer|a0 = the file descriptor
a1 = address of the buffer
a2 = maximum length to read|a0 = the length read or -1 if error| +|Write|64|Write to a filedescriptor from a buffer|a0 = the file descriptor
a1 = the buffer address
a2 = the length to write|a0 = the number of characters written| +|Exit2|93|Exits the program with a code|a0 = the number to exit with|N/A| +|Open|1024|Opens a file from a path
Only supported flags (a1) are read-only (0), write-only (1) and write-append (9). write-only flag creates file if it does not exist, so it is technically write-create. write-append will start writing at end of existing file.|a0 = Null terminated string for the path
a1 = flags|a0 = the file descriptor or -1 if an error occurred| + +### Using MIDI output +These system services comes from MARS, 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
  • 62 = D
  • 63 = D# or Eb
  • 64 = E or Fb
  • 65 = E# or F
  • 66 = F# or Gb
  • 67 = G
  • 68 = G# or Ab
  • 69 = A
  • 70 = A# or Bb
  • 71 = B or Cb
  • 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-7Piano64-71Reed
    8-15Chromatic Percussion72-79Pipe
    16-23Organ80-87Synth Lead
    24-31Guitar88-95Synth Pad
    32-39Bass96-103Synth Effects
    40-47Strings104-111Ethnic
    48-55Ensemble112-119Percussion
    56-63Brass120-127Sound 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. + + diff --git a/docs/Fundamentals-of-RISC-V-Assembly.md b/docs/Fundamentals-of-RISC-V-Assembly.md new file mode 100644 index 00000000..6752dbae --- /dev/null +++ b/docs/Fundamentals-of-RISC-V-Assembly.md @@ -0,0 +1,130 @@ +The [first tutorial](Creating-Hello-World) doesn't explain enough of the core concepts needed to move on to more complicated programs. This page will dig deeper into RISC-V, and explain the fundamentals required for you to write your own "hello world" program. + +## Registers + +Registers are the variables of assembly. However, unlike in high level languages, we have a limited number (32) of registers. Registers are used to store information that will be needed quickly; long-lasting values generally are stored in RAM. + +In RISC-V, we can only directly manipulate registers. If you want to modify something in RAM you must load it into a register, modify it, and then write the value back to RAM. + +There are two ways to refer to each register: its proper name `x0`, `x1`, ..., `x31` and its [ABI](https://en.wikipedia.org/wiki/Application_binary_interface) name `zero`, `ra`, ..., `t6`. The ABI names refer to things like "return address" and "temporary 6". For more information about that, check out the [calling convention page](Calling-Convention). The only special register is `x0`/`zero`, which always has the value 0 and ignores being written to. + +In practice, you don't need to know about the proper names, you will be using the ABIs, and mostly the seven temporary registers `t0` to `t6`. + +If you find yourself hopelessly confused which register you have assigned to do what, you do have the ability to use `.eqv` directive to rename them. It is best to keep the original name as part of the new one: + +``` + .eqv t0_cats t0 + .eqv t1_dogs t1 + .eqv t2_mice t2 + + li t0_cats, 10 + li t1_dogs, 1 + li t2_mice, 100 +``` +We'll explain the `.eqv` directive in greater detail below. RARS will still understand the default names. Note there is currently no way to remove these definitions again. + + +## Instructions + +All code that is executed in a RISC-V processor comes in the form of instructions. Things like adding/multiplying two numbers, loading a value from RAM into a register, and issuing a system call are instructions. There is no fundamental concept of a function, loop, or if statement; all of those concepts are made using instructions. + +For example, the following executes the `addi` instruction 10 times. + +``` + li t0, 10 # t0 = 10 + li t1, 0 # t1 = 0 +loop: + addi t1, t1, 1 # add t1 += 1 + bne t0, t1, loop # Branch to the loop label if t0 == t1 +``` + +A full list of instructions supported by RARS is available [here](Supported-Instructions). + +### Immediates and Labels + +Nearly every instruction involves a register, but some also use a number or label. This is often referred to as an immediate. Immediates are stored as part of the instruction in the form of a number. When assembling the program, the label's address - the instruction's address is used as the immediate. + +The use of labels like `loop` in the small program above is not strictly necessary. You could write the instructions directly doing that, but it would be very time-consuming to write. + +_Note: RARS doesn't actually support hardcoding offset immediates, but it would work in GCC._ + +### Pseudo-Instructions + +RISC-V is a [RISC](https://en.wikipedia.org/wiki/Reduced_instruction_set_computer) instruction set, and therefore tries to minimize instructions. So there isn't an instruction dedicated to loading a value into a register, or to move a value from one register to another. However, many instructions that might be nice to have can be made using other instructions. We call these instructions (that aren't in the instruction set, but can be directly replace with real instructions) pseudo-instructions. + +For example, `li t0, 10` can be translated into `addi t0, zero, 10`. + +## Assembler Directives + +Assembler directives are a way of telling the assembler what to put in the executable besides instructions. It allows you to set up memory for when your program starts running. All directives start with a `.` like `.data` or `.macro`. + +I'll explain the most common ones, but a full list of the directives is available [here](Assembler-Directives). + +### Equivalents + +It is considered bad form to use actual numbers in the code, because any change to these "magic numbers" requires going through the program line-by-line to find them. Instead, we can use the `.eqv` directive to define human-readable constants at the beginning. + +``` + .eqv N_CATS 10 + + li t0, N_CATS +``` + +Any change can now be handled in one place. + +_Note: Other assemblers use `.equ` instead of `.eqv`._ + +### Text vs Data + +Easily the most important directives are `.text` and `.data`; these tell the assembler where to store the instructions or data that follow. The `.text` section stores instructions and the `.data` section stores (global) variables we will be using and starts with them initialized. + +Because code and data become bytes before a program starts, we need to tell the assembler in what section to put code. Both directives can also be used to tell the assembler exactly where to put data/code in the section. This can be done by appending an address after the directive like `.text 0x400010`, but this is generally not necessary. + +There are also subtle differences between the `.data` and `.text` sections that can be disabled in RARS by enabling self-modifying code. More about that can be found in [a later explanation](Sections-and-Permissions). + +### Initializing data + +Generally, there are four types of data that can be set up after a `.data` directive: integers, floating point numbers, strings, and allocated space for the program to fill later. + +Integers can be initialized with `.byte`, `.half`, and `.word` for 1 byte, 2 byte, and 4 byte integers, respectively. They can also store multiple comma-separated values. + +For example: +``` +.data +big: + .word 0x1000000 +medium: + .half 0x1000 +smalls: + .byte 0x10, 90, -10 +``` + +Floating point numbers can be initialized with `.float`. The number is stored as the closest IEEE 754 floating point number. `NaN` and `Inf` can be used to represent "Not a Number" and Infinity, respectively. Like with integers, it can store multiple values. + +For example: +``` +.float 90, NaN, -Inf, -1.337, 1e10 +``` + +There are two flavors of strings: those automatically ending with a NULL (0) byte, and those that are not. `.asciz` or `.string` is used for the former, and `.ascii` is used for the latter. Despite what the names would imply, the directives actually store the strings encoded as [UTF-8](https://en.wikipedia.org/wiki/UTF-8) characters. However, because UTF-8 is backward-compatible with [ASCII](https://en.wikipedia.org/wiki/ASCII), the correct characters/strings are stored. + +For example, the following all put the same string into memory: +``` +ASCIZ: +.asciz "Hello" +ASCII: +.ascii "Hello\0" +BYTES: +.byte 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0 +. +``` + +Finally, the last way to set up memory is to allocate space only filled with zeroes. The main way to do this is using the `.space` directive. `.space 10` will allocate ten bytes of space. The other way is to use `.extern`; it allows multiple files to define the same variable without duplicating it. This would generally be used in a file that is included using `.include` so that multiple files including the same file don't duplicate it in memory. + +``` +.extern LABEL, 90 # allocates 90 bytes that can be referenced by LABEL +``` + + + + diff --git a/docs/Memory-Configurations.md b/docs/Memory-Configurations.md new file mode 100644 index 00000000..eea937fe --- /dev/null +++ b/docs/Memory-Configurations.md @@ -0,0 +1,29 @@ +The new method for configuring memory is not super easy or intuitive so at least some explanation is needed. If you have any trouble, please make an issue so I can know what people might be getting stuck on. + +A default config would look like: + +``` +name = Default +ident = Default_ +.text = 0x400000-0x10000000 +.data = 0x10000000-0x10040000 +heap = 0x10040000-0x30000000 +stack = 0x60000000-0x80000000 +mmio = 0xffff0000-0xffffffff +gp_offset = 0x8000 +extern_offset = 0x10000 +``` + +`name` and `ident` may often be the same value, but there is a difference. `name` is what will be shown in the memory configuration list whereas `ident` is a unique identifier used internally. + +`.text` and `.data` should be mostly self-explanatory. The values are the ranges RARS will assign to the `.text` and `.data` sections. Notably, RARS will not always have enough memory to have the entire range be valid; instead RARS will shorten the range by moving the upper bound down until it fits. + +`heap` determines the range that the `sbrk` system call can allocate. + +`stack` determines the area that the stack is valid in. Unlike other sections, the stack is shrunk by moving the lower bound up. + +The `mmio` range generally should be small enough to directly fit into RARS memory. + +`gp_offset` is the offset from the lower bound of `.data` that will be assigned to the `gp` register on normal program execution. + +`extern_offset` is the amount of space that should be reserved for `.extern` variables in the start of `.data`. This could presumably be left off in the future, but for now the assembler of RARS requires some constant. The default start position when the `.data` directive is used is the lower bound of the `.data` range + `extern_offset` \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..251a34f4 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,23 @@ + +Welcome to the RARS documentation! This is a lightly edited version of the Wiki on the old (inactive) [RARS GitHub repository](https://github.com/TheThirdOne/rars). + +There isn't much here at the moment, but it should start filling up. If you had trouble figuring out how to do something in RARS and want to help others, add a tutorial showing how to do it. + +Tips on using RARS: + +- [Using the Command Line](Using-the-Command-Line.md) + +General Documentation: + +- [Assembler Directives](Assembler-Directives.md) +- [Environment Calls](Environment-Calls.md) +- [Supported Instructions](Supported-Instructions.md) + +Other Documentation (Uncategorized): + +- [Calling Convention](Calling-Convention.md) +- [Creating Hello World](Creating-Hello-World.md) +- [Differences Between 32 and 64 Bit Modes](Differences-Between-32-and-64-Bit-Modes.md) +- [Fundamentals of RISC-V Assembly](Fundamentals-of-RISC-V-Assembly.md) +- [Memory Configurations](Memory-Configurations.md) +- [Sections and Permissions](Sections-and-Permissions.md) diff --git a/docs/Sections-and-Permissions.md b/docs/Sections-and-Permissions.md new file mode 100644 index 00000000..4f64ebd7 --- /dev/null +++ b/docs/Sections-and-Permissions.md @@ -0,0 +1,3 @@ +This is an unfinished page intended towards the end of the small tutorial series I am working on. + +It is intended to explain what sections are, Read Write and Execute Permissions, Layout of sections in memory, and probably touch on what an ELF or other executable file is. \ No newline at end of file diff --git a/docs/Supported-Instructions.md b/docs/Supported-Instructions.md new file mode 100644 index 00000000..ac6982ba --- /dev/null +++ b/docs/Supported-Instructions.md @@ -0,0 +1,276 @@ +RARS supports all instruction in RV32imfd. Additionally, it supports `uret` from extension n, and `wfi` from the privileged specification. rv64 mode instructions are not included here and instead have their own page [[Differences between 32 bit and 64 bit modes]]. Fence instructions do nothing as RARS doesn't have a cache. + +If there is an instruction from RV32imfd missing, that's a bug. Additionally, all pseudo-instructions listed in the spec should also be supported. + +| Example Usage | Description | +|---------------|-------------| +|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,10000|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 t1, f1, 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 t1, f1, 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, dyn|Fused Multiply Add (64 bit): Assigns f2*f3+f4 to f1| +|fmadd.s f1, f2, f3, f4, dyn|Fused 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, dyn|Fused Multiply Subatract: Assigns f2*f3-f4 to f1| +|fmsub.s f1, f2, f3, f4, dyn|Fused 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, dyn|Fused Negate Multiply Add (64 bit): Assigns -(f2*f3+f4) to f1| +|fnmadd.s f1, f2, f3, f4, dyn|Fused Negate Multiply Add: Assigns -(f2*f3+f4) to f1| +|fnmsub.d f1, f2, f3, f4, dyn|Fused Negated Multiply Subatract: Assigns -(f2*f3-f4) to f1| +|fnmsub.s f1, f2, f3, f4, dyn|Fused 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,10000|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| + +Supported pseudo-instructions: + +| Example Usage | Description | +|---------------|-------------| +|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: 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 f1 >= f2, set t1 to 1, else set t1 to 0| +|fge.s t1, f2, f3 |Floating Greater Than or Equal: if f1 >= f2, set t1 to 1, else set t1 to 0| +|fgt.d t1, f2, f3 |Floating Greater Than (64 bit): if f1 > f2, set t1 to 1, else set t1 to 0| +|fgt.s t1, f2, f3 |Floating Greater Than: if f1 > f2, set t1 to 1, else set t1 to 0| +|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| +|flwd f1,%lo(label)(t2) |Load from Address| +|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 Subtract (64 bit): Assigns f2*f3-f4 to f1| +|fmsub.s f1, f2, f3, f4 |Fused Multiply Subtract: 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 t1, f1 |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 Subtract (64 bit): Assigns -(f2*f3-f4) to f1| +|fnmsub.s f1, f2, f3, f4 |Fused Negated Multiply Subtract: 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| +|fsflagsi 100 |Write FP exception flags, immediate| +|fsflagsi t1, 100 |Swap FP exception flags, immediate| +|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| +|fsrmi 100 |Write FP rounding mode, immediate| +|fsrmi t1, 100 |Swap FP rounding mode, immediate| +|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| +|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,(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,(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| +|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| + diff --git a/docs/Using-the-Command-Line.md b/docs/Using-the-Command-Line.md new file mode 100644 index 00000000..459099fa --- /dev/null +++ b/docs/Using-the-Command-Line.md @@ -0,0 +1,97 @@ +RARS can be somewhat confusing from the command line, so this page will be used to provide examples of how to use it. + +## Common commands and their use + +|Command|Usage| +|-------|-----| +|`h`|Display the help text| +|` ...`| Assembles and simulates the files | +|` pa Some program arguments`| Simulates with `argc` and `argv` passed to main| +|`a `| Only assembles the program | +|`a dump .text ` | Assembles `` and dumps the .text section into ``| + +Note on interpreting the table: *Command* should be preceded by `java -jar rars.jar ` and `` should be replaced with paths to files. + + +## Breakdown of the options and how to use them + +The following is a breakdown of the three categories of options that RARS supports. + +Several of the options correspond to setting that can be used from the GUI. These include: + - `ascii` : display memory or register contents interpreted as ASCII codes. + - `dec` : display memory or register contents in decimal. + - `hex` : display memory or register contents in hexadecimal (default) + - `mc ` : set memory configuration. See full help text for explanation + - `np` : use of pseudo instructions and formats not permitted + - `p` : Project mode - assemble all files in the same directory as given file + - `sm` : start execution at statement with global label main, if defined + - `smc` : Self Modifying Code - Program can write and branch to either text or data segment + - `rv64`: Enables 64 bit assembly mode + - `pa` : enable program arguments, see explanation and examples below + +Many of the other options determine what information is logged when an `ebreak` is executed or program execution stops. These include: + - `b` : brief - do not display register/memory address along with contents + - `ic` : display count of basic instructions 'executed' + - `x` : where is number or name (e.g. 5, t3, f10) of register to display + - `` : where is name (e.g. t3, f10) of register to display + - `-` : memory address range from to to display. More details in full help text. + +A few options are specific to the command line and change how the program is run and the format of the output. + - `a` : assemble only, do not simulate + - `ae` : terminate RARS with integer exit code if an assembly error occurs. + - `d` : display RARS debugging statements + - `dump ` -- memory dump of specified memory segment. See full help test for more information + - `h`: display the help + - `me`: display RARS messages to standard err instead of standard out. + - `nc`: no not display copyright notice + - `se`: terminate RARS with integer exit code if a simulation (run) error occurs. + +Of those `ae`,`me`,`nc`, and `se` are only really useful if you are using it in an automated setting. + +## Full help text from `h` option +``` +Usage: Rars [options] filename [additional filenames] + Valid options (not case sensitive, separate by spaces) are: + a -- assemble only, do not simulate + ae -- terminate RARS with integer exit code if an assemble error occurs. + ascii -- display memory or register contents interpreted as ASCII codes. + b -- brief - do not display register/memory address along with contents + d -- display RARS debugging statements + dec -- display memory or register contents in decimal. + dump -- memory dump of specified memory segment + in specified format to specified file. Option may be repeated. + Dump occurs at the end of simulation unless 'a' option is used. + Segment and format are case-sensitive and possible values are: + = .text, .data + = SegmentWindow, HexText, AsciiText, HEX, Binary, BinaryText + h -- display this help. Use by itself with no filename. + hex -- display memory or register contents in hexadecimal (default) + ic -- display count of basic instructions 'executed' + mc -- set memory configuration. Argument is + case-sensitive and possible values are: Default for the default + 32-bit address space, CompactDataAtZero for a 32KB memory with + data segment at address 0, or CompactTextAtZero for a 32KB + memory with text segment at address 0. + me -- display RARS messages to standard err instead of standard out. + Can separate messages from program output using redirection + nc -- do not display copyright notice (for cleaner redirected/piped output). + np -- use of pseudo instructions and formats not permitted + p -- Project mode - assemble all files in the same directory as given file. + se -- terminate RARS with integer exit code if a simulation (run) error occurs. + sm -- start execution at statement with global label main, if defined + smc -- Self Modifying Code - Program can write and branch to either text or data segment + rv64 -- Enables 64 bit assembly and executables (Not fully compatible with rv32) + -- where is an integer maximum count of steps to simulate. + If 0, negative or not specified, there is no maximum. + x -- where is number or name (e.g. 5, t3, f10) of register whose + content to display at end of run. Option may be repeated. + -- where is name (e.g. t3, f10) of register whose + content to display at end of run. Option may be repeated. +- -- memory address range from to whose contents to + display at end of run. and may be hex or decimal, + must be on word boundary, <= . Option may be repeated. + pa -- Program Arguments follow in a space-separated list. This + option must be placed AFTER ALL FILE NAMES, because everything + that follows it is interpreted as a program argument to be + made available to the program at runtime. +``` \ No newline at end of file