The Daily Click ::. Forums ::. Non-Klik Coding Help ::. Assembler
 

Post Reply  Post Oekaki 
 

Posted By Message

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
25th June, 2004 at 05:13:58 -

Hi All
I've been studying for my NETS exam, and part of the exam will be writing several short MIPS programs, so I've been practicing and decided to share how to do this with you.

First, you need to download an interpreter (or compiler). A free Windows program called PCSPIM can be downloaded from http://www.cs.wisc.edu/~larus/SPIM/pcspim.zip and a free unix/Linux program called XSPIM can be downloaded from http://www.cs.wisc.edu/~larus/SPIM/spim.tar.gz

I'm not going to explain everything about assembler, that would take forever because you need to know about the various types of registers (hi/lo/pc/etc) and the commands arent exactly user friendly (what did you expect?) So I'm just going to explain a few things to get you started and show a few example apps. Below each code in assembler is the equivilent code in C++. I find this helps people to understand what's going on.

If you have any questions, feel free to ask. I wont be able to answer until after my exams, so you might need to wait a while for a response.

Example 1: Addition of 2 Hard Coded Integers
This example adds 2 integers (hard-coded) and prints the result. In this example, the integers are 2 and 3.

	.globl main

.text

main:
li $t0, 2
li $t1, 3
add $a0, $t0, $t1

li $v0, 1
syscall

li $v0, 10
syscall

# void main()
# {
# int t0 = 2;
# int t1 = 3;
#
# std::cout << t0 + t1;
# }
- The first two lines are required, and I'm not going to explain them here. Just take them as given until you want to start programming larger things.

- The line "main:" is simply the name of the main function (where the program starts).

- "li" ("Load Integer") sets a register to a value. On the first line, it loads the integer 2 into temporary register 0. The second loads the integer 3 into temporary register 1.

- "add" adds two integers and puts the result into another register. In this example, it adds the values from temporary registers 0 and 1 and puts the result in the argument register 0.

- The next line loads another integer into a variable register. $v0 in MIPS is reserved for system call arguments and return values. A system call takes an integer and interprets it as a function on the system. In this example, the number 1 causes the system to print the integer in the $a0 argument register (which was previously set to the addition of $t0 and $t1).

- "syscall" executes the above syscall code set within the $v0 register.

- Now the $v0 register is set to 10. This is the exit code used to make the program quit, and is finished off with the "syscall" command to execute it.

Other SYSCALL commands
print_int: 1
Print the integer in $a0

print_float: 2
Print the float in $f12

print_double: 3
Print the double in $f12

print_string: 4
Print the string in $a0

read_int: 5
Read an int into $v0

read_float: 6
Read a float into $f0

read_double: 7
Read a double into $f0

read_string: 8
Read a string into $a0 and string length into $a1

sbrk: 9
Use amount from $a0 and return address to $v0

exit: 10
No arguments or return values

Example 2: Addition of 2 Variable Integers
This example reads 2 integers from standard input, adds them, and prints the result of the equation.

	.data

input: .asciiz "Please enter an integer: "
plus: .asciiz " + "
equals: .asciiz " = "

.globl main
.text

main:
# PRINT INPUT LINE
la $a0, input
li $v0, 4
syscall

# GET INTEGER
li $v0, 5
syscall
move $s0, $v0

# PRINT INPUT LINE
la $a0, input
li $v0, 4
syscall

# GET INTEGER
li $v0, 5
syscall
move $s1, $v0

# ADD 2 INTEGERS
add $s2, $s0, $s1

# PRINT FIRST INTEGER
move $a0, $s0
li $v0, 1
syscall

# PRINT PLUS SIGN
la $a0, plus
li $v0, 4
syscall

# PRINT SECOND INTEGER
move $a0, $s1
li $v0, 1
syscall

# PRINT EQUALS SIGN
la $a0, equals
li $v0, 4
syscall

# PRINT RESULT
move $a0, $s2
li $v0, 1
syscall

# EXIT
li $v0, 10
syscall

# #define input "Please enter an integer: "
# #define plus " + "
# #define equals " = "
#
# void main()
# {
# std::cout << input;
# std::cin >> int s0; // Not valid C++, but closest representation
# std::cout << input;
# std::cin >> int s1; // Not valid C++, but closest representation
#
# int s2 = s0 + s1;
#
# std::cout << s0;
# std::cout << plus;
# std::cout << s1;
# std::cout << equals;
# std::cout << s2;
# }
- You will notice the addition of a "data" section, where you can pre-define strings and other types. Here 3 strings have been pre-defined to make the code easier.

- Using syscall 4 we are able to print a string loaded into $a0 using the "la" ("Load Asciiz") command.

- Because the return integer from a syscall is always placed in $v0, we need to move the values out of the register and into a safe register. In this example the safe registers $s0 and $s1 are used to store the values, and $s2 is used to store the addition. You do not need to store the addition value, it could be done as in the first example. It's up to your own preference. The "move" command moves the value of the second register into the first register.

Example 3: Addition of Variable Number of Integers
This can seem pretty complex to someone who hasnt used assembler before, but it works pretty much the same as any language, just it looks nastier

	.data

number: .asciiz "Number of integers: "
input: .asciiz "Please enter an integer: "
toolow: .asciiz "A number greater than 0 is required."
plus: .asciiz "Result: "

.globl main
.text

# $s0: Number of Loops
# $s1: Current Value

main:
# CLEAR REGISTERS
li $s0, 0
li $s1, 0

# PROMPT NUMBER OF LOOPS
la $a0, number
li $v0, 4
syscall

# INPUT NUMBER OF LOOPS
li $v0, 5
syscall
move $s0, $v0

# CHECK NUMBER GREATER THAN ZERO
bgtz $s0, loop

# PRINT ERROR
la $a0, toolow
li $v0, 4
syscall

# EXIT
li $v0, 10
syscall

loop:
# PROMPT INTEGER
la $a0, input
li $v0, 4
syscall

# INPUT INTEGER
li $v0, 5
syscall
add $s1, $s1, $v0

# DECRIMENT LOOP
sub $s0, $s0, 1

# CHECK LOOP REMAINING
bgtz $s0, loop

# PROMPT RESULT
la $a0, plus
li $v0, 4
syscall

# PRINT RESULT
move $a0, $s1
li $v0, 1
syscall

# EXIT
li $v0, 10
syscall

# #define number "Number of integers: "
# #define input "Please enter an integer: "
# #define toolow "A number greater than 0 is required."
# #define plus "Result: "
#
# int s0;
# int s1;
#
# void main()
# {
# s0 = 0;
# s1 = 0;
#
# std::cout << number;
# std::cin >> s0;
#
# if(s0 > 0)
# loop();
#
# std::cout << toolow;
# }
#
# void loop()
# {
# std::cout << input;
# s1 = s1 + std::cin; // Not valid C++, but closest representation
#
# s0 = s0 - 1;
#
# if(s0 > 0)
# loop();
#
# exit();
# }
- The safe variables need to be reset as they retain their values from one execution to the next (note: this is a feature of X/PCSPIM and does not occur in compiled applications). Thus, if you execute the program once and the resultant value in $v1 is 15, then run the program again with 2 integers of the values 3 and 4, then rather than the result being 7, it will be 22 (15 + 7). You do not need to reset the $v0 variable, but it was done here as a matter of completeness.

- The two safe variables were defined outside the main function in the C++ example. This is because the registers used by a program are available globally, whereas in C++ if they were defined inside the main function they would not be available in the loop function. As a result this isnt an exact translation of the MIPS code (in this one respect) but there is no other way to do it that translates any better.

- It works in exactly the same way except for the recursion. Note that in C++ you would do this using a loop (for example, for(int $i = 0; $i < $s0; $i++)).

- "sub" works in the same way as "add" by taking the subtraction of the third variable from the second and placing the result in the first.

- "bgtz" ("Branch If Greater Than Zero") will execute a function if a register is greater than 0. In this example it checks $s0 (which is set to the number of integers we would like to input). If this is equal to or less than zero in the first case, then an error is displayed and the program exits. If it's equal to zero in the second case, the resulting addition is printed and the program exits.

That's it! Like I said, these are extremely basic examples for people who are interested in learning a bit of assembly programming, and have some concept of registers and of the CPU. Post any questions, but please be patient for a reply while I'm doing my exams

Mike ¿

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
25th June, 2004 at 05:19:56 -

BTW I DONT recommend learning MIPS as your first language. If you have no experience in programming, start with C or C++ or something at a higher level than assembler before you try this stuff.

Oh, and in my C++ examples, I've excluded the fact that it sets return values to $v0 before you can assign them to another variable, because the C++ code isnt supposed to be useable, it's merely to show you what's going on at a higher level.

Mike ¿

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert

Tigerworks

Klik Legend

Registered
  15/01/2002
Points
  3882
25th June, 2004 at 09:06:03 -

Is MIPS different to assembly? As far as I know, assembly looks more like 'mov ax,bx' and none of that 'li $v0, 4' stuff.
Plus $t0, $t1 etc. are all invalid C++ names

 
- Tigerworks

Cazra

Crazy?

Registered
  24/07/2002
Points
  4472

Game of the Week WinnerVIP Member
25th June, 2004 at 09:52:34 -

Looks interesting. Is it faster than C++?

 
n/a

Joshtek

Administrator
The Archivist

Registered
  02/01/2002
Points
  3723

Game of the Week WinnerHas Donated, Thank You!Mr BallPicture Me This Round 50 Winner!
25th June, 2004 at 10:20:33 -

Snerlin of Neonair: Faster to code or faster to execute?

AFAIK In both cases it depends on what kinda programmer you are, I suppose. Generally Assembler is faster to run, as C++ is compiled into it anyway. But C++ is easier to code and debug.

 
:: Joshtek ::


Oreos? GO! OREOS!

Cazra

Crazy?

Registered
  24/07/2002
Points
  4472

Game of the Week WinnerVIP Member
25th June, 2004 at 11:59:56 -

Yes, I meant faster at runtime.

 
n/a

Kris

Possibly Insane

Registered
  17/05/2002
Points
  2017
25th June, 2004 at 12:03:36 -

it's usually faster because there tends to be less code (because you only include what you know you need, and not what a compiler adds) but on the other hand, most compilers (MSVC, probably MinGW) can optimise C++ code to produce Assembly you wouldn't have thought about writing (secret opcodes, MMX, whatever) which is faster

 
"Say you're hanging from a huge cliff at the top of mt. everest and a guy comes along and says he'll save you, and proceeds to throw religious pamphlets at you while simultaniously giving a sermon." - Dustin G

ChrisB

Crazy?

Registered
  16/08/2002
Points
  5457
25th June, 2004 at 12:38:00 -

And since nearly all computers are >600MHz now (and most over 1GHz), that's fast enough not to worry about going as low-level as Assembler.

 
n/a

Shen

Possibly Insane

Registered
  14/05/2002
Points
  3497
25th June, 2004 at 13:58:55 -

I have a 200Mhz and an Amstrad! I'm an exception!

Code looks good (and long and confusing). Good lucks.

Image Edited by the Author.

 
gone fishin'

ShadowCaster

Possibly Insane

Registered
  02/01/2002
Points
  2203
25th June, 2004 at 20:16:04 -

Assembler is faster, yes. Actually even though computers are quite fast there are some things you would program in assembler rather than another language. Games use assembler, for example, in parts that are quite intensive because every millisecond you can save adds up as there is a lot of repetition.

This is also the reason why DVDx is 10x faster at ripping and encoding DVDs than FlaskMPEG

Anyway, I've got to go in 15 minutes to my Law exam...

Mike

 
"Now I guess we're... 'Path-E-Tech Management'" -Dilbert
   

Post Reply



 



Advertisement

Worth A Click