alu przechowywania cd
Transkrypt
alu przechowywania cd
Programowanie w asemblerze MIPSa (ciag ˛ dalszy) – p. 1/28 Przypomnienie Trzy poziomy jezyka: ˛ Jezyk ˛ maszynowy - kody operacji, wszystkie adresy, numery rejestrów i stałe zakodowane za pomoca˛ zer i jedynek; „Prawdziwy” asembler - kody operacji zapisywane symbolicznie (np. add, lw, ...), symboliczne nazwy rejestrów, adresy zastapione ˛ etykietami, stałe podawane dziesiatkowo; ˛ „Pseudoasembler” - mamy dodatkowe instrukcje nie majace ˛ bezpośrednich odpowiedników w jezyku ˛ maszynowym, tłumaczone na (krótka, ˛ czasem jednoelementowa!) ˛ sekwencje rozkazów maszynowych – p. 2/28 Typy rozkazów typu R (rejestrowe) – add $t0, $s1, $s2, mult $s1, $s2, slt $t3, $s2, $s1, ... 6 bitów kodu operacji trzy pola po 5 bitów – numery rejestrów 5 bitów shamt 6 bitów funkyjnych - dodatkowe szczegóły operacji typu I (z argumentem stałym) – addi $t0, $s1, -7, bne $s1, $s2, etyk, lw $s1, 4($s6), ... 6 bitów kodu operacji dwa pola po 5 bitów na numery rejestrów 16 bitów na stała˛ typu J (skoki) – j etyk – 6 bitów na kod operacji + 26 bitów na stała˛ – p. 3/28 Przykładowa organizacja pami˛eci $sp 7fff fffchex Stack Dynamic data $gp 1000 8000hex 1000 0000hex Static data Text pc 0040 0000hex Reserved 0 – p. 4/28 Przykład programu Przykład kompletnego programu (z duża˛ liczba˛ pseudorozkazów). Program został zaczerpniety ˛ z „Krotkiego wprowadzenia do SPIM-a” autorstwa Salah A. Almajdouba. Jego zadaniem jest dodanie elementów tablicy. – p. 5/28 .text .globl main main: lw $s0, Size li $s1, 0 li $s2, 0 li $t2, 4 loop: mul $t1, $s1, $t2 lw $s3, Nstart($t1) add $s2, $s2, $s3 addi $s1, $s1, 1 beq $s1, $s0, STOR j loop STOR: sw $s2, Result # # # # # # # # # czyta rozmiar tablicy indeks i s2 bedzie przechowywac sume t2 zawiera stala 4 t1 dostaje i * 4 s3 = N[i] sum = sum + N[i] i = i + 1 skaczemy do STOR na koniec # wynik zachowujemy w Result .data Nstart: .word 8, 25, -5, 55, 33, 12, -78 Size: .word 7 Result: .word 0 – p. 6/28 Tłumaczenie pseudoinstrukcji rozkaz tłumaczenie komentarz lw $s0, Size lui $1, 4097 load upper immediate lw $16, 28($1) li $s1, 4 ori $17, $0, 4 mul $t1, $s1, $t2 mult $s1, $t2 mflo $t1 lw $s3, Nstart($t1) logiczna alternatywa ze stała˛ SPIM traktuje mul jak rozkaz lui $1, 4097 addu $1, $1, $9 lw $19, 0($1) – p. 7/28 Wspomaganie definiowania procedur i funkcji int funkcja (int n){ int i,j; (...) return j; } main(){ int i,j; i=funkcja(i); ... j=funkcja(i+6); } po skoku do funkcji i jej wykonaniu musimy wrócić w odpowiednie miejsce do funkcji należy jakoś przekazać parametry i jakoś odebrać wyniki funkcja operuje na pewnych rejestrach, być może tych samych co program główny – p. 8/28 Wywołanie i powrót z funkcji Rejestr $ra służy do przechowywania adresu powrotu z funkcji. Rozkaz jal fun (jump and link) skacze do etykiety fun i zapamietuje ˛ aktualna˛ wartość licznika rozkazów pc. Powrót z funkcji zapewnia rozkaz jr $ra (jump register) - skocz pod adres zapisany w rejestrze (w ogólnym przypadku nie musi to być rejestr $ra. – p. 9/28 Argumenty i wyniki funkcji Zalecana jest nastepuj ˛ aca ˛ konwencja: Parametry sa˛ przekazywane w rejestrach $a0-$a3. Wyniki zwracane sa˛ w rejestrach $v0, $v1. Jeśli potrzebujemy wiekszej ˛ licbzy parametrów lub wyników możemy je przekazać za pomoca˛ stosu – p. 10/28 Zmienne lokalne W funkcji chcemy użyć $s1, $s2, $s3. Ich wartości moga˛ być jeszcze potrzebne w miejscu wywołania. Zapamietujemy ˛ ich aktualna˛ wartość na stosie, wykonujemy obliczenia i na koniec odtworzy wartości rejestrów. Fun: addi $sp, $sp, -12 sw $s1, 8($sp) sw $s2, 4($sp) sw $s3, 0($sp) (...) lw $s3, 0($sp) lw $s2, 4($sp) lw $s1, 8($sp) addi $sp, $sp, 12 jr $ra – p. 11/28 Argumenty, wyniki i wywołanie (...) addi $a0, $zero, 13 add $a1, $zero, $s2 jal Fun addi $s1, $zero, $v0 (...) Fun: addi $sp, $sp, -12 sw $s1, 8($sp) sw $s2, 4($sp) sw $s3, 0($sp) (...) lw $s3, 0($sp) lw $s2, 4($sp) lw $s1, 8($sp) addi $sp, $sp, 12 jr $ra – p. 12/28 Zagnieżdżone wywołania Problem: jeśli funkcja wywołuje koleja˛ funkcje˛ to tracimy adres powrotu Jeśli zatem zezwalamy na zagnieżdżone wywoływanie funkcji, to musimy odkładać adres powrotu ($ra) również na stosie. Na nastepnym ˛ slajdzie pokażemy przykład programu z funkcja˛ wyliczajac ˛ a˛ rekurencyjnie sume˛ liczb od 1 do n (n jest parametrem funkcji). – p. 13/28 sumton: rec: main: addi sw sw bne addi lw addi jr addi jal lw add lw addi jr addi jal addi li $sp,$sp,-8 $ra,4($sp) $a0,0($sp) $0,$a0,rec $v0,$zero,0 $ra,4($sp) # # # # # # # # $sp,$sp,8 # $ra # $a0,$a0,-1 # sumton # $a0,0($sp) # # $v0,$a0,$v0 # $ra,4($sp) # $sp,$sp,8 # $ra # $a0,$zero, 10 # sumton $a0, $v0, 0 # $v0, 1 # miejsce na dwa elementy na stosie odkladamy adres powrotu odkladamy argument skocz jesli arg rozny od 0 ustawiamy wynik na 0 odtworzenie adresu powrotu (w tym miejscu niekonieczne, ale tak jest bardziej elegancko...) zwalniamy dwa miejsca na stosie powrot z funkcji n := n-1 wywolanie z parametrem n-1 odtworzenie a0 w $v0 jest wynik sumton(n-1) $v0 = n + sumton(n-1) odtworzenie adresu powrotu zwalniamy dwa miejsca na stosie powrot z funkcji wywolanie z parametrem 10 wypisanie wyniku wypisanie wyniku – p. 14/28 Konwencja Strona wywoływana powinna zachowywać (odtworzyć przy powrocie): wartości rejestów $s0-$s7 wartość $sp adres powrotu $ra oczywiście nie wolno jej naruszyć pamieci ˛ powyżej $sp Nie musi sie˛ natomiast troszczyć o: wartości rejestów $t0-$t9 argumenty wywołania $a0-$a3 zawartość stosu poniżej $sp Jest to oczywiście tylko jedna z kilku możliwych konwencji. – p. 15/28 Uwaga o rejestrze $fp Podczas wywoływania funkcji na stosie odkładana jest tak zwana ramka wywołania (zawierajaca ˛ potrzebne wartości rejestrów, ewentualnie dodatkowe zmienne lokalne, argumenty wywołania czy zwracane wartości). My odwoływaliśmy sie˛ do wartości z ramki tylko za pomoca˛ rejestru $sp. Czasem używa sie˛ dodatkowo rejestru $fp (frame pointer) wskazujacego ˛ na najwyższy adres ramki. Wtedy w funkcji możemy np. modyfikować wartość $sp. Użycie $fp nie jest jednak konieczne. I tak np. kompilator GNU MIPS C używa $fp, ale kompilator C z MIPS/Silicon Graphics radzi sobie bez niego (i używa $fp jako kolejnego rejestru na zmienne) – p. 16/28 Operacje na napisach Dotychczas operowaliśmy na słowach (32-bitowych). Istnieje też dostep ˛ do poszczególnych bajtów. lb $s2, 3($s1) - load byte – rozkaz typu I kopiuje bajt o podanym adresie do 8 najmniej znaczacych ˛ bitów rejestru i wypełnia reszte˛ rejestru najbardziej znaczacym ˛ bitem bajtu (działa przy reprezentacji uzupełnień do 2, jeśli chcemy uzupełnić zerami używamy lbu – load byte unsigned). sb $s2, -2($s1) - store byte - kopiuje 8 najmniej znaczacych ˛ bitów rejestru. Zazwyczaj powyższe rozkazy używane do operacji na tekstach. – p. 17/28 .data .asciiz "Przykładowy napis." .text .globl main str1: strlen: loop EXIT main: addi : lb beq addi addi j jr $v0, $t0, $t0, $a0, $v0, loop $ra $zero, 0 0($a0) $zero, EXIT $a0, 1 $v0, 1 # -v0 - licznik (...) la $a0, str1 #pseudoroz., tlumaczony na lui i ori jal strlen (...) – p. 18/28 Rozkazy logiczne Zestaw operacji bitowych: and, or, nor, andi, ori, nor, xor, xori. W jaki sposób załadować do rejestru konkretny wzorzec bitowy, np. 0x5467a7ba? Nie może być jednej instrukcji - 32 bity! lui $t0, 0x5467 # niższe bity rejestru ustawia na 0. ori $t0, $t0, 0xa7ba Operacje przesuniecia: ˛ sll $t0, $t1, 7, analogicznie srl (dopełnia zerami) – rozkazy typu R - przesuwamy o maksymalnie 32 pozycje – przesuniecie ˛ kodowane w polu shamt, (jeden rejestr wolny). – p. 19/28 Mnożenie i dzielenie Mnożenie: mult $s0, $s1 – 64 bity wyniku. Wynik zapisywany w rejestrach HI i LO. Bardziej znaczac ˛ a˛ cz˛eśc wyniku można uzyskać rozkazem mfhi $t0, mniej znaczac ˛ a: ˛ mflo $t1. Także w wersji dla liczb bez znaku multu $s0, $s1 Istnieje pseudorozkaz mul r1, r2, r3 – zapisywana jest mniej znaczaca ˛ cz˛eśc wyniku Dzielenie: div $s2, $s3. Wynik w rejestrze LO, reszta z dzielenia w rejestrze HI. divu – dzielenie bez znaku – p. 20/28 Liczby zmiennopozycyjne Na liczbach zmiennopozycyjnych operuja˛ inne układy cyfrowe niż na całkowitych Mamy wiec ˛ inne rozkazy oraz specjalny zestaw rejestrów zmiennopozycyjnych - $f0, $f31. Jednostka zmiennopozycyjna nazywana jest koprocesorem 1. Rozkazy add.s, sub.s, add.d , sub.d (format R), odpowiednio single i double precision. Musza˛ być nowe rozkazy przesyłania - 32 rejestry - 5 bitów! – lwc1 $f1, 40($s1), swc1, ldc1, swc1 dla trybu double. Uwaga: rejestry sa˛ 32-bitowe, a format zmiennopozycyjny double 64-bitowy. W przypadku double wartości przechowywane sa˛ w parach rejestrów – odwołujemy sie˛ tylko do rejestrów parzystych. – p. 21/28 Liczby zmiennopozycyjne -cd. Przykład wczytywania liczb zmiennopozycyjnych: .data floats: .float -0.123, 3.14 .text (...) lwc1 $f0, floats lwc1 $f1, floats + 4 Dla liczb zmiennopozycyjnych sa˛ osobne rozkazy skoku: c.eq.s $f1, $f2 bc1t Label bc1f Label2 Jeśli rejestry $f1 i $f2 przechowuja˛ ta˛ sama˛ wartość to skocz pod Label, w przeciwnym wypadku pod Label2. (wynik porównania w specjalnym jednobitowym rejestrze niedostepnym ˛ dla programisty). – p. 22/28 Liczby zmiennopozycyjne -cd. Mnożenie: mul.s $f0, $f1, $f2 Dzielenie: div.s ..... – p. 23/28 Wejście-wyjście Prawdziwe procesory MIPS używaja tzw. wejścia-wyjścia odwzorowanego w pamieci ˛ (memory mapped IO), tzn. operuja˛ na IO za pomoca˛ lw, sw. Jest tylko jedno urzadzenie ˛ wejściowe (klawiatura) oraz jedno wyjściowe (monitor). Każde z nich ma dwa rejestry: 0xffff0000 - rejestr sterujacy ˛ wejściem (tylko dwa ostatnie bity maja˛ znaczenie) 0xffff0004 - rejestr danych wejściowych (przechowuje jeden bajt) 0xffff0008 - rejestr sterujacy ˛ wyjściem (tylko dwa ostatnie bity maja˛ znaczenie) 0xffff000c - rejestr danych wyjściowych – p. 24/28 Wejście-wyjście cd. Załóżmy, ze chcemy wczytać pojedynczy bajt do rejestru $s2: lui $t0, oxffff lw $s2, 4 ($t0) Problem: chcemy odczytywać tylko gdy urzadzenie ˛ wejściowe jest gotowe. Musimy wićc najpierw sprawdzić stan urzadzenia. ˛ A konkretnie pierwszy bit rejestru 0xffff0000. Procesor musi zaczekac aż ten bit przyjmie wartość 1: Wait: lui $t0, 0xffff lw $t1, 0($t0) andi $t1, $t1, 0x0001 beq t1, $zero, Wait lw $s2, 4 ($t0) # ladujemy rejestr do t1 # zeruj wszystkie bity oprocz os – p. 25/28 Funkcje systemowe Operacje wejścia-wyjścia za pomoca˛ funkcji systemowych: 1 Print an Integer ($a0 = wartość do wydrukowania) 2 Float 3 Print Double 4 Print String ($a0 = adres napisu) 5 Read an Integer (wczytany wynik zwracany w $v0) 6 Read Float 7 Read Double 8 Read a String ($a0 = adres pod jaki należy wpisać, $a1 = długość przydzielonej pamieci) ˛ + kilka innych – p. 26/28 Użycie funkcji systemowych .data str: .asciiz "the answer = " .text li $v0, 4 la $a0, str syscall li $v0, 1 li $a0, 5 syscall – p. 27/28 Rysunek z nast˛epnego wykładu 0 M u x Add Add 4 Control PC Instruction [25–21] Read register 1 Instruction [20–16] Read register 2 Read address Instruction [31–0] Instruction memory 0 M u Instruction [15–11] x 1 Write register Write data Instruction [15–0] 16 1 Shift left 2 RegDst Branch MemRead MemtoReg ALUOp MemWrite ALUSrc RegWrite Instruction [31–26] ALU result Read data 1 Zero Read data 2 Registers Sign extend 0 M u x 1 ALU ALU result Address Read data 1 M u x 0 Data Write memory data 32 ALU control Instruction [5–0] – p. 28/28