Tässä dokumentissa kuvataan vain VICCin toteuttamiselle oleellinen osa Alphasta. Koko assembler-dokumentaatiosta on saatavissa massiivisempi Digitalin tuottama dokumentaatio, jota tarvitaan, jos haluaa toteuttaa täyden tuen debuggerille, liukuluvuille tms.
Nimi | Suositeltu käyttötapa |
---|---|
v0 ($0) | Funktioiden paluuarvo. |
t0-t11 ($1-$8,$22-$25) | Laskennan tilapäismuuttujia, caller-saved. |
s0-s6 ($9-$15) | Callee-saved-rekistereitä. |
a0-a5 ($16-$21) | Argumenttien välitys |
ra ($26) | Funktion paluuosoite |
AT ($28 tai $at) | Varattu assemblerin käyttöön |
gp ($29 tai $gp) | Osoitin globaaliin dataan |
sp ($30 tai $sp) | Pino-osoitin |
zero ($31) | Vakio nolla |
Vain callee-saved-rekisterit säilyttävät arvonsa funktiokutsujen yli. Niinpä jos kutsuttu funktio haluaa käyttää jotain rekistereistä s0-s6, on sen ensin talletettava sen arvo omaan pinokehykseensä ja luettava se sieltä takaisin ennen paluutaan.
gp on osoitin modulikohtaisen vakiodataan, ja sen arvo voi muuttua kun tehdään kutsuja eri moduleissa oleviin funktioihin. Tästä lisää myöhemmin.
Alphassa rekisteri s6 voi olla merkitykseltään myös ns. frame-osoitin, jota tarvitaan lähinnä vain jos halutaan tuotetun koodin olevan helposti debugattavaa. Muutoin se voidaan käyttää muihin tarkoituksiin. Kuvaavammat nimet v0, t0, t1 jne. saa assemblerissa käyttöönsä, jos assembler-tiedoston alussa on rivi
#include <regdef.h>
Käsky | Semantiikka | Toteutus |
---|---|---|
lda r0, L | r0 = L; | Makro |
ldiq r0, C | r0 = C; | Makro |
ldq r0, C(r1) | r0 = mem[r1 + C]; | Käsky |
stq r0, C(r1) | mem[r1 + C] = r0; | Käsky |
addq r0, r1, r2 | r2 = r0 + r1; | Käsky |
addq r0, C, r1 | r1 = r0 + C; | Käsky |
s4addq r0, r1, r2 | r2 = 4*r0 + r1; | Käsky |
s4addq r0, C, r1 | r1 = 4*r0 + C; | Käsky |
s8addq r0, r1, r2 | r2 = 8*r0 + r1; | Käsky |
s8addq r0, C, r1 | r1 = 8*r0 + C; | Käsky |
subq r0, r1, r2 | r2 = r0 - r1; | Käsky |
subq r0, C, r1 | r1 = r0 - C; | Käsky |
s4subq r0, r1, r2 | r2 = 4*r0 - r1; | Käsky |
s4subq r0, C, r1 | r1 = 4*r0 - C; | Käsky |
s8subq r0, r1, r2 | r2 = 8*r0 - r1; | Käsky |
s8subq r0, C, r1 | r1 = 8*r0 - C; | Käsky |
absq r0, r1 | r1 = (r0 < 0) : -r0 : r0; | Käsky |
absq C, r0 | r0 = (C < 0) : -C : C; | Käsky |
negq r0, r1 | r1 = -r0; | Käsky |
negq C, r0 | r0 = -C; | Käsky |
mulq r0, r1, r2 | r2 = r0 * r1; | Käsky |
mulq r0, C, r1 | r1 = r0 * C | Käsky |
divq r0, r1, r2 | r2 = r0 / r1; | Makro |
divq r0, C, r1 | r1 = r0 / C; | Makro |
remq r0, r1, r2 | r2 = r0 % r1; | Makro |
remq r0, C, r1 | r1 = r0 % C; | Makro |
sll r0, r1, r2 | r2 = r0 << r1; | Käsky |
sll r0, C, r1 | r1 = r0 << C; | Käsky |
sra r0, r1, r2 | r2 = r0 >> r1; | Käsky |
sra r0, C, r1 | r1 = r0 >> C; | Käsky |
mov r0, r1 | r1 = r0; | Käsky |
cmpeq r0, r1, r2 | r2 = (r0 == r1); | Käsky |
cmpeq r0, C, r1 | r1 = (r0 == C); | Käsky |
cmplt r0, r1, r2 | r2 = (r0 < r1); | Käsky |
cmplt r0, C, r1 | r1 = (r0 < C); | Käsky |
cmple r0, r1, r2 | r2 = (r0 <= r1); | Käsky |
cmple r0, C, r1 | r1 = (r0 <= C); | Käsky |
br L | goto L; | Käsky |
beq r0, L | if (r0 == 0) goto L; | Käsky |
bne r0, L | if (r0 != 0) goto L; | Käsky |
blt r0, L | if (r0 < 0) goto L; | Käsky |
ble r0, L | if (r0 <= 0) goto L; | Käsky |
bgt r0, L | if (r0 > 0) goto L; | Käsky |
bge r0, L | if (r0 >= 0) goto L; | Käsky |
cmoveq r0, r1, r2 | if (r0 == 0) r2 = r1; | Käsky |
cmoveq r0, C, r2 | if (r0 == 0) r2 = C; | Käsky |
cmovne r0, r1, r2 | if (r0 != 0) r2 = r1; | Käsky |
cmovne r0, C, r2 | if (r0 != 0) r2 = C; | Käsky |
cmovlt r0, r1, r2 | if (r0 < 0) r2 = r1; | Käsky |
cmovlt r0, C, r2 | if (r0 < 0) r2 = C; | Käsky |
cmovle r0, r1, r2 | if (r0 <= 0) r2 = r1; | Käsky |
cmovle r0, C, r2 | if (r0 <= 0) r2 = C; | Käsky |
cmovgt r0, r1, r2 | if (r0 > 0) r2 = r1; | Käsky |
cmovgt r0, C, r2 | if (r0 > 0) r2 = C; | Käsky |
cmovge r0, r1, r2 | if (r0 >= 0) r2 = r1; | Käsky |
cmovge r0, C, r2 | if (r0 >= 0) r2 = C; | Käsky |
Alpha osaa lukea muistista dataa vain kahdeksalla jaollisista osoitteista, muutoin kyseessä on ns. alignment-virhe. Sama pätee tietysti myös muistiin kirjoituksiin.
Ylläolevan listan käskyt addq, subq, negq ja mulq voi korvata käskyillä addqv, subqv, negqv ja mulqv, jos haluaa tarkistaa ylivuodot.
Makrot voivat vastata useita Alpha-käskyjä. Käskyt divq ja remq käyttävät rekistereitä t9-t12. Todellisuudessa myös moni muu käsky saattaa laventua useammaksi käskyksi, varsinkin jos niiden vakio-argumentti on yli 8-bittinen. Apuna käytetään tällöin AT-rekisteriä, jota käyttäjän siis ei tulisi hyödyntää.
Oikein innokas optimoija saattaa kaivata muitakin käskyjä, esimerkiksi bittioperaatioita ja fetch-käskyä. Niiden dokumentaatio löytyy Alphan assembler-manuaalista.
.text .align 3 .globl funktio .ent funktio funktio: ldgp gp,0(pv) funktio..ng:
Alphassa pino (yleensä) kasvaa ylhäältä, suuremmista muistiosoitteista, alaspäin, kohti pienempiä muistiosoitteita. Koska VICCissä kaikki argumentit mahtuvat aina kuuteen argumenttirekisteriin, ei tarvetta välittää argumentteja pinossa tule. Niinpä pinoon tarvitsee varata tilaa vain funktion paikallisille muuttujille ja callee-saved -rekisterien ja ra:n tallettamiseen. Jos funktio ei kutsu muita funktioita, ei käytä callee-saved -rekistereitä ja kaikki sen käyttämät muuttujat mahtuvat tilapäisrekistereihin, ei pinoa tarvitse laisinkaan käyttää. Jos funktio kutsuu muita funktioita, pitää pino-osoitinta muuttaa tämän funktion pinokehyksen verran (vaikka 32 tavua):
lda sp, -32(sp)
Kaikki funktion käyttämät callee-saved -rekisterit pitää tallettaa pinokehykseen:
stq s0, 8(sp) stq s1, 16(sp)
Jos funktio kutsuu muita funktioita, pitää sen tallettaa paluuosoite pinokehykseen:
stq ra, 0(sp)
Kun funktio haluaa kutsua toista funktiota, pitää sen ensin tallettaa omat tilapäismuuttujansa (mikäli niissä on jotain funktiokutsun jälkeen tarpeellista tietoa) pinokehykseen ja sijoittaa kutsun argumentit argumenttirekistereihin. Jos kutsuttava funktio kutsuttu on VICC-funktio, varsinainen kutsu on yksinkertaisesti
bsr kutsuttu..ng
Huomaa, että VICCissä ei ole modulijärjestelmää, joten gp:n muuttumista ei tapahdu. Jos kutsuttu funktio on kirjastofunktio (toisessa käännösyksikössä), on kutsu
jsr kutsuttu ldgp gp, 0(ra)
Tilapäismuuttujien lisäksi myös argumenttirekisterien arvo voi muuttua kutsun aikana. Paluuarvot ovat rekisterissä v0, johon myös funktion tulee jättää oma paluuarvonsa. Kun funktio päättyy, pitää luotu pinokehys purkaa ja mahdollisesti muuttunut ra ja callee-saved -rekisterit lukea takaisin:
ldq ra, 0(sp) ldq s0, 8(sp) ldq s1, 16(sp) lda sp, 32(sp)
Lopulta funktio palaa:
ret zero,(ra),1 .end funktio
Funktioiden ulkopuolella voi assemblerin käskeä luomaan muuttujia ja vakioita. Esimerkkinä merkkijonovakio foo ja taulukko bar : ARRAY [2] OF Int
.data foo: .ascii "...\0" .align 3 bar: .comm bar 16
Tähän liittyy seuraava helpottava korjaus VICCin määrittelyyn: Taulukkoa ei voi sijoittaa toiseen taulukkoon. Taulukon välittäminen argumenttina tapahtuu viittauksena, eli välitetään argumenttirekisterissä taulukon alkuosoite, ja vastaavasti taulukkotyyppisille paluuarvoille.
Taulukko paikallisena muuttujana tarkoittaa taulukon käyttämän tilan vaaraamista pinosta.
Assemblerissa risuaita # kommentoi pois loppurivin, mutta koska assembler ajetaan cpp:n läpi, ei risuaita voi olla rivin ensimmäinen merkki.
Muita esimerkkejä saa esimerkiksi Alphan assembler-manuaaleista ja kääntämällä Alphoissa sopivia C-ohjelman pätkiä optiolla -S.