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

Podobne dokumenty