Stack och subrutiner Programmeringskonventionen Du ska förstå hur en instruktion behandlas i processorn Du ska känna till några fler instruktioner Du ska veta hur maskinkoden för ett program byggs upp Du ska börja programmera i på riktigt Du ska kunna skriva och anropa subrutiner i Större delen av materialet framtaget av :Jan Eric Larsson, Mats Brorsson och Mirec Novak IT-inst LTH Subrutin 1 Schemarubrik Subrutin 21 HUVUDPROGRAM Subrutin 2 Subrutin 22 Subrutin 3 Subrutin 31 Biblioteksrutin Programmet består av olika moduler - Huvudprogram (skrivet t.ex. i ) - Subrutiner (skrivna i eller ) - Subrutiner ur bibliotek Subrutiner i kan anropa subrutiner i och tvärtom Huvudprog Subrutin Subrutin Bibliotek Huvudprog Kompilator Kompilator Assembler Huvudprog Länkare M a s k i n k o d sqr: add t0, a0, zero# Flytta a0 (argument) till t0 mult t0, t0 # Multiplicera med sig själv mflo v0 # Hämta resultat från LO # v0 returvärde # LO får ej ändras 2 instr. Sqr kan anropas från på denna form. 1
Vi kan ha mera komplexa situationer.globl silly.ent silly.globl sillier.ent sillier Subrutiner som anropar andra subrutiner kan ställa till problem. VARFÖR?? Vi tittar på ett exempel: silly: addiu t0,zero,0 # Nollställ.. jal sillier # sparar #återhoppsadress i ra! sw v0,16(t0).end silly sillier: addi t1, t1, 7.end sillier OM EN RUTIN ANROPAR silly BLIR DET FEL - VARFÖR??? VAD BLIR ÅTERHOPPSADRESSEN NÄR VI HOPPAR TILLBAKA FRÅN silly? Subrutinen silly anropar en annan subrutin Genom jal-instruktionen i silly skrivs ra över Det är omöjligt att komma tillbaka till den rutin som anropade silly Höga adresser Stack Ledigt minne Dyn. data Stack. Växer nedåt Heap. Växer uppåt Register ra måste sparas undan någonstans. Men var? Låga adresser Statiska data Program Variabler etc..data Programkod.text Vi organiserar en del av minnet i form av en stack Stack är ett vedertaget begrepp Tekniken att använda en stack kan förklaras på många olika sätt Ex: Gruppuppgifter uppgifter hamnar på Mireks bord Uppg 1 Uppg 3 Uppg 2 Uppg 2 Uppg 4 Så fort en ny uppgift dyker upp, hamnar den överst i högen. När den är färdigrättad plockas den bort och den översta rättas. Pilen visar den uppgift jag jobbar med vid varje tidpunkt! 2
På samma sätt kan en subrutin lägga upp data på stacken. När subrutinen (uppgiften) är klar tas data bort från stacken igen. För att krångla till det: Stacken i vår dator växer nedåt (varför?) Det som motsvarar min pil (håller reda på stackens topp) är ett register: stackpekaren (sp). Vedertagna operationer för att hantera stacken: - push: Lägger ett element på stackens topp och ändrar pekaren till detta element - pop: Hämtar tillbaka ett element från stackens topp och sätter pekaren på elementet nämast under! "# REG NAMN $0 zero $1 at $2-$3 $3 v0-v1v1 $4-$7 $7 a0-a3a3 $8-$15 $15 t0-t7 t7 $16-$23 $23 s0-s7 s7 $24-$25 $25 t8-t9 t9 $26-$27 $27 k0-k1k1 $28 gp $29 sp $30 s8/fp $31 ra KOMMENTAR alltid noll används av n returvärden från funktioner argument till subrutiner temporära register får ej förändras av subrutiner temporära register används av operativsystem/avb avbrott global datapekare stackpekare ibland frame pointer return address: återhoppsadress Vilka element kan en subrutin vilja spara undan? - ra: registret med återhoppsadressen (om rutinen anropar något annat) - a0-a3: parametrarna till rutinen - s0-s8: subrutinregistren ska sparas om de används - lokala variabler - plats för argument till anropad subrutin $% $% Register som ska sparas läggs på stacken Utrymme för lokala variabler och plats för argument allokeras på stacken Totala allokerade mängden data ska vara jämnt delbart med 8 (konvention) Det allokerade utrymmet kallas AKTIVERINGSBLOK Sp Sp ra andra reg. Lokala var argument 3
( int stacktest(int x, int y) int a,b; a=level1(x); b=level1(y); return a+b; /* Subrutinen stacktest anropar level1 som i sin tur anropar level2 */ int level1(int x) int a; a=level2(x+4); return a+4; int level2(int x) return x+1; BÖRJA MED level2: - Anropar inga subrutiner - Behöver inte spara register om bara t0-t9 används.globl level2.ent level2 level2: addi t0,a0,1 or v0,t0,zero.end level2 int level1(int x) int a; a=level2(x+4); return a+4; int level2(int x) return x+1; ENKELT FORTSÄTT MED level1: PROLOG - allokera aktiveringsblocket! - level1 anropar en subrutin - level2 - Vi måste spara undan register som kan förändras: ra, s0, s1, a0 - Allokera plats för en lokal variabel samt plats för argument till subrutin som vi kallar på. - Totalt 6*4=24 bytes. Jämnt delbart med 8 - OK.globl level1.ent level1 level1: subu sp,sp,24 # Plats på stacken sw ra,20(sp) # Återhoppsadressen sw s0,16(sp) # Reg s0 sw s1,12(sp) # Reg s1 sw a0,8(sp) # Argumentet # Lokal variabel på 4(sp) # Plats för argument a0 på 0(sp) level1: SUBRUTINENS FUNKTION addi a0,a0,4# Räkna fram rätt argument jal level2 # Hoppa till subrutinen sw v0,4(sp) # Spara resultatet i lokal variabel lw s0,4(sp) # Hämta igen (lite dumt, men) addi s1,s0,4# Beräkna a+4 or v0,s1,zero # Flytta s1 till v0 level1: EPILOG (ÅTERSTÄLL REGISTER OH ÅTERLÄMNA AKTIVERINGSBLOK) # Och så är det dags att återställa lw a0,8(sp) lw s1,12(sp) lw s0,16(sp) lw ra,20(sp) addiu sp,sp,24 # Återställ stacken.end level1 % - Deklarera subrutinen - Allokera aktiveringsblocket - Spara register på stacken - Gör det som ska göras i rutinen - Återställ registren - Återlämna aktiveringsblock - Hoppa tillbaka 4
$ % Vanligaste direktiven: -.globl : Gör symbolen känd globalt -.ent : Start på subrutin -.text : Text- (program) segment (instruktioner) -.data : Datasegment -.end : Slut på subrutin $ % Assemblerdirektiv för data -.word X : Deklarera ett 32 bits ord, värde X -.byte Y : Samma fast byte -.float Z : Deklarera flyttal -.ascii sträng : Deklarera en sträng -.space M: Allokera utrymme M bytes MIPS är en RIS-processor med begränsat antal instruktioner, tex add, andi osv. Vissa instruktioner är enkla att programmera med, men finns ej i instruktionsuppsättningen. Ex neg, not och b. Assemblern översätter dessa till kombinationer av existerande instruktioner Ex: *neg rd,rs tar talet i rs och byter tecken, lägger i rd ersätts av sub rd,zero,rs *b label (ovillkorligt hopp) ersätts av beq zero,zero,label *not rd,rs inverterar bitarna i rs. Ersätts av nor rd,rs,zero 5