Fussunk át gyorsan rajta, hogy mit is tudunk eddig.
- Ismerünk néhány assembly utasítást (LDA, STA), amivel képesek vagyunk byte-okat írni a memória megadott címeire
- Tudjuk, hogy ha a $0400-tól kezdődő memóriaterületre írunk, az megjelenik a képernyőn, mint karakter
- Emellett, ha a $D800-tól kezdődően befolyásolni tudjuk a karakterek színeit
Világosan látszik, hogy minden ismeret rendelkezésünkre áll egy jó kis "Hello világ" programhoz.
Nézzük meg először a nehézkes, nyögvenyelős módszert, vagyis lépjünk be a monitorba (MON), és kezdjük el begépelni az alábbi csodát az $1000-es címtől kezdődően (A 1000 LDA #$08). Akinek kell egy kis felfrissítés az előzményekről, az itt találja. (Ne feledje senki, az LDA, STA, stb utasítások előtti számokat a gép az enter leütésére magától kitölti, nekünk csak a parancsok begépelésével kell foglalkozni!)
A programunk, ami most a rövidség miatt csak egy HELLO-t ír ki:
Nézzük mi történik. Az A regiszterbe beteszünk egy számot, a 8-ast, ami a mi H betűnk lesz majd (LDA #$08). Aztán az A tartalmát kiírjuk a $0400-as címre (STA $0400), ami a képernyő bal felső sarka.
Ezt a logikát követve végigmegyünk a HELLO betűin, de itt már bele tudunk vinni egy apró "optimalizálást". Mivel L betűből kettő is van, felesleges lenne azt újra beraknunk másodjára az A regiszterbe. Mivel a tárolt érték ott marad amíg meg nem változtatjuk, így egyszerűen csak egymás után kiírjuk a $0402 és a $0403 címekre. Máris spóroltunk egy LDA-t, ami 2 byteot foglalna feleslegesen. (Na meg ugye azt valakinek be is kell gépelni...)
RTS - ReTurn from Subroutine
Máris tanultunk egy újabb utasítást. Most nem megyünk bele, mi a szubrutin, egyelőre elégedjünk meg annyival, hogy ez az utasítás visszaadja a vezérlést a monitornak, vagyis szabályosan befejeződik a programunk. Nem kell az ESC+PageUp-pal erőszakosan kilépnünk.
Lássuk mit csinál, töröljük a képernyőt (Shift+Home), üssünk pár üres entert, majd mehet a futtatás a G 1000 -rel.
És valóban megjelent a HELLO a képernyő tetején!
Rendben, most írjuk át a programunkat úgy, hogy a HELLO felirat piros legyen. Most vannak, akik feljajdulnak, mert látják maguk előtt a rengeteg LDA, meg STA sort. Sajnos igazuk van :D
Futtassuk, valóban működik:
Itt valószínűleg már sokan elkezdtek vakarózni, és gondolkodni azon, hogy nagyon elkelnének már azért ide valamiféle ciklusok. És nagyon is jól gondolják!
A ciklus
A ciklus az általában valamiféle ismétlődő feladat elvégzésére jó. Esetünkben a rengeteg STA $D80x sort kiválthatnánk egy ciklussal, ami szépen elszámol $D800-tól $D804-ig. Nem kellene annyit gépelni, kényelmesebb, és szebb is. A számolást valahogy nyilván kell tartani, hogy tudja a program, hogy mettől meddig kell haladnia. Erre való a ciklusváltozó. A ciklusváltozó értéke esetünkben 0-tól 4-ig fog számolni. De nézzük, hogy működik ez a gyakorlatban.
X és Y regiszter
Mivel eddig csak az A-t használtuk, de még van 2 szabad regiszterünk, amik jelen esetben tökéletesek lennének ciklusváltozónak.
Az LDA-hoz teljesen hasonlóan, ezeknek a regisztereknek az értékét beállíthatjuk az LDX, és LDY utasításokkal. Logikusan így az STX és STY ugyanarra képes, mint az STA.
Valamint az értéküket könnyedén
- növelhetjük az INX, INY (INcrement X , Y)
- csökkenthetjük a DEX, DEY (DEcrement X, Y)
utasításokkal.
Az aktuális értéküket összehasonlíthatjuk egy általunk megadott értékkel a CPX, CPY (ComPare X, Y) parancsokkal.
Fogjuk meg a szín beállító részt, és írjunk belőle egy ciklust!
Végiggondolva, hogy mit is szeretnénk (félkövérrel kiemelve az, amit már tudunk):
- állítsuk be az A-t piros színre - LDA #$02
- állítsuk be az X-et 0-ra - LDX #$00
- tároljuk az A-t a $D800 + X-edik memóriacímen - valami STA ...?...
- növeljük az X-et - INX
- nézzük meg, vajon X már nagyobb-e mint 4? - CPX #$05 (ugye 5 > 4 )
- ha nem nagyobb, akkor ugorjunk vissza az STA-ra - hogyan ???
Mint látszik, összesen 2 lépéssel nem vagyunk tisztában, kezdjük az egyszerűbbel.
Az utasításkészletben számtalan olyan utasítás létezik, amelynek a paramétereit többféleképpen is megadhatjuk, ilyenek az STA és az LDA parancsok is. (Az STX, STY, LDX, LDY jóval korlátozottabbak sajna)
Az egyik fajta ilyen megadási mód, amit eddig is használtunk, az abszolút címzés, vagyis STA $D800.
Ám de használhatunk abszolút indexelt címzési módot is, ami azt jelenti, hogy a megadott címhez mindig hozzáadódik az X, vagy az Y regiszter tartalma. Formája esetünkben:
STA $D800,X
Ha például X=3, akkor az STA $D800,X a $D803-as címre fog hivatkozni. Értelemszerűen ahogy növeljük az X értékét, a programunk szépen végig fog lépdelni a megadott memóriacímeken.
De vajon hogyan ugrunk vissza a ciklus elejére, ha X = 5?
Ehhez érdemes megnéznünk, hogyan működik a CPX parancs. (És a CPY is) Valójában semmi mágia nem történik, csak a proci az X regiszterből kivonja az általunk megadott számot. Ha ez a két szám egyenlő, akkor a végeredmény ugye pont nulla lesz, ami nekünk most nagyon jó hír.
Még a bemelegítés résznél volt szó a processzor státuszregiszteréről. Sok időt nem vesztegettünk rá, csupán annyit tudunk, hogy egyes bitjei bizonyos műveletek hatására megváltoznak. Nézzük meg ezt most egy kicsit közelebbről:
Elővéve a jól ismert képet, ez bizony megmutatja nekünk a processzor regisztereinek aktuális tartalmát. Az ADDR a PC (program counter), utána az A, X, Y, Stack pointer, a 01-et még hagyjuk, a végén az a sok betű pedig maga a státusz regiszter 8 bitje. Ezek közül minket most csak a Z (Zero) bit érdekel.
Ennek a bitnek az értéke minden egyes összeadásnál, vagy kivonásnál automatikusan beállítódik. Ha az eredmény nulla, a Zero bit értéke 1 lesz.
Mint fentebb említettem, a CPX #$05 megfogja az X-et, és kivonja belőle az 5-öt. Ha X=5, akkor az eredmény nulla lesz, tehát a státuszregiszter Z bitje 1-re vált.
És teljesen véletlenül van nekünk egy, a JMP-hez hasonló utasításunk, ami csak akkor ugrik a megadott memóriacímre, ha a Z bit = 1, vagyis az eredmény nulla. Ez a
BEQ - Branch if EQual
Na de várjunk csak, nekünk pont fordítva kellene, mert akkor akarunk visszaugrani, ha X még nem érte el az 5-öt. Erre való a
BNE - Branch if Not Equal
ami csak akkor ugrik, ha Z bit = 0.
Hú, ennyi alapozás után lássuk az átírt programunkat, ami $1019-től változott.
Remélhetőleg ennyi háttérinfóval ez a pár sor már értelmezhető. Akinek zsong az agya a sok infótól, az ne szégyelljen visszatekerni, és átnyalni újra. Az assembly viszonylagos nyakatekertségét az adja, hogy nagyon apró téglákból kell építkeznünk.
Aki figyel a részletekre, annak feltűnhetett, hogy a BNE $101B csak 2 byte-ot foglal a memóriából, és nem az elvárt 3-at, mint pl az STA soroknál. Ennek oka, hogy a BNE és BEQ relatív címzést használ, vagyis 1 byteon tárolódik, hogy hány byte-nyit kell előre, vagy épp hátra ugrani a kódban. Szerencsénkre az assembler kiszámolta nekünk, hogy a $101B az pont 8 byte-al van hátrébb.
$FF - $08 = $F8
Ha az érték pl $05 lenne, akkor előre ugranánk.
Mindezt csak érdekességképp említem, az okos assembler miatt nekünk nem kell semmiféle számolással törődnünk.
Egy kis nyalánkság levezetésképpen a végére. Ha tegyük fel kiírjuk a ciklusunk utasításainak teljes nevét a rövidítések helyett, akkor ezt kapjuk:
- Load A #$02
- Load X #$00
- Store A $D800,X
- Increment X
- Compare X #$05
- Branch if not equal $101B
Nem meglepő módon, ezek tökugyanazok a lépések, amiket fentebb leírtam, csupán angolul. Gyakorlatilag egy az egyben fordítható volt a magyar nyelvű algoritmusunk angolra, onnan pedig átírható direkt assemblyre. Ami pedig könnyedén átalakítható akár gépi kódra is, a parancsoknak a megfelelő számokkal való helyettesítésével.
Így már közös nyelvet beszélünk mi emberek, és a processzor.
Ám további szuper programokkal még jobban megérthetjük egymást.
A bejegyzés trackback címe:
Kommentek:
A hozzászólások a vonatkozó jogszabályok értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a Felhasználási feltételekben és az adatvédelmi tájékoztatóban.