Assembler-programmering |
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
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.
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.
Å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.
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
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).
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.
Den del av processorn som sköter beräkningar kallas ALU (Arithmetic Logic Unit).
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.
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.
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.
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.
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
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.
Under kursen kommer vi att använda simulatorn SPIM för att testa våra MIPS-assemblerprogram.
Ett första litet exempelprogam att köra i spim:
.text .globl main main: addi $t0, $zero, 3 addi $t1, $zero, 2 add $t2, $t0, $t1 jr $ra
.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
.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
.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
.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
.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
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.