Lösningsförslag till tentamen i IS1500 Datorteknik 2010-12-13 Obs! Dessa lösningsförlag kan vara felakktiga och/eller ofullständ Uppgift 1. Maskinartmetik a) Flyttalet X lagras som 0 1000 0011 000 1001 0000 0000 0000 0000. 0 1000 0011 000 1001 0000 0000 0000 0000.. Första nollan anger att talet är positivt. Exponentkoden är 1000 0011 vilket motsvarar +4 i excess-127-kod. 127+4=131 Lagrade mantissabitar är 000 1001 0000 0...0; vi måste lägga till "1," före och får då talet +1,000 1001 0000 0...0 två 2 4 = +10001,0010...0 två 2 0 = +17,125 b) Z = 8X får samma flyttalskod som X fast exponentkoden ska ökas med 3 eftersom Z är 2 upphöjt till 3 gånger större än X. Hela koden blir då 0 1000 0110 000 1001 0000 0000 0000 0000 c) Talet Y = 22,250 = +10110,01 två 2 0 ; Talet måste normaliseras så att det börjar med "1," och då får vi Y = +1,011001 två 2 4 Exponenten 4 lagras i excess-127-kod som 1000 0011, och "1," lagras inte, så hela talet blir 0 1000 0011 011 0010 0000 0000 0000 0000 0000. d) +22 = 0001 0110 två 22 får med negering: invertera alla bitar och addera 1 i minst signifikant position. Resultat: 1110 1010 två e) Exponenten 4 ska nu lagras i excess-1023-kod, som 1023 + 4 = 1027, vilket blir 100 0000 0011. De lagrade mantissabitarna ska ha ytterligare 29 nollor längst till höger. Svar: 0 100 0000 0011 0001 0010 000 0000 0000 0000 0...0 (där 0...0 står för 29 ytterligare nollor). Uppgift 2. Assemblerprogrammering för Nios II a) isbcdigit: # 4 bitar i r4 andi r4,r4,0xf # maska fram 4 LSBits cmplti r2,r4,10 # jämför med 10 ret # returhopp från subrutin Man kan såklart göra på andra sätt, t.ex. med villkorligt hopp. b) pointstovalid8bcd: # adress i r4 PUSH r31 # pusha returadressen innan call ldw r4,0(r4) # hämta värdet till r4 call isvalid8bcd # anropa anbefalld funktion POP r31 # återställ sparad returadress ret # returhopp från subrutin Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 1
c) antalvalid8bcd: # adress till vektor i r4, längd i r5 movi r2, 0 # returvärde för tom vektor beq r5,r0,klar # om tom vektor PUSH ra # för att kunna anropa subrutin PUSH r16 # för adress till vektor PUSH r17 # för antalsräknare PUSH r18 # för delresultat mov r16, r4 # kopiera vektoradress mov r17, r5 # kopiera antalsräknare mov r18, 0 # noll hittills loop: mov r4, r16 # kopiera adress till element call pointstovalid8bcd add r18,r18,r2 # räkna upp delresultat addi r16,r16,4 # räkna upp adress subi r17, r17, 1 # räkna ner antalsräknare bgt r17,r0,loop # fortsätt om det finns fler element mov r2, r18 # delresultatet är nu slutresultat POP r18 # återställ sparade register POP r17 POP r16 POP ra klar: ret # returhoppet Uppgift 3. In- och utmatning och avbrott a) ReceiveWord: movia r8,para0 # adress till parallellport ldwio r9,12(r8) # läs Edge Capture Register beq r9,r0,receiveword # om inget nytt, gör om stwio r0,12(r8) # nollställ Edge Capture Register ldwio r2,0(r8) # läs datain-registret ret # returhopp från subrutin b) ReceiveWordNonBlocking: movi r2,-1 # förbered returvärde för ena fallet movia r8,para0 # adress till parallellport ldwio r9,12(r8) # läs Edge Capture Register beq r9,r0,fin # om inget nytt, returnera med förberett värde stwio r0,12(r8) # nollställ Edge Capture Register ldwio r9,0(r8) # läs datain-registret stw r9,0(r4) # spara på angiven plats i minnet movi r2,0 # detta är andra fallet, byt returvärde fin: ret # returhopp från subrutin c) ReceiveInt: PUSHALL # spara alla register som subrutiner får ändra call ReceiveWord # läs indata mov r4,r2 # flytta returvärde till rätt register för inparameter call SaveWord # anropa anbefalld rutin POPALL # återställ sparade register eret # returhopp från avbrottsrutin, alltid med eret Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 2
Uppgift 4. Cacheminnen a) Tidslokalitet innebär att en och samma minnescell läses och/eller skrivs mer än en gång med kort tidsmellanrum. Det finns ingen tidslokalitet (re-use) för läsning/skrivning av data i programmet, eftersom varje element i varje array bara läses eller skrivs en gång och sen aldrig mer. Rumslokalitet innebär att minnesceller med näraliggande adresser läses och/eller skrivs med kort tidsmellanrum. Det finns rumslokalitet (locality) för data i programmet eftersom loopen går igenom alla element i arrayerna x, y och z i tur och ordning och elementen i respektive array är lagrade i rad intill varandra. Arrayelement med näraliggande nummer och följaktligen näraliggande adresser kommer refereras med kort tidsmellanrum. b) Cacheminnet har följande utseende. Cacheminnet har två set med vardera 16 ord. Varje set har 2 rader, och varje rad i ett set innehåller ett block på 8 ord. Avstånd mellan element i x och y med samma arrayindex är 16 ord, vilket är 1 gånger storleken på ett set i cacheminnet. Eftersom 1 är ett heltal, så placeras x[0] och y[0] på Avstånd mellan element i y och z med samma arrayindex är 16 ord, vilket är 1 gånger storleken på ett set i cacheminnet. Eftersom 1 är ett heltal, så placeras y[0] och z[0] på Avstånd mellan element i x och z med samma arrayindex är 32 ord, vilket är 2 gånger storleken på ett set i cacheminnet. Eftersom 2 är ett heltal så kommer x[0] och z[0] att placeras på Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7] som placeras på en viss rad i ena cachehalvan. Index-fältet i adressen anger vilken rad, och vi kan anta vänstra cachehalvan eftersom hela cacheminnet är tomt. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7] som placeras på samma rad som x[0], men i ett annat set. rad. Det blir högra cachehalvan, eftersom den vänstra halvan av raden är upptagen av x[0] till x[7]. Skrivning av z[0] ger miss; cacheminnet hämtar z[0] till z[7] som placeras på samma rad som x[0] och y[0]. Nu är det fullt på raden och utbytesalgoritmen LRU används. Blocket med x[0] till x[7] har legat oanvänt längst tid och byts ut mot z[0] till z[7]. Läsning av x[1] ger miss; cacheminnet hämtar x[0] till x[7] som placeras på samma rad som förra gången. Nu är det fullt på raden och utbytesalgoritmen LRU används. Blocket med y[0] till y[7] har legat oanvänt längst tid och byts ut mot x[0] till x[7]. Läsning av y[1] ger miss, LRU används och nu har blocket med z[0] till z[7] legat oanvänt längst tid, så det byts ut. Skrivning av z[1] ger miss, LRU byter ut blocket med x[0] till x[7]. Läsning av x[2] ger miss, LRU byter ut blocket med y[0] till y[7] och så vidare. Vi får inga träffar alls, och hitrate = 0% Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 3
c) Ändrad associativitet till direktmappad (associativitet = 1). Cacheminnet får följande utseende. Antalet rader fördubblas till 4. Det enda set som nu finns i cacheminnet är 32 ord stort. Avstånd mellan element i x och y med samma arrayindex är 16 ord, vilket är 0,5 gånger storleken på ett set i cacheminnet. Eftersom 0,5 inte är något heltal, så placeras x[0] och y[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i y och z med samma arrayindex är 16 ord, vilket är 0,5 gånger storleken på ett set i cacheminnet. Eftersom 0,5 inte är något heltal, så placeras y[0] och z[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i x och z med samma arrayindex är 32 ord, vilket är 1 gånger storleken på ett set i cacheminnet. Eftersom 1 är ett heltal, så placeras x[0] och z[0] på Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7] som placeras på en viss rad. Index-fältet i adressen anger vilken rad. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7] som placeras på en annan rad än x[0]. Skrivning av z[0] ger miss; cacheminnet hämtar z[0] till z[7] som placeras på samma rad som x[0]. Eftersom cacheminnet är direktmappat så betyder det att x[0] till x[7] tas bort från cacheminnet. Läsning av x[1] ger miss; x[0] x[7] hämtas igen och placeras på samma rad som förut. Då tas z[0] z[7] bort från cacheminnet. Skrivning av z[1] ger miss; cacheminnet hämtar om z[0] till z[7] som placeras på samma rad som förut. Eftersom cacheminnet är direktmappat så betyder det att x[0] till x[7] tas bort från cacheminnet. Läsning av y[2] ger träff. Skrivning av z[2] ger miss. Läsning av x[3] ger miss. Läsning av y[3] ger träff. Skrivning av z[3] ger miss. Läsning av x[4] ger miss. Läsning av y[4] ger träff. Skrivning av z[4] ger miss. Läsning av x[5] ger miss. Läsning av y[5] ger träff. Skrivning av z[5] ger miss. Läsning av x[6] ger miss. Läsning av y[6] ger träff. Skrivning av z[6] ger miss. Läsning av x[7] ger miss. Läsning av y[7] ger träff. Skrivning av z[7] ger miss. Läsning av x[8] ger miss, för nu är blocket slut. Cacheminnet hämtar in x[8] till x[15], som placeras på raden efter x[0] x[7]. På samma sätt ger läsning av y[8] miss, och cacheminnet hämtar y[8] y[15]. Skrivning av z[8] ger miss, och cacheminnet hämtar z[8] z[15] som ska in på samma rad som x[8] x[15]. Mönstret upprepas. Träffmönstret blir: MMM MTM MTM MTM MTM MTM MTM MTM, MMM MTM MTM... Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 4
Hitrate blir 7/24 30% Ändrad associativitet till 4-vägs associativitet. Cacheminnet får följande utseende. Antalet rader halveras, till 1 rad, så att cacheminnet är fullt associativt. Ett set i cacheminnet är nu 8 ord stort. Ett fullt associativt cacheminne behöver ingen analys av avstånd mellan olika minnesceller för att avgöra om det finns konflikter. Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7] som placeras i något set. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7] som placeras i något annat set, för det finns lediga platser. Skrivning av z[0] ger miss; cacheminnet hämtar z[0] z[7] som placeras i ett tredje set. Läsning av x[1] ger träff. Läsning av y[1] ger träff. Skrivning av z[1] ger träff. Läsning av x[2] ger träff. Läsning av y[2] ger träff. Skrivning av z[2] ger träff. Läsning av x[3] ger träff. Läsning av y[3] ger träff. Skrivning av z[3] ger träff. Läsning av x[4] ger träff. Läsning av y[4] ger träff. Skrivning av z[4] ger träff. Läsning av x[5] ger träff. Läsning av y[5] ger träff. Skrivning av z[5] ger träff. Läsning av x[6] ger träff. Läsning av y[6] ger träff. Skrivning av z[6] ger träff. Läsning av x[7] ger träff. Läsning av y[7] ger träff. Skrivning av z[7] ger träff. Läsning av x[8] ger miss, för nu är blocket slut. Cacheminnet hämtar in x[8] till x[15], som placeras i cacheminnets sista lediga set. Läsning av y[8] ger miss, och nu är cacheminnet fullt. LRU används; det är blocket med x[0] x[7] som har legat oanvänt längst tid. Det blocket tas bort från cacheminnet, men det gör ju inte så mycket eftersom funktionen calc inte kommer att använda de matriselementen mer. Skrivningen av z[8] ger miss; cacheminnet hämtar z[8] z[15]. LRU används, och det är blocket med y[0] y[7] som har legat oanvänt längst tid. Det blocket tas bort från cacheminnet, men det gör inget för de matriselementen behövs inte längre. Läsning av x[9] ger träff. Läsning av y[9] ger träff. Skrivning av z[9] ger träff, och så vidare. Vi får följande träffmönster: MMM TTT TTT TTT TTT TTT TTT TTT MMM TTT TTT TTT TTT TTT TTT TTT Hittrate = 42/48 = 21/24 = 7/8 = 87,5% d) Halverad storlek till 16 ord. Cacheminnet får följande utseende. Antalet rader halveras, till 1 rad, så att cacheminnet är fullt associativt. Ett set i cacheminnet är nu 8 ord stort. Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7]. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7]. Skrivning av z[0] ger miss; LRU används och nu har blocket med x[0] till x[7] legat oanvänt längst tid och byts ut. Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 5
Läsning av x[1] ger miss; LRU används och nu har blocket med y[0] till y[7] legat oanvänt längst tid och byts ut. Läsning av y[1] ger miss, LRU byter ut blocket med z[0] till z[7] och så vidare. Vi får inga träffar alls, och hitrate = 0% Fördubblad cacheminnesstorlek. Cacheminnet får följande utseende. Antalet rader fördubblas till 4 rader. Ett set i cacheminnet är nu 32 ord stort. Avstånd mellan element i x och y med samma arrayindex är 16 ord, vilket är 0,5 gånger storleken på ett set i cacheminnet. Eftersom 0,5 inte är något heltal, så placeras x[0] och y[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i y och z med samma arrayindex är 16 ord, vilket är 0,5 gånger storleken på ett set i cacheminnet. Eftersom 0,5 inte är något heltal, så placeras y[0] och z[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i x och z med samma arrayindex är 32 ord, vilket är 1 gånger storleken på ett set i cacheminnet. Eftersom 1 är ett heltal, så placeras x[0] och z[0] på Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7] som placeras på en viss rad i ena cachehalvan. Index-fältet i adressen anger vilken rad, och vi kan anta vänstra cachehalvan eftersom hela cacheminnet är tomt. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7] som placeras på en annan rad än x[0]. Skrivning av z[0] ger miss; cacheminnet hämtar z[0] till z[7] som placeras på samma rad som x[0]. men i ett annat set. Det blir högra cachehalvan, eftersom den vänstra halvan av raden är upptagen av x[0] till x[7]. Läsning av x[1] ger träff. Läsning av y[1] ger träff. Skrivning av z[1] ger träff. Läsning av x[2] ger träff. Läsning av y[2] ger träff. Skrivning av z[2] ger träff. Läsning av x[3] ger träff. Läsning av y[3] ger träff. Skrivning av z[3] ger träff. Läsning av x[4] ger träff. Läsning av y[4] ger träff. Skrivning av z[4] ger träff. Läsning av x[5] ger träff. Läsning av y[5] ger träff. Skrivning av z[5] ger träff. Läsning av x[6] ger träff. Läsning av y[6] ger träff. Skrivning av z[6] ger träff. Läsning av x[7] ger träff. Läsning av y[7] ger träff. Skrivning av z[7] ger träff. Läsning av x[8] ger miss, för nu är blocket slut. Cacheminnet hämtar in x[8] till x[15], som placeras på raden efter x[0] x[7]. På samma sätt ger läsning av y[8] miss, och cacheminnet hämtar y[8] y[15]. Skrivning av z[8] ger miss, och cacheminnet hämtar z[8] z[15]. Därefter får vi träffar tills loopen är slut. Träffmönstret blir: MMM TTT TTT TTT TTT TTT TTT TTT, MMM TTT TTT... Hitrate blir 21/24 = 7/8 = 87,5% Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 6
e) Ändrad arraystorlek från 16 ord till 24 ord. Samma figur som i uppgift b) Cacheminnet har två set med vardera 16 ord. Varje set har 2 rader, och varje rad i ett set innehåller ett block på 8 ord. Avstånd mellan element i x och y med samma arrayindex är 24 ord, vilket är 1,5 gånger storleken på ett set i cacheminnet. Eftersom 1,5 inte är något heltal, så placeras x[0] och y[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i y och z med samma arrayindex är 16 ord, vilket är 1,5 gånger storleken på ett set i cacheminnet. Eftersom 1,5 inte är något heltal, så placeras y[0] och z[0] på olika rader i cacheminnet, och så vidare för andra arrayindex. Avstånd mellan element i x och z med samma arrayindex är 48 ord, vilket är 3 gånger storleken på ett set i cacheminnet. Eftersom 3 är ett heltal, så placeras x[0] och z[0] på Läsning av x[0] ger miss; cacheminnet hämtar x[0] till x[7] som placeras på en viss rad i ena cachehalvan. Index-fältet i adressen anger vilken rad, och vi kan anta vänstra cachehalvan eftersom hela cacheminnet är tomt. Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[7] som placeras på en annan rad än x[0]. Skrivning av z[0] ger miss; cacheminnet hämtar z[0] till z[7] som placeras på samma rad som x[0]. men i ett annat set. Det blir högra cachehalvan, eftersom den vänstra halvan av raden är upptagen av x[0] till x[7]. Läsning av x[1] ger träff. Läsning av y[1] ger träff. Skrivning av z[1] ger träff. Läsning av x[2] ger träff. Läsning av y[2] ger träff. Skrivning av z[2] ger träff. Läsning av x[3] ger träff. Läsning av y[3] ger träff. Skrivning av z[3] ger träff. Läsning av x[4] ger träff. Läsning av y[4] ger träff. Skrivning av z[4] ger träff. Läsning av x[5] ger träff. Läsning av y[5] ger träff. Skrivning av z[5] ger träff. Läsning av x[6] ger träff. Läsning av y[6] ger träff. Skrivning av z[6] ger träff. Läsning av x[7] ger träff. Läsning av y[7] ger träff. Skrivning av z[7] ger träff. Läsning av x[8] ger miss, för nu är blocket slut. Cacheminnet hämtar in x[8] till x[15], som placeras på raden efter x[0] x[7]. På samma sätt ger läsning av y[8] miss, och cacheminnet hämtar y[8] y[15]. Skrivning av z[8] ger miss, och cacheminnet hämtar z[8] z[15]. Därefter får vi träffar tills loopen är slut. Läsning av z[0] ger miss; cacheminnet hämtar z[0] till z[1] som placeras på en viss rad i vänstra cachehalvan. (Index-fältet i adressen anger vilken rad) Läsning av y[0] ger miss; cacheminnet hämtar y[0] till y[1] som placeras på samma rad som z[0] till z[1] men nu i högra cacherutan som ju är tom. Mönstret från sekvensen här ovanför upprepar sig inom varje block MMM TTT TTT TTT TTT TTT TTT TTT MMM TTT TTT TTT TTT TTT TTT TTT Hit rate = 21/24 = 7/8 = 87,5%. Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 7
Uppgift 5. Processorkonstruktion a) Delayed branch kräver en NOP efter rad nummer 2, 5, 9, 10. Det går att ta bort NOP efter rad 2; om load-instruktionen hämtar ett extra värde efter sista varvet så ändras bara r8, och eftersom XXL är en subrutin så får r8 ändras. Avancerat: I många datorer är det förbjudet att utföra en load-instruktion i onödan; det kan medföra exception om adressen är utanför ett tillåtet område. I den här kursen bortser vi från det problemet. Det går inte att ta bort NOP efter rad 5, för då görs felaktig uppdatering av r2. Det går inte att ta bort NOP efter rad 9, för då görs felaktig retur. Det går inte att ta bort NOP efter rad 10 för då ändras returvärdet i r2 innan returen. b) Utan data forwarding så krävs 2 NOP eller andra oberoende instruktioner mellan RAW-beroende instruktioner. Mellan rad 3 och 4 behövs 2 NOP på grund av RAW-beroende (r8). Mellan rad 4 och 5 behövs 2 NOP på grund av RAW-beroende (r9). c) Med 5-stegs pipeline så behövs en oberoende instruktion efter Load. Rad 3 är en Load-instruktion, och rad 4 använder det laddade värdet. En NOP måste in mellan rad 3 och rad 4. d) Samma som i a) och c) (både Branch Delay och Load Delay). e) Beräkning av CPI (Clocks Per Instruction). Summering av antalet cykler och instruktioner. Före loop: rad 1 tar 1 cykel för 1 instruktion. Loop, varv 1: rad 2 4 och 7-8 tar 1 cykel var, men rad 9 tar 2 cykler. Om vi antar att r9 = 0 på rad 5 så tar ett varv i loopen 9 cykler för 7 instruktioner. Loop, varv 2: som varv 1, 9 cykler till och 8 instruktioner till. Loop, varv 3: som varv 2, 9 cykler till och 8 instruktioner till. Efter sista varvet utförs rad 2 som hoppar till FOO, vilket tar 2 cykler för 1 instruktion. Vid läget FOO utförs ret vilket tar 2 cykler för 1 instruktion. Totalsumma 32 cykler för 24 instruktioner. CPI = 32/24 = 4/3 1,3 Uppgift 6. Trådar och synkronisering a) Time-slice nr 1: P1 producerar 5 heltal och lägger 5 i Fifo-kön. Time-slice nr 2: P2 producerar 6 heltal och lägger 5 av dem i Fifo-kön. Sen är det fullt. Orsaken till att P2 producerar 6 heltal är att produktionen sker innan tråden försöker lägga in heltalet i Fifo-kön. Det sista heltalet kommer inte in i kön, utan ligger kvar i en lokal variabel i P2. Time-slice nr 3: C3 konsumerar 10 heltal 5 från P1 och 5 från P2. Time-slice nr 4: C4 konsumerar inga heltal, Fifo-kön är tom. Time-slice nr 5: P1 producerar 5 heltal och lägger 5 i Fifo-kön. Time-slice nr 6: P2 har kvar ett heltal som inte fick plats i Fifo-kön förra gången. Nu lägger P2 det talet i Fifo-kön. Därefter producerar P2 5 heltal till och lägger 4 av dem i Fifo-kön. Sen är det fullt. Time-slice nr 7: C3 konsumerar 10 heltal 5 från P1 och 5 från P2. Time-slice nr 8: C4 konsumerar inga heltal, Fifo-kön är tom. Summering: 10 heltal från P1 och 10 heltal från P2 konsumerades på 8 time-slices. Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 8
b) Time-slice nr 1: P2 producerar 10 heltal och lägger 10 i Fifo-kön. Time-slice nr 2: P1 producerar 1 heltal men Fifo-kön är full, så talet får inte plats utan ligger kvar i en lokal variabel i P1. Time-slice nr 3: C4 konsumerar 10 heltal varav 10 från P2 och inget från P1. Time-slice nr 4: C3 konsumerar inga heltal, Fifo-kön är tom. Time-slice nr 5: P2 producerar och skriver 10 tal. Time-slice nr 6: P1 har ett heltal i en lokal variabel; det heltalet producerades under time-slice nr 2. Men kön är full, så talet ligger kvar i den lokala variabeln i P1. Time-slice nr 7: C4 konsumerar 10 heltal, alla från P2. Time-slice nr 8: C3 konsumerar inga heltal, Fifo-kön är tom. Summering: 20 heltal från P2 och inga heltal från P1 konsumerades på 8 timeslices. c) Fel i funktionen Signal. Rad 3: det ska inte finnas någon if-sats, ökning av semaforvariabeln ska alltid utföras när Signal anropats. d) Förklaring av funktionen Wait. Rad 10 funktionsdefinition, rad 11 inleder ett block. Rad 12 hindrar avbrott och därmed trådbyte så att kontroll och ändring av semaforvariabelns värde görs odelbart (atomärt). Om trådbyts skulle tillåtas emellan, så skulle någon annan tråd kunna ändra semafor-variabeln mellan testet och uppdateringen. Rad 13 utför testet och börjar en vänteloop om semaforens värde är noll eller negativt. Rad 14 inleder ett block. Rad 15 tillåter avbrott och alltså trådbyte medan den aktuella tråden väntar. Det är viktigt, annars stoppas timer-avbrotten på obestämd tid. Rad 16 ger upp resten av den aktuella trådens time-slice, eftersom tråden väntar på att en annan tråd ska anropa Signal. Det kommer att hända fortare om den väntande lämnar över processorn till nästa tråd. Rad 17 förbjuder avbrott igen. Detta kommer att utföras när körningen återvänder till den väntande tråden. Avbrott måste vara förbjudna eftersom tråden nu än en gång ska utföra testet (och kanske uppdateringen) av semafor-variabeln. Rad 18 avslutar det block som utgör kroppen (body) i while-loopen. Rad 19 utförs om semafor-variabeln var positiv: variabelns värde minskas med 1. Rad 20 tillåter avbrott igen (förklaringar här ovanför). Rad 21 avslutar funktionen. e) Vid läget L1: ea innehåller sparat programräknarvärde för den avbrutna tråden, och sp innehåller stackpekaren för samma tråd. Som exempel antar vi att det är tråd 0. Mellan lägena L1 och L2 körs oslab_internal_threadswitch, som sparar stackpekaren för den avbrutna tråden (tråd 0 i vårt exempel) och returnerar stackpekaren för nästa tråd som ska köras (tråd 1 i vårt exempel). Efter retur från funktionen så används den nya stackpekaren för att hämta upp registervärden, inklusive innehåll i ea, från trådens stack (stacken för tråd 1). Vid läget L2: ea innehåller sparat programräknarvärde för den tråd som ska startas, och sp innehåller stackpekaren för samma tråd (tråd 1 i vårt exempel). Lösningsförslag IS1500 (och IS1200/2G1518) 2010-12-13 sida 9