TIES448 luento 12

Luettavaa

Viralliset dokumentaatiot (laajoja, pitkälti TMI):

Konekieliset esimerkkikoodit

Yousourcessa

Konekielten yleispiirteitä

  • erittäin yksinkertainen rakenne
  • tyypittömyys (”kaikki on sanoja”)
  • rekisterit (ei nimettyjä paikallisia muuttujia)
  • alkeistoimituksiin rajoittuminen
  • abstraktiokeinojen puute

Konekielen rakenne

  • Konekielinen ohjelma on jono käskyjä
  • Kukin käsky koostuu (tavallisesti) operaatiokoodista eli opkoodista (engl. opcode) sekä 0–3 operandista
  • Käskyjen koodaustapa riippuu ISA:sta. Esimerkiksi
    • IA32:ssa kolmitavuinen käsky 80 F1 12 käskee XORraamaan luvun 18 ja erään rekisterin arvon ja tallettamaan tuloksen ko. rekisteriin.

Konekielen tyypit

  • Konekielessä kaikki data on eri mittaisia bittijonoja.
  • Kohdedatan pituus ja tulkinta riippuvat täysin opkoodista.
  • Tyyppitarkastusta ei ole.
  • tyypillisiä konekielen tyyppejä:
    • \(n\)-bittinen etumerkitön kokonaisluku (aritmetiikka modulo \(2^n\))
    • \(n\)-bittinen etumerkillinen kokonaisluku (kahden komplementti)
    • \(n\) bitin bittijono
    • \(n\) bitin IEEE-liukuluku
    • \(n = 8, 16, 32, 64(, 128)\) (paitsi liukuluvuilla \(n = (16,) 32, 64\))

Symbolinen konekieli

  • Tunnetaan myös nimellä assembly.
  • Ihmisen luettavaksi tarkoitettu konekielen esitystapa.
  • Joka ISA:lla on oma assemblynsä, joillakin on jopa useampia.
  • Yksi käsky esitetään yhdellä rivillä.
  • Kullakin opkoodilla on lyhyt nimi eli muistikas (engl. mnemonic).
  • Operandit esitetään havainnollisella tavalla.

Assemblerit

  • Yksinkertainen kääntäjä, ns. assembler, kääntää symbolisen konekielen konekieleksi.
  • Monilla ISA:illa on monta eri assembleria
  • Osa assemblereista tukee monta ISA:ta
  • tällä luennolla käytämme GNU:n assembleria ja AMD64:n Intel-syntaksia (melkein sama kuin nasmissa)

Ensimmäinen ohjelma

# Lasketaan 1+1 (kokonaisluvuilla), tulos mainin paluuarvona

        .intel_syntax noprefix
        
        .global main

        .text
        
main:   mov     rax, 1
        add     rax, 1
        ret

Käännös ja suoritus Linuxin komentorivillä (AMD64-koneessa, 64-bittinen käyttöjärjestelmä):

$ gcc -o minimal-amd64 minimal-amd64.s 
$ ./minimal-amd64 
$ echo $?

Sivuhuomautus

Käännämme gcc:llä, koska

  • gcc tajuaa tiedostopäätteestä .s, että syöte on assemblya
    • kutsuu automaattisesti assembleria
  • gcc hoitaa linkityksen puolestamme
  • gcc linkittää mukaan C-standardikirjaston
    • mukaan lukien C:n alustuskirjaston, joka kutsuu main-funktiota

Ensimmäiset rivit

  • Kommenttirivi alussa; kommentti päättyy rivin loppuun ja alkaa
    • # (AMD64)
  • Seuraava epätyhjä rivi valitsee käytetyn syntaksin
  • Kolmas epätyhjä rivi .global main ilmoittaa, että nimi main on julkinen

Assemblerin toimintaperiaate

  • pääsääntöisesti jokainen epätyhjä rivi muuttuu jonoksi tavuja ohjelmassa
    • myös käskyrivit koodataan jonoiksi tavuja
    • kommenttirivit lasketaan tyhjiksi
  • poikkeuksena osa riveistä, joilla esiintyy ns. "pseudokäskyjä", jotka alkavat pisteellä
  • ohjelma jaetaan osiin (section):
    • .text-osaan ohjelmakoodi ja vakiot
    • .data-osaan alustetut globaalit muuttujat
    • .bss-osaan nollaksi alustettavat globaalit muuttujat
  • osaan tulee kaikki osan ilmoittavan pseudokäskyn jälkeen tulevat tavut

Ensimmäinen koodirivi

        # AMD64
main:   mov     rax, 1
  • Rivin alussa on label main
    • main tarkoittaa tästä lähtien labelia seuraavan tavun osoitetta
  • Siirretään rekisteriin kokonaislukuvakio 1
  • Käsky alkaa muistikkaalla (engl. mnemonic), joka kertoo käskyn operaation
    • tässä sattumalta molemmissa assemblyissä sama muistikas, aina näin ei ole
  • Käskyn operandit erotetaan toisistaan pilkulla
    • eka operandi on operaation kohde
  • Vakio kirjoitetaan AMD64:ssä sellaisenaan

Rekisterit

  • keskusyksikön sisällä olevia nimettyjä muistiyksiköitä
  • rekisterin käyttö olennaisesti muistin käyttöä nopeampaa
  • joissakin ISA:issa laskenta mahdollista vain rekistereissä
  • erittäin rajattu määrä, kourallisesta muutamaan kymmeneen
  • AMD64:ssä
    • 64-bittisiä kokonaislukurekistereitä 16 kpl (RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8–R15)
    • 2×64-bittisiä liukulukurekistereitä 16 kpl (XMM0–XMM15)

AMD64:n alirekisterit

  • Jokaisella AMD64:n kokonaislukurekisterillä on nimettyjä alirekistereitä
  • RAX, RBX, RCX, RDX:
    • 32 alinta bittiä EAX, EBX, ECX, EDX
    • 16 alinta bittiä AX, BX, CX, DX
      • jonka 8 ylintä bittiä AH, BH, CH, DH
    • 8 alinta bittiä AL, BL, CL, DL
  • RSI, RDI, RBP, RSP, R8–R15:
    • 32 alinta bittiä ESI, EDI, EBP, ESP, R8D–R15D
    • 16 alinta bittiä SI, DI, BP, SP, R8W–R15W
    • 8 alinta bittiä SIL, DIL, BPL, SPL, R8B–R15B

Toinen koodirivi

        # AMD64
        add     rax, 1
  • AMD64:n add laskee operandit yhteen ja tallettaa tuloksen ensimmäiseen operandiin
  • add tekee kokonaislukuyhteenlaskun!
    • liukulukulaskentaan on eri käskyt

Kolmas koodirivi

        # AMD64
        ret
  • käsky tekee "pellin alla" aika lailla, mutta lopputulos on selkeä:
    • palataan aliohjelmasta main sen kutsujaan

Toinen ohjelma

        # Kopioidaan syöte tulosteeseen

        .intel_syntax noprefix
        .global main
        .text

main:   push    rbp
0:      call    getchar
        cmp     eax,    0
        jl      1f
        mov     rdi,    rax
        call    putchar
        jmp     0b
1:      mov     rax,    0
        pop     rbp
        ret

Suuruusvertailujen koodaaminen

        # AMD64
        cmp     eax,    0
  • cmp suorittaa vähennyslaskun, jonka tulosta ei tallenneta mihinkään
  • tuloksen ominaisuuksia tallennetaan lippuihin, mm.
    • SF: oliko negatiivinen?
    • ZF: oliko nolla?
    • OF: tuottiko ylivuodon?
    • CF: tuottiko carry-tilanteen?

Valintojen tekeminen

        # AMD64
        jl      1f
...
1:
  • lippujen perusteella voidaan tehdä ehdollisia hyppyjä
    • hyppykäskyn nimi alkaa AMD64:ssä j
    • nimi jatkuu ehdon kuvauksella
  • hypyn kohteena jokin lähellä oleva käsky
    • 1f viittaa käskyä seuraavaan 1:-labeliin
    • 1b viittaa käskyä edeltävään 1:-labeliin
    • 2f / 2: jne myös mahdollisia
    • voi myös käyttää tavallisia nimettyä labeleita

Ehdollisia hyppykäskyjä AMD64:ssä

Käsky Ehto cmp \(a\),\(b\) \(a\)
je, jz \(ZF = 1\) \(a = b\) \(a = 0\)
jne, jnz \(ZF = 0\) \(a \neq b\) \(a \neq 0\)
js \(SF = 1\) \(a < 0\)
jns \(SF = 0\) \(a \geq 0\)
jb \(CF=1\) \(a < b\) (u)
jnb \(CF=0\) \(a \geq b\) (u)
jge \(SF = OF\) \(a \geq b\) (s)
jl \(SF \neq OF\) \(a < b\) (s)
jg \(ZF=0\land SF=OF\) \(a > b\) (s)
jle \(ZF=1\lor SF\neq OF\) \(a \leq b\) (s)
  • u tarkoittaa etumerkitöntä tulkintaa
  • s tarkoittaa kahden komplementti -tulkintaa

Silmukat

        # AMD64
0:
...
        jl      1f
...
        jmp     0b
1:
  • silmukoita ei ole, ne on koodattava hyppykäskyistä
  • while (e) S kääntyy muotoon
    • e
    • jos epätosi, hyppää ulos
    • S
    • hyppää silmukan alkuun
  • ehdoton hyppy AMD64:ssä jmp

Application Binary Interface (ABI)

  • ISA-kohtainen
  • yleensä myös käyttöjärjestelmäkohtainen
  • voi olla myös kääntäjäkohtainen
  • määrittelee mm.
    • tietotyyppien esitystavat
    • aliohjelmien kutsurajapinnan
  • jos käännetty ohjelma ja käännetty kirjasto noudattavat samaa ABIa, ne voi linkittää yhteen

ABI-vaihtoehtoja

Aliohjelmat AMD64:ssä (SysV ABI)

main:   push    rbp
        call    getchar         # int getchar(void)
        cmp     eax,    0
        jl      1f
        mov     rdi,    rax
        call    putchar         # int putchar(int)
...
1:      mov     rax,    0
        pop     rbp
        ret
  • aliohjelmakutsukäsky on call ja paluukäsky ret
    • paluuosoite kulkee automaattisesti pinossa
    • paluuosoite on 8 tavua pitkä, mutta pinon osoitteen tulee olla 16:lla jaollinen kutsun tapahduttua, joten laitetaan jotain muuta myös pinoon
  • 6 ensimmäistä kokonaislukuparametria välitetään rekistereissä rdi, rsi, rdx, rcx, r8 ja r9
  • kokonaislukupaluuarvo välitetään rekisterissä rax
    • mutta rax on 64-bittinen, int on 32-bittinen, joten vertailu tehdään yllä rax:n 32-bittisellä osarekisterillä eax

Caller save ja callee save

  • rekisterit jaetaan kahteen luokkaan:
    • caller save: rekisteri, jota aliohjelma saa käyttää vapaasti aliohjelmakutsujen välissä, mutta jonka arvo ei välttämättä säily aliohjelmakutsun yli
    • callee save: rekisteri, jota aliohjelma saa käyttää vain, jos se tallettaa sen alkuperäisen arvon ja palauttaa sen ennen paluutaan
      • AMD64:ssä (SysV ABI) rbx, rbp, r12, r13, r14, r15

Rekisterien arvojen talletus pinoon AMD64:ssä

  • push rekisteri laitaa pinoon
    • huolehdi, että aliohjelmakutsun laitettua paluuosoitteen pinoon pino-osoitin on jaollinen 16:lla
  • pop rekisteri poistaa pinosta
    • poista käänteisessä järjestyksessä (LIFO!)

Liukuluvut AMD64:ssä

  • yksinkertaisuuden vuoksi vain kaksoistarkkuus (double)
  • Samalla myös esimerkki
    • globaaleista muuttujista
    • merkkijonovakioista

Keskiarvoaliohjelma

avg:    addsd           xmm0, xmm1
        mov             rax, 2
        cvtsi2sd        xmm1, rax
        divsd           xmm0, xmm1
        ret
  • 8 ensimmäistä liukulukuparametria välitetään rekistereissä xmm0,...,xmmm7
  • liukulukupaluuarvo välitetään rekisterissä xmm0
  • addsd laskee kaksi liukulukua yhteen
  • cvtsi2sd muuttaa kokonaisluvun liukuluvuksi
  • divsd on jakolasku

Liukulukujen lukeminen scanf:llä

        lea     rdi,    [rfmt]
        mov     al,     0
        lea     rsi,    [tmp1]
        lea     rdx,    [tmp2]
        call    scanf
  • lea laskee ensimmäiseen operandiinsa toisen operandinsa muistiosoitteen
    • hakasulkeisiin kirjoitetaan muistiosoitteen lähde
      • voi olla vakio, rekisteri tai vähän monimutkaisempaa
  • scanf-funktion tyyppisille aliohjelmille (stdarg) on kerrottava al-rekisterissä, montako liukulukuparametria on enintään rekistereissä

Formaattimerkkijono

lea     rdi,    [rfmt]
...
        .balign         8
rfmt:   .asciz          "%lf %lf\n"        
  • Merkkijonovakio (ei Unicode) kirjoitetaan .text-osaan .asciz-pseudokäskyllä.
    • Sille annetaan nimi kuten aliohjelmalla.
    • Ennen merkkijonovakiota varmistetaan, että osoite on kahdeksalla jaollinen, pseudokäskyllä .balign 8

Luettujen lukujen tallennusmuuttujat

        lea     rsi,    [tmp1]
        lea     rdx,    [tmp2]
...
        .bss
        .balign 8
        .comm   tmp1,   8
        .comm   tmp2,   8
  • Jokaista formaattimerkkijonossa olevaa %lf-osajonoa kohti pitää olla yksi tallennusmuuttuja, jonka osoite viedään parametina.
  • Globaalit muuttujat voidaan luoda .bss-osaan
    • Muuttujalle annetaan nimi tmp1 ja 8 tavua tilaa pseudokäskyllä .comm tmp1, 8
    • Muuttujat alustuvat ohjelman käynnistyessä nollabittijonoiksi

Lukemisen onnistumisen tarkastaminen ja lukujen käyttöönotto

        call    scanf
        cmp     rax,    2
        jne     1f
        movsd   xmm0,   [tmp1]
        movsd   xmm1,   [tmp2]
  • scanf palauttaa onnistuneesti luettujen muuttujien lukumäärän
  • luetut luvut siirretään liukulukurekistereihin movsd-käskyllä

Lukujen tulostaminen printf:llä

        movsd   xmm0,   [tmp1]
        movsd   xmm1,   [tmp2]
        lea     rdi,    [wfmt]
        mov     al,     1
        call    printf
  • printf:n formaattimerkkijonossa esiintyvä %lf tarkoittaa, että printf:lle pitää antaa lisäksi double-tyyppinen parametri
  • movsd-käskyä voi myös käyttää liukuluvun muistista lataamiseen
  • liukulukuparametrit välitetään myös stdarg-funktioille liukulukurekistereissä
    • al:ssä kerrottava liukulukurekistereissä olevien parametrien enimmäismäärä
      • ei tarvitse olla tarkka tieto, mutta max 8

C-kirjastofunktioiden yhteenveto

Vapaaehtoisia harjoitustehtäviä

Kirjoita assembly-ohjelma:

  1. joka lukee käyttäjältä rivin ja tulostaa saman rivin niin, että pienet kirjaimet on muutettu isoiksi kirjaimiksi (vain ASCII, eli ei ääkkösiä ym).
  2. joka laskee syötteessä olevien sanojen lukumäärän.
  3. joka tulostaa Fibonaccin lukujonon (siihen asti kunnes 64-bittisestä kokonaisluvusta loppuu esitystarkkuus tai kunnes käyttäjä ohjelman keskeyttää Ctrl-C:llä)

Näitä voidaan käsitellä ohjauksissa.

These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.