Assembler-programmering

Innehåll

På föreläsningen tisdagen 1/11 gick vi igenom grunderna i MIPS-Assembler.

En stor del av tiden ägnades åt att gå igenom olika exempelprogram. Till detta användes MIPS-simulatorn SPIM. Genom att koppla en videoprojektor till min laptop kunde sedan de närvarande följa simuleringen.

Emellanåt kommenterade och ritade jag på svarta tavlan. Dessa kommentarer och figurer finns tyvär inte med i dessa anteckningar.

~ Karl

Introduktion och översikt

En dator gör exakt vad den blir tillsagd att göra, den följer slaviskt en anvisning steg för steg. Om vi vill summera tal måste talen lagras någonstans. Sedan skriver vi en exakt beskrivning av hur datorn skall sumera talen. Talen ges sedan som indata och datorn följer beskrivningen eller programmet för att summera talen. Resultatet kallar vi utdata.

Karl ritar på svarta tavlan
Figur 1: Datorn som en låda.

Stored-Program Concept: Både data och program lagras på samma sätt - i minnet som tal.

En dator ser alltså på världen som om den endast består av tal. Vad betyder talen? Det finns ingen givet svar. Det är upp till varje program att själv bestämma vad talen representerar. Vi skall senare se hur bokstäver kan representeras som olika tal med ASCII-kod men tillsvidare kommer vi att nöja oss med att tolka talen som possitiva heltal.

För att kunna skriva ett program måste vi känna till det "språk" eller maskinkod som datorn talar. En dator gör en sak i taget enligt de instruktioner vi ger den.

Instruction set: Den uppsättning av instruktioner som en viss arkitektur förstår.

Eftersom både data och program utgörs av tal utgörs varje instruktion/maskinkod av ett tal. Datorer älskar att bolla med tal men människor tycker bättre om att samtala eller skriva i ord och meningar.

Att programera en dator genom att skiva in en lång radda tal verkar inte allt för tilltalande. Det vore betydligt trevligare om vi kunde göra det i mer "naturligt" i form av ord och meningar.

Assembler: Ett sätt att uttrycka maskinkoden för en dators processor på ett sätt som människor kan läsa och skriva. Maskinkod består av tal (lagrade som mönster av ettor och nollor) och är i allmänhet svår för programmerare att använda. Assembler tillåter att bitmönstren istället skrivs med bokstäver och siffror, så kallade mnemoniska symboler, vilket väsentligen underlättar programmerarens arbete. Vidare tillhandahåller assembler möjligheten att använda symboliska namn för minnesadresser.

Det finns assemblerspråk definierade för alla processorer, men varje typ av processor har sin egen assembler. Detta gör att det i allmänhet inte går att använda ett assemblerprogram skrivet för en processor på en annan typ av processor. För att göra det möjligt att flytta program mellan olika processortyper används istället ett högnivåspråk. Exempel på högnivåspråk är C, C++, Pascal.

En kompilator för ett högnivåspråk översätter programkod skriven i språket till processorspecifik maskinkod och genom att använda olika kompilatorer kan samma högnivåkod användas för olika processortyper.

Program skrivna i assembler översätts med en assemblator till maskinkod.

I den här kursen kommer vi att studera MIPS-processorn och MIPS-assembler.

Bakgrund

År 1981 startade John L. Hennessy (författare till kursboken) ett projekt på Stanford University som resulterade i den första MIPS-processorn.

Bakgrunden till projektet var att ta fram en snabbare processor genom att effektivisera användande av så kalladde pipelines. Namnet MIPS står för Microprocessor without Interlocked Pipeline Stages.

MIPS-processor används bland annat i Nintendo 64, Sony PlayStation och Sony PlayStation 2.

Ett första exempel

Hur adderar MIPS-processorn två tal? Om vi har två "variabler" $t0 och $t1 som vi vill addera så att resultatet tilldelas "variabeln $t2 kan vi tänka oss att skriva ner detta som:

	    add $t2, $t0, $t1	    # $t2 = $t0 + $t1	
	  
Uttrycket ovan är faktiskt ett exempel på hur riktig MIPS-assembler ser ut! Först anges vad vi vill göra (add), sedan vart resultatet skall hamna ($t2), sedan följer operanderna ($t0, $t1). För att skriva kommentarer i koden används tecknet #. En kommentar startar vid #-tecknet och slutar när raden slutar.

Datalagring

På den gamla goda tiden lagrades data på hålkort. Sedan kom olika typer av magnetiska band och idag använder vi roterande skivor med magnetiska skikt (hårddiskar).

Karl ritar på svarta tavlan
Figur 2: Olika sätt att lagra data på.

Ju längre bort från processorn (CPU) ju långsammare går överföringen mellan processor och lagringsmedia. Samtidigt är oftast kapacitet på de lagringsenheter som befinner sig längre bort ofta större än de som ligger nära processorn. Det omvända gäller för priset. RAM-minne är dyrt i jämförelse med disk.

Storleken har betydelse: En grunläggande princip för hårdvara är att litet är samma sak som snabbt. En annan sida av samma mynt är att snabbt ofta är lika med dyrt. En konsekvens av detta är att man får snåla med små, snabba och dyra saker och se till att använda dessa på bästa sätt.

Närmast processorn finns ett fåtal men mycket snabba platser att lagra data på. Varje plats kan lagra ett tal om 32 bitar - ett så kallat ord om fyra bytes. Dessa platser kallas register.

Aritmetisk Logisk Enhet

Den del av processorn som sköter beräkningar kallas ALU (Arithmetic Logic Unit).

Karl ritar på svarta tavlan
Figur 3: Aritmetisk Logisk Enhet, Minne och register

I MIPS är det så att alla operander måste finnas i något register. Likaså hamnar resultatet av en operation i något register.

Minnet

I MIPS finns det endast 32 stycken register. Eftersom alla operander måste finnas i något register blir vi mycket begränsade om vi endast använder oss av register.

Karl ritar på svarta tavlan
Figur 4: Minnet - varje plats har en egen adress.
  • RAM-minnet är en utmärkt plats att lagra större mängder av data på.
  • Minnet lagrar tal av storleken fyra bytes (32 bitar).
  • Varje minnesplats har en egen adress.

Notera att vi än så länge endast studerar data av typen heltal. Ett heltal lagras som ett ord om fyra bytes, därför är skillnaden mellan två intilliggande addresser 4.

Från minnet till register

För att flytta heltal från minnet till ett register använder vi instruktionen lw (Load Word)

Syntaxen för lw ser lite speciell ut:

	  lw $t1, 0($t0)	# Hämta det som finns på address $t0
			        # i minnet och lagra i register $t1.
	

Från register till minnet

För att flytta heltal från ett register till en plats i minnet minnet använder vi instruktionen sw (Sore Word)

Syntaxen för sw ser ut så här:

	  sw $t1, 0($t0)	# Lagra det som finns i register $t1 
				# på address $t0 i minnet
	

Konstanter

Ibland är det praktiskt att använda konstanter. Antag att vi vill addera konstanten 33 till innehållet i registret $t0 och spara resultatet tillbaka till register $t0. I MIPS ser det ut så här:

	    addi $t0, $t0, 33    # $t0 = $t0 + 33
	  

Notera likheten med add-instruktionen som vi bekantat oss med tidigare. Genom att lägga till i anger vi att vi vill addera med hjälp av en immediate (omedelbar, direkt) konstant.

Learning by doing

Under kursen kommer vi att använda simulatorn SPIM för att testa våra MIPS-assemblerprogram.

Ett första exempel

Ett första litet exempelprogam att köra i spim:

simple_add.s

	.text
        .globl main

main: 
	addi	$t0, $zero, 3
	addi	$t1, $zero, 2

	add	$t2, $t0, $t1
	
	jr	$ra

Lagra till och från minnet, etiketter mm

to_and_from_memory.s

	.data

x:	.word 5
y:	.word 3
z:	.space 4
	
	.text
        .globl main

main:
	la	$t0, x
	lw	$t0, 0($t0)

	lw	$t1, y

	add	$t2, $t0, $t1

	la	$t3, z
	sw	$t2, 0($t3)

	sw	$t2, 4($t3)


	addi	$t2, $t2, 2

	sw	$t2, z
	
	jr	$ra
Figur 5: Minnesrymden för ett MIPS-program är uppdelat i tre segment.

Utskrift av strängar och heltal

I SPIM finns det möjlighet att skriva ut till skärm (console).

hello.s

        .data
str:    .asciiz "Hello world, your lucky number is: "  
        
	.text
        .globl main

main:   
	li      $v0, 4		# system call code for print_str
        la      $a0, str	# address of string to print
        syscall         
	
	li	$v0, 1		# system call code for print_int
	addi	$a0, $zero, 44	# integer to print
	syscall
	
	jr	$ra

Loopar

loop.s

        .text
        .globl main

main:   add	$t0, $zero, $zero  # loop-counter

        addi	$s1 ,$zero, 5

	# Loopa fem gånger (från 0 till 4):	

	
loop:   beq $t0, $s1, done
		    
	add	$a0, $zero, $t0		# integer to print
        li	$v0, 1			# system call code for print_int    (NOTE li)

	syscall
	
        addi $t0, $t0, 1
	
        j loop
	
done:   jr $ra

If-Then-Else

if_then_else.s

        .data
str_then:    .asciiz "lika!"
str_else:    .asciiz "olika!"  	

	.text
        .globl main

main:   li	$t0, 15
	addi	$t1, $zero, 15

	# if ($t0 == $t1) then print "lika" else print "olika"

	
if:	bne $t0, $t1, else
	
then:
	li      $v0, 4                  # system call for print_str	

	la	$a0, str_then
	syscall
	j done
else:		
	li      $v0, 4                  # system call for print_str	

	la	$a0, str_else
	syscall
	j done
	
done:		
	jr $ra

Arrayer

array.s

	.data

NL:	.asciiz "\n"
	
size:	.word 10
array:	.word 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

        .text
        .globl main

main:   add	$t0, $zero, $zero	# array index i

        lw	$t2, size

	# loopa genom arrayen


	la	$t1, array		# pekare p till array  A

	
loop:   beq	$t0, $t2, done
		    
	lw	$a0, 0($t1)		# integer to print A[i]
        li	$v0, 1			# system call code for print_int

	syscall

	# ny rad

	
	li      $v0, 4                  # system call for print_str
        la      $a0, NL			# address of string to print

	syscall
	
        addi	$t0, $t0, 1		# i++
	addi	$t1, $t1, 4		# p += 4

	
        j	loop
	
done:   jr	$ra

Tyck till

För att vi tillsammans skall kunna göra kursen bättre är det viktigt att ni kommer med synpunkter. Självklart går det bra att framföra åsikter muntligt eller via e-post. Vill man vara anonym går det bra att använda formuläret nedan.

Som helhet tycker jag att föreläsningen var:

Bra
Hyffsad
Dålig

Kommentar:

Som helhet tycker jag att svårighetsgraden var:

Lätt
Lagom
Svår

Kommentar:

Vad kan göras bättre:

Något som du uppskattade speciellt:

Övrigt: