Az első assembly programunk

Eljött végre a pillanat, amit mindenki annyira várt! Rendhagyó módon mi most nem egy HELLO WORLD-el kezdünk, hanem valami sokkal látványosabbal. Ami nem mellesleg sokkal egyszerűbb is, mint egy Hello World, valamint segít megalapozni a továbbiakat.

Izzítsuk be az emut, tehénkedjünk az Action Replay menüjében az F7-re (fastload), és írjuk be az alábbi parancsot:

POKE 53280,13

006-poke.jpg

Az enter leütése után valami csoda történik, és a kék keret világoszöldre vált. Miféle gonosz mágia lehet ez? És mi a fene az a POKE?

A POKE működése pofonegyszerű, a megadott memóriacímre beírja a megadott számot. Esetünkben a cím a memória 53280-dik byteja, a beleírt érték pedig a 13. Így tudjuk könnyedén megváltoztatni a memória bármelyik területének a tartalmát.

Honnan jönnek ezek a számok és mit jelentek?

Gépünkben van egy grafikus chip, a VIC-II, ami a látott kép megjelenítését végzi. Kipakolja a képernyőre a betűket, színeket, és mindent. Egyszerűsége ellenére mégis nagyszerű, és viszonylag könnyen vezérelhető. Gondolom már sejthető, hogy ezzel a POKE-al valamiféle utasítást adtunk neki az előbb.

Ez a chip is rendelkezik regiszterekkel, amiken keresztül a működését lehet befolyásolni. Elég sok van neki, szám szerint 46 darab. Ezek a regiszterek csúnya szóval élve, "be vannak fűzve" a C64 memóriájába, vagyis egy adott memóriacím írásával/olvasásával, egy adott regisztert tudjuk piszkálni. A regiszterek írásával/olvasásával a chip által generált képet tudjuk kedvünk szerint manipulálni. Vagyis itt már kezdünk belecsúszni a korábban említett hardverközeliségbe, hiszen közvetlenül a vasat piszkáljuk. 

Ez a 46 regiszter a memóriában az 53248 és 53294 közötti memóriaterület. A fent "megpókolt" 53280 az bizony pont a keret színéért felelős regiszter, amibe benyomtuk a 13-as számot, ami a világoszöldet jelenti.

A VIC sok tekintetben rettenetesen faék, és kőbuta. Agyatlanul minden egyes képernyőfrissítéskor az itt tárolt színkódnak megfelelő színnel fogja kirajzolni a keretet. Ha beírunk egy másik értéket ugyanide, akkor a számnak megfelelő színre vált. Nem kérdez, nem ellenőriz, nem rinyál, és nem dob hibaüzenetet semmire.

Don't panic!

Nem kell beszarni, ez a sok hülye szám elsőre ijesztő lehet, de természetesen, mint minden rendes programozónak, nekünk is vannak referenciáink. A regiszterek listája, és a színek számkódjai is mind megtalálhatóak a VIC-II Wikipédia oldalán. Meg még persze 1000 más helyen a neten :)

A későbbiekben természetesen elkezdünk majd használni mindenféle emberközelibb fejlesztőeszközöket, és egyebeket, amik majd megkönnyítik a dolgunkat, de szeretném az alapoknál kezdeni, hogy lehetőleg mindenki értse is azt, amit csinál. :)

Aki megnézte a linkelt Wiki oldalt, annak feltűnhetett, hogy sehol sincs 53280, viszont a 32. regiszternél vagyis "border color"-nál egy D020 áll.

006-d020.jpg

Nem megyek most bele a kettes (bináris), és tizenhatos (hexadecimális) számrendszer mélységeibe, akit érdekel, talál rengeteg infót róla a neten. Elégedjünk meg most annyival, hogy a BASIC mindig a jól ismert 10-es számrendszert használja, mi viszont a 16-ost fogjuk majd javarészt a jövőben. A doksik is mind ezt használják, szóval elkezdünk átszokni rá. Aki szeretné, kipróbálhatja ezen az átváltó oldalon, hogy a D020 hexadecimális szám, az biza 53280 a 10-es (decimális) számrendszerben. A későbbiekben a hexadecimális számokat dollárjellel fogjuk jelölni, vagyis $D020.

Ha már eddig eljutottunk, jöjjön egy kis játék, állítsuk át a belső kék rész színét is, mondjuk sötétzöldre. A wiki oldalon kikereshetjük a "Background color 0" regiszter címét, ami $D021, valamint a sötétzöld számkódját, ami 5.

Mivel még BASIC-nél tartunk, a hexa $D021 decimális megfelelője az 53281, az 5 meg értelemszerűen ugye simán 5 😄

A végeredmény ez lesz:

006-poke2.jpg

Hoppáhoppá, vezéreljük a grafikus chipet! Közvetlenül a vasat, a hardvert! Hát nem nagyszerű? Természetesen ennél jóval menőbb dolgokra is képes a masina, de most még ne rohanjunk annyira előre. Egy elefántot is csak falatonként lehet megenni 😉

Próbáljuk ki a következő BASIC programot. (Én most kireseteltem a gépet Alt+F9-el a screenshot kedvéért, és amiatt kék újra minden)

006-border.jpg

Jelenleg már megfelelő tudással rendelkezik mindenki, hogy magától rájöjjön, hogy itt mi bizony gyorsan változtatjuk majd a keret színét a világoszöld (13), és a világoskék (14) között. A RUN beírása után mindenki egy standard villogásra számít majd...

ÉS NEM! Az eredmény sokkal meglepőbb!

006-border2.jpg

Érdekes módon valamiféle fura, mozgó csíkozást kapunk. Ennek oka, hogy a VIC másodpercenként 50szer rajzolja ki a teljes képet, elkezdve a bal felső sarokból, és szépen soronként lefelé végigmegy. Mivel a BASIC programunk gyorsabb, mint amennyi idő kell egy ilyen teljes kép kirajzolásához, a rajzolás közben valahol a képernyő közepén már átváltja a keretszínt, majd később pedig vissza. Mire végez a teljes kép frissítésével mi menet közben többször is megváltoztatjuk a színt, amivel dolgoznia kell.

Gondolom ismerős PC-s játékokból a kikapcsolt VSync-nél jelentkező screen tearing. Az elv ugyanaz ott is. A GPU elkezdi kirajzolni a monitorra a képet, de az a rajzolás közben valahol megváltozik, és az új, következő frame-et rajzolja tovább, így a képben keletkezik egy szemmel látható törés.

A grafikus chipünk "buta", őt nem érdekli, hogy van-e értelme az adott feladatnak, csupán végrehajtja. Ha éppen a képernyő kirajzolásának a közepén váltjuk át a keret színét... Akkor ő bizony gondolkodás nélkül rajzolja tovább, immár az új színnel. Aki látott már C64-et, annak ismerős lehet ez a keret villogás, csinálunk is majd belőle érdekes dolgokat 😉

Az igazán vájtszeműek még azt is kiszúrhatják a képemen, hogy éppen hol tartott a VIC a kép kirajzolásával, mikor éppen átváltott a program kék színről zöldre. (Jobb szélen, a kereten van egy "törés")

006-border22.jpg

 

Ha megállítjuk a programot ESC-el, akkor viszont a villogás abbamarad, és vagy kék, vagy zöld keretet kapunk, mivel az adott regiszterben lévő számérték már nem változik többé. Igy a chipünk mindig ugyanazzal a színnel rajzol majd. Másodpercenként 50-szer.

Hol van már az assembly???

Oké, rendben, jöjjön hát a lényegi rész. 

Az Action Replay-ünknek egy nagyon jó kis bővítménye, a "Gépi kódú monitor"-nak, vagy csak simán monitornak nevezett program. Ezzel megnézhetjük, és átírhatjuk direktben a memória bármilyen szegletét. Emellett van benne egy egyszerű assembler is, ami nekünk most nagyon jól jön. (Igen, van a VICE-ban is beépített monitor, de attól hülyét kapok, sry)

Írjuk hát be: MON és csapjunk az enterre: (Én már megint reseteltem, hogy átláthatóbb legyen a dolog a képen, de természetesen nem muszáj)

006-mon.jpg

Indításkor kiír mindenfélét, de mi ezzel most nem foglalkozunk.

Írjuk meg hát az első assembly programunkat, ami pontosan a fenti képernyővillogtató lesz. Először pötyögjük be, aztán rátérünk, hogy mit és hogyan csinál. (Innentől minden szám hexadecimális, akár áll előtte dollárjel, akár nem!)

Ha beírjuk, hogy "A 1000 LDA #$0D" és leenterezzük, valami láthatóan történik:

006-asm01.jpg

Hát ez átírta a sorunkat! Majd a következő sor elejére kiírta, hogy 1002:

006-asm02.jpg

Mi is történt? Az "A" (mint assemble) parancs mondja meg a monitor programnak, hogy mi most bizony assembly kódot fogunk beírni neki. Példánkban az 1000 azt a memóriacímet jelöli, ahová el szeretnénk helyezni a parancsunkat. (Ez már hexa 1000 vagyis $1000) Természetesen teljesen más címet is választhattam volna, hiszen van 64kb szabad RAMunk 😉

Sejthetően az LDA #$0D pedig már végre valahára valamilyen assembly kód lesz...

Pontosan így van, az LDA (LoaD to A) utasítás be tud tenni egy byte-ot a processzor A regiszterébe. (Itt esetleg vissza lehet ugrani az előző postokhoz, hogy mi is az a regiszter) Jelen esetben belerakunk egy hexa $0D értéket (ami decimálisan 13, a világoszöld). A kettőskeresszttel azt mondjuk meg, hogy mi itt most direktben kézzel megadunk egy konkrét számot. (Lesznek más jelölések is később, például a memória egy tetszőleges helyéről is beolvashatunk akár egy byte-ot.)

Mikor entert nyomunk, akkor viszont a monitor (értsd: monitorprogram) átszerkeszti a sort, és belekerül egy "A9 0D" a parancs elé. Ez valójában az utasításunk lefordított, gépi kódú megfelelője. Ugye mindenki emlékszik, a processzor csak és kizárólag számokat tud értelmezni. Az A9 valójában az "LDA #", a mögötte álló "0D" pedig az érték, amit megadtunk.

Az assembler megkönnyíti az életünket, mert nem kell hülye számokat megjegyeznünk. Valószínűleg az A9-et holnapra mindenki elfelejti, viszont az LDA-t ha 2 nap múlva meglátod, akkor be fog ugrani róla, hogy micsoda.

És mivel extra jó fej, az assembly parancsunk értelmezése, és tárolása után nagyon előzékenyen kiírja nekünk a következő sor elejére az "A 1002"-t, vagyis azt memóriacímet, ahová a következő utasítást beírhatjuk. Nem kell számolgatnunk, hogy az LDA #$0D az pont 2 byte hosszú, ezt mind intézi helyettünk.

Pötyögjük be a maradékot, és nézzük meg egyben, majd értelmezzük: (Ha valamit elgépelünk, és az assembler nem érti, egy ?-et fog kiírni. Ilyenkor nyugodtan gépeljük be újra a parancsot a következő sorba, vagy menjünk fel a kurzorral, javítsuk, és csatt rá egy enter. Ugye fullscreen editorunk van, pont mint BASIC-ben ugyanúgy működik a programsorok módosítása 😄😄

006-asm03.jpg

Itt már újabb utasításokat is felfedezhetünk, és szépen látható, hogy ezek milyen módon tárolódnak a memóriában.

Az STA (STore A) kb az LDA fordítottja, az A regiszter tartalmát kiírja valahova. Esetünkben most egy megadott konkrét memóriacímre, ami a már ismerős $D020. Tehát a keret világoszöldre vált majd.

Az alatta lévő két sor ugyanezt csinálja, beteszünk A-ba egy $0E értéket (ami 14 ugye-kék), és azt kiírjuk a $D020-as címre. A keret ettől világoskék lesz.

Az STA utasítások sorában megfigyelhető, hogy itt bizony már 3 szám keletkezett az utasítás előtt. Igen, a "8D" az STA kódja, a "20 D0" pedig a D020, csak fordítva. A C64 a memóriacímeket mindig ilyen fordított sorrendben tárolja, ezt hívjuk alsó/felső byte alaknak, ez sokszor előkerül majd még.

Az utolsó utasítást nem lesz nehéz kikövetkeztetni, ez a JMP (JuMP), vagyis ugyanaz, mint a BASIC-ben a GOTO. Itt természetesen nem programsort, hanem egy direkt memóriacímet adunk meg, hogy hová ugorjon a program. Mi bizony visszaugrunk a legelejére, vagyis a $1000-es memóriacímre. És itt minden kezdődik elölről.

A gépi kódot, és az assembly utasításokat összevetve könnyen észrevehető, hogy vannak parancsok, amik 2 byte, míg vannak amik 3 byte hosszúak. Valójában minden utasítás csak 1 byte, a mögötte álló plusz 1 vagy 2 byte az az utasítás paraméterei. Van utasítás, aminek nincs paramétere, az értelemszerűen csak 1 byte hosszú lesz. Szerencsére ezeket nem kell fejben tartanunk, az assembler tudja helyettünk is. (Ugye milyen kényelmes? 😆😆😆)

Az is jól látszik, hogy az assembly parancsaink mind 3 karakteresek, és általában valamilyen könnyen megjegyezhető szó rövidítései.

Futtassuk!

Ha minden tuti, a kurzor most egy "A 100D" kezdetű sorban villog, az assembler azt hiszi mi még folytatnánk a programot, és várja az utasítást. Ha itt nem adunk meg semmit, csak ráenterezünk, akkor tudja, hogy köszi nem akarunk többet pötyögni, és egy sima üres sort kapunk a kurzorral.

006-asm04.jpg

A programunkat futtatni a "G"-vel tudjuk (Go). Meg kell adnunk egy memóriacímet is, ami ez esetben 1000. Vagyis beírva, hogy "G 1000", a programunknak el kell indulnia.

006-asm05.jpg

Hasonló, de sokkal sűrűbb villogást kapunk, mint a BASIC program esetében, viszont itt már látszik a sebességkülönbség a BASIC és az assembly között. Mivel közvetlenül a processzort programozzuk, mindenféle közvetítő (esetünkben a BASIC parancsértelmező) nélkül, így kb 300-szoros sebességnövekedést értünk el.

BASIC-ben kb fél képernyőnként változott a keret színe, jelenleg pedig minden egyes sorban többször is. Ha tegyük fel, sikerülne úgy beidőzíteni a programunkat, hogy csak minden sor elején változtassa meg a keret színét, akkor érdekes effekteket érhetnénk el, mint pl a klasszikus C64-es "rasterbar"-ok.

006-rasterbar.jpg

Összefoglalva

A processzor csak számokat ért meg. Minden szám egy parancs számára. Ezeket a számokat a memóriából olvassa ki. A számokat beírhatjuk kézzel a memóriába, vagy egy assemblerrel, ami kicsit könnyebb.

Ja igen...

Most mindenki ottmaradt egy villogó képernyővel, ami a végtelenségig ezt csinálja. Hogyan is állítsuk meg?

Az ESC+PageUp lenyomása megfelel az igazi C64 RUN/STOP + RESTORE billentyűkombinációjának. Ha megnyomjuk, az egy megszakítást (interrupt) generál. Előzőleg volt már szó arról, hogy a processzor képes megszakítások kezelésére, vagyis amikor kap egy ilyen megszakításkérelmet, akkor félbehgyja az adott program futását, és valami mást csinál. A monitorprogram ezt a megszakítást átírja saját magára, így a billentyűk lenyomásakor a mi programunk futása megszakad, és a megszakítás által megadott helyen folytatódik, vagyis visszakerülünk a monitorba, egy üres prompttal.

006-restore.jpg

Még néhány apróság

Lehet feltűnt már, hogy néhol monitorként, néhol assemblerként hivatkozok a programra, amit használunk. A monitor az általános összefoglaló neve magának a toolnak, amiben van assembler, disassembler, memory dumper, meg egyéb nyalánkságok.

Ez a monitor program természetesen nem csak assembly -> gépi kód átalakítást tud (assembler), hanem fordítva is, vagyis a disassembler is egyben. Magyarán a memóriából kiolvasott byte-okat értelmezve, elő tudja állítani az eredetileg általunk beírt assembly kódot is, mivel az ugye csak egy szám->3 betűs szöveg összerendelés.

Ezt a D (disassemble) paranccsal tudjuk elővarázsolni. Ha beírjuk, hogy "D 1000 1010", akkor kilistázza az 1000-1010 közötti memóriaterületet nekünk, olvasható formában.

006-disasm.jpg

Itt látható, hogy írhattunk volna nagyobb számot is, mint a 1010, de felesleges lett volna, hiszen a programunk a JMP-nél véget ér, utána pedig már csak a memóriában található "szemét" látszik. Az "FF" például a processzor által nem értelmezhető utasítás, így kérdőjelek jelennek meg. (A 6502/6510 nem rendelkezik 255 különböző utasítással, így némely byte értelmezhetetlen, vagy hivatalosan nem dokumentált működést eredményez) 

Mivel a teljes képernyős szerkesztő mindig mindenhol működik, a kilistázott programunkat a BASIC-hez hasonlóan tudjuk módosítani is. Csak annyit kell tenni, hogy a kurzorral felmegyünk, és az LDA #$0D -t átírjuk LDA #$00 -ra, majd nyomunk egy entert. Azután a másik LDA-nál a 0E-t pedig például 01-re, majd enter.

Futtatva látható, hogy a keret most a fekete (00) és a fehér (01) színek között váltogat.

006-disasm2.jpg

A kilistázott programot, és a képernyőt "görgetni" is tudjuk, ha lemegyünk az utolsó sorba, és nyomkodjuk a lefelé nyilat. Ilyenkor a memória listázása 1010-től folytatódik. Ugyanez igaz a képernyő tetején is. Mindenki próbálja ki, elsőre kicsit fura, de szokható. Valamint nagyon hasznos, hosszab programoknál, amik nem férnek ki egy képernyőre.

006-disasm3.jpg

Az F5 és F7 billentyűkkel a képernyőt automatikusan folyamatosan görgethetjük a megfelelő irányba, pause: kurzor fel, kilépés a görgetésből: kurzor le.

Elsőre ennyi, mindenki emésztgesse, játszogasson, próbálgasson. Eminenseknek szorgalmi: írjuk át a programot, hogy ne a keretet, hanem a háttérszínt váltogassa. ($D021)

Lépjünk tovább, lássuk hogyan tudunk valamit megjeleníteni a képernyőn!

Némi extra adalék: Action Replay monitor parancsok (7.pontnál)

A bejegyzés trackback címe:

https://c64assembly.blog.hu/api/trackback/id/tr2616182122

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.

Nincsenek hozzászólások.

C64 assembly alapok

Friss topikok

  • Heretic83: Sziasztok! Tudnátok ajánlani olyan tudástárt, ahonnan lehet tanulni bővebben az assembly programoz... (2023.07.13. 19:15) Mi az az assembly
  • tájbor1001110: Válaszolva a saját kérdésemre: Az End+o -val lehet 5 szín séma között váltogatni. (2022.09.20. 21:14) Turbo Assembler
  • tájbor1001110: Ne haragudj, aludtam rá egyet és észrevettem hogy a "*=$1000" direktívából hiányzik a '$'. (((: Bo... (2022.09.16. 08:56) Képernyőtörlés

Címkék

süti beállítások módosítása