Basic Instructions
Basic Instructions
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:
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.
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
###############################################################
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.
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
• 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:
• 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:
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
• 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
Macros:
Introduction to macros
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
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.
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.
Notes
Examples
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.
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 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
.macro done
li a7,10
syscall
.end_macro
.data
value: .word 13
.text
li a7, 1
lw a0, value
syscall
done
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