Ebben a feladatban egy kicsi, az x86-64 assembly nyelvből ihletett nyelv szintaxisát és végrehajtási modelljét definiálod.
A cél, hogy formalizáld, hogyan írjuk le, dolgozzuk fel és hajtjuk végre a kódot egy korlátozott környezetben.
Az assembly kódodat egy különleges szerkezetbe írod:
let program := assemble!(
-- assembly code here
)
Ennek a szerkezetnek egy programot kell létrehoznia, ami egy olyan függvényt szimulál, amely végrehajtja az assembly kódot, és visszaad egy értéket.
Ezt a programot ezután a következő szintaxissal hívod meg, ahol az a, b, c stb. argumentumok:
let result := program(a, b, c, ...)
Minden argumentum és a visszatérési érték is 64 bites előjeles egész szám. Legfeljebb 6 argumentum engedélyezett.
A visszatérési érték és az összes argumentum is regisztereken keresztül adódik át. A nyelvednek a következő regisztereket kell támogatnia:
| regiszter | szerep |
|---|---|
rdi |
1. argumentum |
rsi |
2. argumentum |
rdx |
3. argumentum |
rcx |
4. argumentum |
r8 |
5. argumentum |
r9 |
6. argumentum |
rax |
visszatérési érték |
A regiszterek alapértelmezett értéke 0, kivéve, ha a függvény argumentumainak átadására szolgálnak.
Például a következő programban minden regiszter értéke 0, kivéve a rdi-t, amelynek kezdőértéke 10, és az rsi-t, amelynek kezdőértéke -20:
program(10, -20)
A program visszatérési értéke mindig az rax regiszterben tárolódik.
A regiszterneveknél nem számít a kis- és nagybetű.
Ez azt jelenti, hogy a rax, a RAX és az rAx mind ugyanarra a regiszterre utal.
Az assembly kód minden sora kétféle formájú lehet:
Ha egy utasítás nem módosítja, a program végrehajtása lineárisan halad. Ez azt jelenti, hogy egy sor teljesen végrehajtódik, mielőtt a következő sor végrehajtódna.
A címkék szintaxisa a következő:
<label>:
Nem módosítják egyetlen regiszter értékét sem, és semmilyen más hatásuk nincs a programra, mint hogy kijelölik a kód bizonyos helyeit, hogy az utasítások használhassák őket.
A címkéknél számít a kis- és nagybetű.
Ez azt jelenti, hogy a Start, a start és az sTart három különböző címke.
A legtöbb utasítás szintaxisa a következő:
<opcode> <destination>, <source>
Az opcode jelöli a végrehajtandó műveletet, a destination és a source operandus értékeit felhasználva.
A művelet eredménye a destination operandusban tárolódik.
Például az add utasítás összeadja a destination és a source operandus értékét, és az eredményt a destination operandusban tárolja.
A destination operandus mindig regiszter, a source operandus viszont lehet regiszter vagy literál szám.
Az alábbi utasításokat kell támogatnia a programodnak:
| opcode | végrehajtott művelet |
|---|---|
mov |
destination := source |
add |
destination := destination + source |
sub |
destination := destination - source |
mul |
destination := destination * source |
div |
destination := destination / source |
xor |
destination := destination ^^^ source |
and |
destination := destination &&& source |
or |
destination := destination ||| source |
shl |
destination := destination <<< source |
shr |
destination := destination >>> source |
Ezeken kívül a programodnak támogatnia kell a kétoperandusú cmp utasítást is.
Ez az utasítás nem módosít egyetlen regisztert sem, hanem összehasonlítja a destination és a source operandust, és a program egy belső állapotát három lehetőség egyikére állítja:
destination > source
destination == source
destination < source
Hogyan tartod nyilván belül ezt az állapotot, az rád van bízva.
Van néhány utasítás, amely megváltoztatja a program folyamatát, és a végrehajtást a kód egy másik pontjára viszi át. Ezeket ugró utasításoknak nevezzük.
Mindegyikük egyetlen operandust kap, egy címkét, amely az ugrás célját jelöli, vagyis a kód azon pontját, ahol a végrehajtás folytatódik:
<opcode> <label>
A jmp utasítás mindig végrehajtja az ugrást a célcímkére.
A többi ugró utasítás csak akkor ugrik, ha a program belső állapota egy adott értékre van állítva.
Ez azt jelenti, hogy általában egy cmp utasítás előzi meg őket.
Az alábbi ugró utasításokat kell támogatnia a programodnak:
| utasítás | végrehajtott művelet |
|---|---|
jmp |
feltétel nélküli ugrás. Az ugrás mindig megtörténik |
je |
ugrik, ha a belső állapot egyenlő |
jl |
ugrik, ha a belső állapot kisebb, mint |
jg |
ugrik, ha a belső állapot nagyobb, mint |
Az utasítások opcode-jainál nem számít a kis- és nagybetű, akár kétoperandusú, akár egyoperandusú utasításról van szó.
Ez azt jelenti, hogy a add, az ADD és az aDd mind ugyanarra az utasításra utal.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Lean nyelvet 100 feladat segítségével, valódi emberi mentorálással, mindez ingyen.