Some Samples of MIPS Assembly Language Lecture for CPSC 5155 Edward Bosworth, Ph.D. Computer Science Department Columbus State University Structure of This Lecture • This lecture will be built around a number of sample programs, written in assembly language and run under both SPIM and MARS. • We begin with some of the syntax for writing the programs and then illustrate. System Calls • Almost all programs, user and system, are written to be run on a machine with a fully functional operating system. • The operating system provides a number of services to other programs. • We shall use a number of SPIM system calls to facilitate input and output. Example of System Calls • .data str: asciiz “The answer is ” .text li $v0, 4 # Code for print string la $a0, str # Address of string syscall # Print the string li $v0, 1 # Code for print integer li $a0, 5 # Value to print syscall li $v0, 10 # Code for exit syscall The SPIM System Services SPIM Data Directives • Here are a number of directives used to set aside memory and initialize it. • .asciiz str stores the string in memory and null terminates it in the style of C or C++. • .space N allocates N bytes of storage for structures such as arrays, etc. • .word w1 … wn allocates and initializes n 32-bit words. Sample of Space Allocation • S1: .asciiz “A null-terminated string” This associates label S1 with the string. • C1: .space 80 This allocates 80 bytes, possibly for an array to hold 80 characters or 20 32-bit integers. • W1: .word 1, 2, 3 This initializes three integer values. Same as W1: .word 1 .word 2 .word 3 # At address W1 + 4 # At address W1 + 8 Conventions for Strings • Strings should be null terminated, as in the style of C and C++. • String constants are enclosed in double quotes “This is a string” • Special characters follow the C/C++ style newline \n tab \t quote \” Data Alignment • The assembler will normally align data as required by the data type. • The .align N directive aligns the next entry on an address that is a multiple of 2N. • Character data can be stored anywhere. • Byte data must be stored at an address that is a multiple of 2. Use .align 1 • Word data must be stored at an address that is a multiple of 4. Use .align 2 Kernel and User Mode • All modern computer systems restrict certain operations to the operating system, when it is operating in what is called “kernel mode”. • Example restricted operations: direct access to I/O devices, memory management, etc. • SPIM allows programs to be run in either user mode or kernel mode, as both modes are emulated on the host machine. SPIM Text & Data • .data <address> This stores subsequent items in the user data segment. • .kdata <address> This stores subsequent items in the kernel data segment. • .ktext <address> This stores subsequent items in the kernel text area, which holds instructions for kernel code. • .text <address> This stores subsequent items in the user text area, also for program instructions. The Optional Address • Each of these directives has an optional address, commonly not used. • One exception will be for an interrupt handler, which must be in the kernel at a fixed address. • savea0 savea1 .ktext 0x80000180 sw $a0, savea0 sw $a1, savea1 More code goes here. .kdata .word 0 .word 0 Compute Fibonacci Numbers • The Fibonacci sequence was introduced in 1202 by the Italian mathematician Leonardo of Pisa, known as Fibonacci. • The sequence is defined for N 0 as follows: F0 = 1, F1 = 1, and FN = FN-1 + FN-2 for N 2. • This is often used as an example of recursion. • We shall implement a non-recursive solution in the MIPS assembly language. The Basic Algorithm • The basic non-recursive algorithm for computing Fibonacci numbers uses a loop. • To compute F(N), do the following F1 = 1 F2 = 1 For (J = 2; J <= N; J++) F0 = F1 F1 = F2 F2 = F1 + F0 End For The Program (Page 1) • Here is the standard program header. # # # # # # # # # # # # # # # # Fibonacci This is a non-recursive program to calculate Fibonacci numbers, defined as follows: F(0) = 1, F(1) = 1, and F(N) = F(N - 1) + F(N - 2), for N >= 2. This runs under both MARS and SPIM. Written by Edward Bosworth, Ph.D. Associate Professor of Computer Science Columbus State University Columbus, GA 31907-5645 E-Mail [email protected] Date first written: July Date last revised: July 4, 2012 4, 2012. The Program (Page 2) • Here is the start of the code. Note that the program must have a globally visible label “main” at its start. main: # getN: # .text .globl main # Make this global li $v0, 4 # Print string la $a0, str0 # Address of title string syscall # Print the title li $v0, 4 # Print string la $a0, str1 # Address of input prompt syscall # Print the prompt. li $v0, 5 syscall # Read an integer # Return the value in $v0 The Program (Page 3) • The program is designed to loop: read user input and then to produce the result. The user input is found in $v0. • Non-positive input is a signal to terminate the program. A value of 1 disrupts the loop logic, so treat as special. # # blez $v0, done li $v1, 1 bgt $v0, $v1, doit # Do we stop? # F(1) is trivial. # Is N > 1? li $v0, 4 la $a0, str2 syscall j getN # # # # F(1) will disrupt the loop logic; just print answer Get more input. The Program (Page 4) • Here is the main computational loop. doit: li li # loop: addi move move addu blt # $t1, 1 $t2, 1 # Initialization # code for loop. $v1, $t0, $t1, $t2, # # # # # # $v1, 1 $t1 $t2 $t0, $t1 $v1, $v0, loop Bump $v1 New F(N - 2) New F(N - 1) New F(N), where $v1 contains N Are we done? The Program (Page 5) • Print out the result and get more input. move $a1, $v0 # Set aside the value of N li $v0, 4 la $a0, str3 # Print part of output string syscall # li $v0, 1 move $a0, $a1 # Get the value of N back syscall # Print N # li $v0, 4 la $a0, str4 # Print more of the string syscall # li $v0, 1 # F(N) is found in $t2 move $a0, $t2 # Print the result syscall # j getN # Get more input. The Program (Page 6) • Here is the closing code and data section. # Set up for a proper exit # done: li $v0, 4 # Print closing remark la $a0, finis # Address of closing remark syscall # li $v0, 10 # Call exit syscall # .data str0: .asciiz "Program to calculate a Fibonacci number“ str1: .asciiz "\nInput an integer: “ str2: .asciiz "\nF(1) is equal to 1.“ str3: .asciiz "\nThe Fibonacci number F(“ str4: .asciiz ") is equal to “ finis: .asciiz "\nAll done. \n" # \n is New line Comments on the Text • Figure B.1.4 on page B-7 contains the following. It has a problem. .text .align 2 .globl main main: • The MARS emulator does not allow the .align directive within the text area. When to Use Assembly Language • When should one prefer assembly language for writing programs? • Your instructor’s opinion is “almost never”. Compiler technology has advanced to a point that it is almost impossible to write code better or faster than that emitted by a modern compiler. • The only real use for assembly language is as a part of teaching computer science. Ancient History (PDP-9 FORTRAN) • The PDP-9 computer was fairly advanced for the early 1970’s. Your instructor programmed such a machine from 1971 to 1973. • The PDP-9 supported programs written in the FORTRAN programming language. The code produced was very slow. • The preferable method was to have the FORTRAN compiler emit assembly language code, edit that code, and assembly it. Labels: Local and Global • A label is called “global” or “external” if it can be referenced from another module. • The label may refer to a memory storage location or to a function or procedure. • The linker will resolve global references as a part of creating the executable image. If module A references a global in module B, the linker will make the correct connection. • Any label not declared as global is local, not available for reference by other modules. Rdata & Sdata • The sample code on page B-28 includes a data directive “.rdata”. • There is another directive, “.sdata”. • The MARS emulator does not recognize either directive. Use “.data” for user data and “.kdata” for kernel data. Various Jump Commands • The code near the bottom of page B-28 contains an error: 2 commands are confused. • j jump to the instruction at the target address • jr jump to the instruction at the address stored in the register. • The instruction should be j L1. • Also, the label should be L1, not $L1. The Assembly Temporary ($at) Register • Register $1 (called “$at”) is reserved for use by the assembler. • The SPIM emulator will raise an error if the code references the $at register. • To avoid this problem, when it is necessary to save the $at register, use the following. • .set noat # Allow use of $at move $k1, $at # Save it .set at # Assembler can now # use $at again. Exception Handler Code • The sample code on page B-36 contains the following code, which should be corrected .ktext 0x80000180 mov $k1, $at # Save the register $at • It should read as follows .ktext 0x80000180 .set noat move $k1, $at .set at Exceptions & Interrupts • Exceptions are events that disrupt the normal flow of program control and cause exception code to be executed. • Almost always, this is kernel code managed by the operating system. • Interrupts are exceptions from outside sources such as I/O devices. • The terminology is not uniformly applied. Examples of Exceptions • External interrupts include: Input the device has data ready to transfer Output the device is ready to receive data Timer the CPU timer raises an interrupt, used to run the CPU clock. • Exceptions include Arithmetic overflow – an arithmetic error Page fault – a reference has been made to an invalid virtual memory address. Exceptions: Control Unit Implications • For most external interrupts, the implications for the control unit are rather simple. • As the control unit begins the execution of each instruction, it tests a signal (often called “INT”) that indicates that there is an interrupt pending. • If there is a pending interrupt, the control unit branches unconditionally to kernel code. Handling Exceptions • Depending on the exception type, the control unit will begin execution of kernel code at one of two locations. 0x80000000 for undefined instructions 0x80000180 for other exceptions • For that reason, the standard exception handler begins as follows: .ktext 0x80000180 .set noat move $k1, $at # This at 0x80000180 .set at More Control Unit Implications • The handling of page fault exceptions shows one of the strengths of a load/store RISC. • Only register load and register store instructions can reference memory. • Neither load nor store change the computer state prior to referencing memory, so that any instruction can be restarted after a page fault. The Coprocessors CP0 for exceptions; CP1 for floating point Coprocessor Registers • Coprocessor 0 has a number of registers to control exceptions and manage memory. • Here are some of the registers used by SPIM $12 the interrupt mask and enable bits $13 the exception type and pending interrupt bits $14 EPC: address of the instruction that caused the exception. Accessing CP0 Registers • There are two instructions for accessing the registers in coprocessor 0: mfc0 and mtc0. • mfc0 $k0, $13 Copy CP0 cause register ($13) into CPU register $k0 ($26). • mtc0 $k1, $12 Load CP0 status register ($12) from CPU register $k1 ($27). Structure of Two Registers Sample Exception Handler Code • .ktext 0x80000180 sw $a0, savea0 sw $a1, savea1 sw $v0, savev0 # # Now determine the # mfc0 $t0, $13 srl $t0, $t0, andi $t0, $t0, # More code here to # Use static storage # It uses $a0 and $a1, so save # It also uses $v0 cause of the exception or interrupt # Move cause into $t0 2 # Shift to get exception code 0xF # Put the code into $t0 handle exception Returning from Exception • la $a0, L4 mtc0 # mtc0 mfc0 andi ori mtc0 # lw lw lw # eret $a0, $14 # L4 is the return address, # defined elsewhere in code # Put it into the EPC $0, $a1, $a1, $a1, $a1, # # # # # $13 $12 0xFFFD 0x1 $12 Clear the cause register Get the status register Clear the EXL bit Enable the interrupt and reset the status register $v0, savev0 $a1, savea1 $a0, savea0 # Return to new spot # Not a subroutine return.