Színpaletta

Színpaletta

Tanulófeladat

Bevezetés

Memória

A memóriát egy program számára általában az operációs rendszer (OS) képezi le egy általános elrendezés szerint:

cím memóriaterület
magas verem
...
kupac
olvasható-írható szegmens
kód/csak olvasható szegmens
alacsony fenntartott

A szegmensekre osztott memória különböző jogosultságú szakaszokra tagolódik.

Az eddig definiált függvényeink mind a .text szakaszban voltak. Ez a szakasz csak olvasható, végrehajtható adatokat tartalmaz. Más szakaszok adatváltozók deklarálására szolgálnak, amelyek lehetnek csak olvashatók vagy olvasható-írhatók, de nem végrehajthatók.

A .data szakasz

Az inicializált adatokat a .data szakaszban deklaráljuk.

A NASM-ben (The Netwide Assembler, az ezen a kurzuson használt assembler) egy inicializált változónak van neve, egy direktívája, amely az adat méretét jelzi, és egy vesszővel elválasztott értéklistája. Ezeket szóköz választja el egymástól, a címkét pedig opcionálisan : követheti.

A fő direktívák és a hozzájuk tartozó adatméretek:

direktíva méret
db 1 bájt
dw 2 bájt
dd 4 bájt
dq 8 bájt

Például ez egy space nevű, 1 bájtos változót deklarál 10 értékkel:

section .data
    space db 10

A section .data szakaszban deklarált változók módosíthatók, azaz olvashatók és írhatók. Emellett statikus tárolási időtartammal rendelkeznek, ami azt jelenti, hogy a program teljes futamideje alatt léteznek.

A .rodata szakasz

A .rodata szakasz hasonló a section .data szakaszhoz. Mindkét szakasz inicializált adatokat tartalmaz, amelyeket ugyanúgy deklarálunk, és amelyeknek ugyanaz a tárolási időtartama.

A fő különbség köztük az, hogy a section .rodata szakaszban lévő adatok változhatatlanok, azaz csak olvashatók.

Note

Az equ direktívával definiált konstansok eltérnek a section .rodata szakaszban definiáltaktól.

Az equ direktívával definiált konstans nem foglal helyet a memóriában, és az assembler közvetlenül behelyettesíti az értékével. Valójában ez csupán az adott érték helyőrzője.

Ezzel szemben a section .rodata szakaszban definiált konstansok ténylegesen a memóriában tárolódnak, és van címük.

Az adatok elérése

Címkék és indirekció

A deklarált adatokhoz névnek kell kapcsolódnia. Ezt a nevet címkének nevezzük.

A címke egy szimbólum, amely a memóriában lévő adat konkrét címét kódolja. Az x86-64-ben a címek 64 bites értékek.

A NASM-ben, ha egy adatot közvetlenül a címkéjével próbálunk elérni, nem a lefoglalt memóriát kapjuk meg, hanem annak címét:

section .data
    example dq 27 ; this declares a 8-byte variable initialized with 27

section .text
fn:
    mov rax, example ; this stores the address of the declared variable in rax, not its contents
    ...

Ahhoz, hogy egy memóriacím tartalmát elérjük, dereferálni kell azt. Ezt indirekciónak nevezzük.

A NASM-ben erre a [] szolgál:

section .data
    example dq -27 ; this declares a 8-byte variable initialized with -27

section .text
fn:
    mov rax, [example] ; this dereferences example and access the value stored in memory (-27)
    ...

Vannak azonban olyan helyzetek, amikor kérdéses lehet a dereferált memória mérete. Ilyen esetekben a méretet megadó előtagot kell használni.

Ezek a legfontosabb előtagok és méreteik egy tipikus x86-64 programban:

előtag méret
byte 1 bájt
word 2 bájt
dword 4 bájt
qword 8 bájt

Ugyanez a betöltés kiírható úgy is, hogy a méretet egyértelműen megadjuk:

    mov rax, qword [example] ; same dereference, size stated explicitly

Jó gyakorlat, ha memória dereferálásakor mindig használunk előtagot.

Írás a memóriába

A memóriába írás ugyanígy történik, egy cím dereferálásával:

section .data
    example1 db 10            ; example1 is a 1-byte memory location initialized with value 10
    example2 dq -456          ; example2 is a 8-byte memory location initialized with value -456
    example3 dd 54            ; example3 is a 4-byte memory location initialized with value 54

section .text
fn:
    mov byte [example1], 20   ; example1 now has value 20
    mov qword [example2], rdx ; example2 now has value equal to the contents in rdx
    mov dword [example3], eax ; example3 now has value equal to the contents in eax

Megjegyzendő, hogy a legtöbb utasításban használhatsz memóriaoperandusokat anélkül, hogy előbb betöltenéd a tartalmukat egy regiszterbe. Általában azonban nem lehet egyszerre a forrás- és a céloperandusban használni őket, csak az egyikben:

section .data
    example4 dw 4
    example5 dq -8
    example6 dd 15

section .text
fn:
    add word [example4], 5     ; example4 is now a 2-byte memory location with the value 4 + 5 = 9
    imul rax, qword [example5] ; rax = rax * (-8)
    ; this is not possible -> sub dword [example6], dword [example6]
A LEA utasítás

Bár a mov is használható egy változó címének regiszterbe mentésére, erre a konkrét célra külön utasítás létezik: a lea.

Ez az utasítás memóriaformájú operandust használ, de nem olvassa a memóriát. Ehelyett kiértékeli az effektív cím kifejezését, és az eredményt a céloperandusba írja:

lea rax, [example] ; this stores the address of 'example' in rax

Idiomatikusabb a lea utasítást használni memóriacímek kiszámítására és regiszterekbe mentésére.

Relatív címzés

Amikor memóriahelyeket érünk el, a NASM alapértelmezett viselkedése, hogy abszolút címeket, azaz rögzített memóriacímeket állít elő.

Biztonsági okokból a futtatható fájlokat gyakran PIE (pozíciófüggetlen futtatható fájl) formában készítik el, ahol a memóriaterületek véletlenszerűen kiválasztott helyekre kerülnek. Egy PIE-ban egy változó végleges címe nem ismert a linkelés idején. Ezért a kód inkább egy rip nevű speciális regiszter értékéhez viszonyított eltolásként számítja ki a címeket; ez a regiszter a végrehajtandó következő utasításra mutat.

Ezt általában RIP-relatív címzésnek nevezik.

A NASM-ben a rel operátorral kérhetsz RIP-relatív elérést:

mov rax, qword [rel variable]

A relatív címzés egy forrásfájl alapértelmezésévé is tehető a fájl elején szereplő default rel direktívával.

Ezen a kurzuson minden feladatot PIE-ként fordítunk és linkelünk, ezért a relatív címek előállításához a rel-t kell használni.

Láthatóság

Bármely szakaszban (például .text, .data, .rodata) definiált címke (függvény és adat) látható ugyanazon forrásfájlon belül. Ha global-ként vannak deklarálva, más forrásfájlokból is láthatók.

Fordítva, a más forrásfájlokban definiált címkék akkor láthatók az aktuális forrásfájlból, ha extern-ként vannak deklarálva. Ebben az esetben az assemblyben nincs utalás az adat méretére, azt előre ismerni kell.

default rel

section .data

global number1 ; 'number1' is a variable visible to other source files
number1 db 200

extern number2 ; 'number2' is a variable visible to the current source file, but defined in another

section .text

extern sum ; sum is a function visible to the current source file, but defined in another

fn:
    mov dil, byte [number1]
    mov sil, byte [number2]
    call sum
    ...

Utasítások

A barátod, José tanár egy helyi iskolában. Eszébe jutott néhány szórakoztató kísérlet, amivel megmutathatja, hogyan lehet a színeket összekeverni, hogy különböző színek szülessenek belőlük.

Ezekhez a kísérletekhez kérte a segítségedet.

Note

Ebben a feladatban egy színt egy 32 bites (4 bájtos) szám képvisel, amely a szín RGB értékét kódolja.

Egy RGB érték 3 csatornából áll, Red, Green és Blue, amelyek mindegyike 8 bitet (1 bájtot) foglal el. A negyedik bájtot általában az Alpha csatorna számára tartják fenn, ebben a feladatban azonban az értéke üres (0) lesz.

1. Kérdezd le egy szín RGB-értékét

Az egyes színek értékeit egy táblázat tárolja, amely egy másik forrásfájlban van definiálva. Egy színt a táblázatban egy egyedi cím azonosít.

Definiálj egy get_color_value függvényt, amely visszaadja egy szín 32 bites értékét. A függvény paraméterként átveszi a szín érvényes címét a színtáblázatban.

get_color_value(black)
// => 0

Tipp - 32 bit 4 bájtnak felel meg.

2. Adj hozzá alapszínt

Hogy különböző színeket keverhessen, José először rögzít egy alapszínt, és csak a hozzá kevert másodlagos színt változtatja.

Definiálj egy add_base_color függvényt, amely elmenti egy szín 32 bites értékét a base_color változóba, hogy később felhasználható legyen. A függvénynek nincs visszatérési értéke, és paraméterként a színnek a színtáblázatban lévő címét veszi át.

A base_color változót neked kell definiálnod, és más forrásfájlokból is elérhetőnek kell lennie.

Egyszerre legfeljebb 1 alapszín lehet. Ha új alapszínt adsz hozzá, a régi elvész.

Alapértelmezés szerint, a program indulásakor a base_color kezdőértéke a white 32 bites értéke, azaz a 0xFFFFFF00 legyen.

Tipp - A NASM elfogadja a hexadecimálisan, a 0x előtaggal megadott számokat, mint például a 0xFFFFFF00.

3. Definiálj konstansokat az elsődleges színekhez

José sok kombinációt szeretne készíteni az elsődleges színek felhasználásával, ezért külön szeretné őket tárolni a gyors elérés érdekében. Mivel a színek ábrázolására az RGB-t használja, az elsődleges színek a következők:

  • RED, 0xFF000000 értékkel.
  • GREEN, 0x00FF0000 értékkel.
  • BLUE, 0x0000FF00 értékkel.

Definiálj egy-egy konstansot mindegyik színhez. Ezeknek a konstansoknak más forrásfájlokból is elérhetőnek kell lenniük.

4. Keverd össze a színeket

A színeket egy másik forrásfájlban definiált combining_function szerint kell összekeverni. A függvény paraméterként a base_color és a vele keverendő másodlagos szín 32 bites értékét veszi át. Visszaadja a kombinált szín 32 bites értékét.

Definiálj egy make_color_combination függvényt, amely összekever két színt, és az eredményt a memóriában tárolja. A függvénynek nincs visszatérési értéke, és ebben a sorrendben a következő paramétereket veszi át:

  • A cím, ahová a kombinált szín 32 bites értékét tárolni kell.
  • A másodlagos szín címe a színtáblázatban, amelyet az alapszínnel kell összekeverni.
Caution

Figyeld meg, hogy a combining_function módosíthatja az általad használt regiszterek értékeit. Mielőtt meghívod a függvényt, minden szükséges változót ments el a memóriába.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
x86-64 Assembly Exercism

Készen állsz elkezdeni a(z) Színpaletta feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) x86-64 Assembly nyelvet 22 fogalom130 feladat segítségével, valódi emberi mentorálással, mindez ingyen.