Képernyőtörlés

Valószínűleg már kezdenek unalmassá válni a hellók, és az A betűk, csináljunk valami izgalmasabbat!

Van már működő Turbo Assemblerünk, így mindenki töltse be, indítsa el, és gépelje be a lenti kis szösszenetet.

010-code11.jpg

Miután mindenki kiszörnyülködte magát, hogy mi is ez, lássuk lépésenként.

  • LDA, LDX ... Az A legyen nulla, és X legyen $FF. Világos.
  • STA ... a (képernyőram kezdete + X) -edik byteba írjuk ki ezt a nullát. Ahem, semmi extra, megjelenik majd ott egy karakter, valahol a 7. sorban ( 6*40 + 15 = 255). A nullás karakter az a kukac lesz -> @
  • DEX ... Csökkentsük az X értékét. Ez is oké...
  • BNE, vagyis ugrás vissza a "torol" címkéhez, ami majd megint kiír egy karaktert, megint csökkent, és így tovább

 

De honnan tudja a BNE, hogy most neki mikor is kell ugrania, és ezt meddig csinálja? Vegyük át újra ezt a 2 utasítást.

  • A BNE akkor ugrik, ha a státuszregiszter Z (Zero) bitje 0
  • A BEQ akkor ugrik, ha a státuszregiszter Z (Zero) bitje 1

Az előző ciklusainknál a CPX parancs az összehasonlításnál azt csinálta, hogy kivonta X-ből a megadott számot. Ha X = szám, akkor a kivonás eredménye nulla. Ha valamilyen aritmetikai művelet eredménye nulla, akkor a proci automatikusan beállítja a státuszregiszter Z (Zero) bitjét 1-re, hogy ezzel jelezzen nekünk.

Hátha így könnyebb:

CPX #<szam> ==> if ( X == szam ) then Z=1 else Z=0

Ezt a státuszbit állítgatós dolgot minden számítási művelet elvégzi, így a DEX is. Hiszen mit csinál a DEX? Kivon egyet az X regiszter tartalmából, és azt visszateszi X-be.

Ha ennek a műveletnek az eredménye nulla... akkor a Z bit 1 re állítódik!

Tehát egészen addig ugrálunk vissza, amíg a Z bit 0. Ha 1 lesz az értéke, akkor az azt jelenti, hogy az X regiszter jelenlegi tartalma nulla. Így hát nem ugrunk vissza, és a következő utasításnál folytatódik a program végrehajtása, ami egy RTS. Vagyis kilépünk.

Miért jó ez nekünk? Mert ha $FF-től megyünk lefelé $00-ig, akkor megspórolunk egy összehasonlítást, egy CPX-et. Kisebb kód, boldogabb kóder. Meg azért, hogy lássunk ilyet is. ;)

Na nézzük, mit művel a programunk.

010-code2.jpg

Nem túl szép, de legalább (majdnem) a várt eredményt hozta. A program lefutott, aztán a BASIC parancsértelmező persze belerondított a kimenetünkbe a hülye "ready"-jével, de ezzel mi most nem törődünk.

Viszont van egy kis bibi, a bal felső sarokba már nem jutott a kukacból. Hogyan is lehet ez?

Vegyük át szép sorban. Tegyük fel, X folyamatosan csökken és most már X=1:

  • Az STA kidobja $0400+1 -re a kukacot
  • Csökkentjük X-et => 0 lesz
  • A BNE így már nem ugrik vissza, és a programunk végetér

Nincs STA, ami kirakná $0400-ra a kukacot. Nosza, gondolhatnánk, rakjunk a BNE után egy STA $0400-at, és probléma megoldva. Az ötlet nem rossz, de az még egy plusz utasítás. Lehet ezt rövidebben is.

8 bites regiszterek

Sajnos egy kicsit bele kell bonyolódni a bináris számábrázolás mélységeibe. Vegyünk pár 10-es számrendszerbeli számot és nézzük meg bináris megfelelőjüket

0 00000000
1 00000001
2 00000010
3 00000011
... ...
255 11111111

 

8 biten pont ennyi számot vagyunk képesek tárolni. 0 és 255 között. Mi történik, ha a 255-höz hozzáadunk még 1-et? 256 lesz az eredmény, ami binárisan:

... ...
255    11111111
256 1 00000000

 

Hoppá, 9 bitre lenne szükségünk hozzá, de nekünk csak 8 darab van. És mi történt a meglévő 8-al? Látható, mind nulla lett, a 9. bitet pedig mivel nem tudjuk hova eltárolni, az gyakorlatilag elveszett. (Esetünkben most épp)

Így a 255 + 1 összeadás ereménye túlcsordul (overflow), és 0 eredményt ad. Hasonló módon a 0 - 1 kivonás pedig alulcsordul, és 255 lesz az eredmény.

A kivonást nem vesszük most itt át részletesen, mert feleslegesen bonyolítanánk az életünket.

Ez a gyakorlatban azt jelenti, hogy egy regiszter, vagy memóriacím tartalmát egy sima összeadással, vagy kivonással folyamatosan "körbe-körbe" tudjuk pörgetni 0-255 között. Nem akad ki, nem kapunk hibaüzenetet, csak a számok alul, vagy felülcsordulnak.

Ezt a tulajdonságukat kihasználva, nézzük meg mi történik, ha módosítjuk a programunkat, és átírjuk a kezdő

LDX #$FF -et LDX #$00-ra?

010-code1.jpg

Minden más maradt a régiben. Elvileg most úgy indul a programunk, hogy X=0, vagyis az STA kiírja $0400-ra a kukacot. Hopp, ez az egy maradt ki az előbb!

Aztán a DEX csökkenti X-et, és a fentiek szerint alulcsordul, vagyis most X=$FF. A BNE visszaugrik a ciklus elejére, hiszen X nem nulla (hanem $FF).  Innentől ugyanúgy fut a ciklusunk, mint mikor $FF-ről indítottuk.

010-code3.jpg

És kaptunk kereken 256 darab kukacot. (Aki szorgalmas, piros pontért megszámolhatja) Nagyszerű. 

De mi is a cél? Mit akarunk ezzel elérni? Én személy szerint szeretném feltölteni az egész képernyőt kukacokkal. De van egy kis bibi.

A képernyő 40x25 = 1000 byte, mi pedig csak 0-255-ig tudunk elszámolni. Kellene valami trükk.

Jelenleg $0400-$04FF között töltöttük fel a kukacokkal a memóriát. Ahhoz hogy $0500-tól folytassuk, csinálhatunk akár egy ilyet is:

010-code4.jpg

Hiszen semmi sem tiltja, hogy a cikluson belül egyszerre írjuk ki $0400+X -re, és $0500+X-re a kukacot. Valamint az assembler ismeri a matematikai műveleteket, majd szépen kiszámolja, hogy mennyi az annyi.

Futtassuk:

010-code5.jpg

Az eredmény jól láthatóan siker. $0400-$05FF-ig csak kukac, pontosan 2x256 darab. Ezen felbuzdulva adjunk hozzá még két sort a programunkhoz.

010-code6.jpg

Így már a ciklusban a képernyőre egyszerre 4 helyen írjuk ki a kukacot, majd lépdelünk visszafelé.

A végeredmény teljesen lenyűgöző, mindenhol kukac!

010-code7.jpg

Itt ismét egy kis kitérő. Azzal már tisztában vagyunk, hogy $0400-tól van a képernyő ram. Azt viszont még nem tudjuk, hogy $0800-tól kezdődik a BASIC RAM, ahol a BASIC programok tárolódnak.

Ha kiszámoljuk, látható, hogy mi most pont 4x256 vagyis 1024 byteot írtunk a képernyő memória kezdetétől. Így a legutolsó byte, ahova kiírtuk a kukacot az a $07FF.

Viszont a képernyő ram az csak 1000 byte. Utána következik egy 24 byteos "lyuk", és ezután kezdődik a BASIC RAM. Viszont ebből a "lyuk"ból az utolsó 8 byteot a grafikus chip felhasználja bizonyos célokra. Esetünkben ez most nem jelent gondot, mert még nem használtunk olyan funkciókat, amik elromolhatnának emiatt.

De érdemes észben tartani, hogy a videoram kereken 1000 byte, és 1000-nél több karaktert nem érdemes kiírnunk, ha nem szeretnénk meglepetéseket :)

Teli van hát a képernyőnk, jaj de csodás. Viszont ez nem túl izgalmas, teleírtuk kukaccal. Mi van akkor ha más karakterekkel szeretnénk telepakolni? A válasz egyszerű, módosítjuk az első sorban az LDA utáni értéket.

Nyugodtan próbálkozzunk vele, írjuk át bármilyen tetszőleges számra, érdemes kipróbálni mi történik, ha $80-nál nagyobb értéket írunk be. :)

Csavarjunk egyet a történeten, írjuk át úgy a programot, hogy a kiírásra szánt karakter kódját egy memóriacímről olvassa be.

Csakúgy, ahogyan az STA, az LDA is képes abszolút címzésre, vagyis az LDA $xxxx egy adott memóriacím tartalmát tölti be az A-ba.

Módosítsuk a programunkat az alábbiak szerint:

010-code88.jpg

Mi is változott?

A program végén megadtunk egy "karakter" címkét, mögötte pedig egyetlen byte-ot "kézzel". A ".byte" egy megadott számot tárol majd a memóriában. Jelenleg a $01-et, pont az RTS utáni címen. Nem kell aggódnunk miatta, hiszen a programunk futása az RTS-nél mindig végetér, így nem futhat "zátonyra" a végrehajtás.

Az első sorban az LDA #$00 helyett pedig most egy LDA $memóriacím van valójában, ami a "karakter"-re mutat. Ahogyan a programunk növekszik méretben, a "karakter" mindig az RTS utáni byte-ot fogja jelenteni. Szerencsére nekünk a számokkal most sem kell törődnünk, mindent intéz az assembler.

Lefuttatva szép, A-kkal teleírt képernyőt kapunk. Itt szintén eljátszhatunk, hogy átírogatjuk a .byte utáni értéket. Az eredmény ugyanaz lesz, mint előzőleg a direkt LDA paraméter változtatással.

Igen ám, de itt már belevihetünk egy kis trükközést, hogy mókásabb legyen a dolog:

010-code99.jpg

A program elejéhez adjunk hozzá egy "eleje" címkét, így később vissza tudunk ugrani ide.

Az RTS-t cseréljuk ki az INC, és a JMP utasításokra.

INC $xxxx - Increment

Az INC parancs nagyon egyszerű, a megadott memóriacím tartalmát növeli 1-el. (Párja a DEC pedig csökkenteni tud)

Ez most a "karakter" által mutatott byte-unk memóriacíme, aminek az értékét megnöveljük. Majd a JMP visszaugrik a program elejére. Ahol az LDA beolvassa A-ba a memóriacím tartalmát, és megy minden a megszokott módon.

Mivel nincs RTS, már megint van egy végtelen ciklusunk. PageUp-pal ugyanúgy visszatérhetünk az assemblerbe.

A programunk egy érdekes, villódzó képernyőt produkál, ahol a karakterek pörögnek szépen sorban egymás után.

010-code999.jpg

Ha kireseteljük a gépet Alt+F9-el, a memória tartalma nem törlődik, így nyugodtan megleshetjük a lefordított programunkat a monitorprogrammal.

010-monitor2.jpg

Úgy tűnik az assembler mindent jól csinált. Szerencsétlenségemre mikor megnyomtam a resetet, a "karakter" értéke éppen $4C volt, ami pont a JMP gépi kódú megfelelője. Így a disassembler megpróbálja JMP-ként kiírni. Természetesen a mögötte lévő 2 byte az $A7 és a $50 már csak valami maradék szemét a memóriából. 

Végszóként

Lehet néhányan elgondolkodtak azon, hogy miért nem növeltük az A értékét, pl az INX utasításhoz hasonló módon, ami az X-et növeli 1-el.

Mert nem lehet.

Sajnos nincs ilyen parancs, ami az A-t növeli 1-el, vagy csökkenti. Össze lehet adatni az A tartalmát pl egy megadott memóriacímmel. (Ahol mondjuk egy 1-es értéket tárolunk) De egyszerűbbnek éreztem ezen a módon megmutatni :)

Nem meglepő módon például ilyen sem létezik: LDX $xxxx,A vagy STX $xxxx,A

 

A bejegyzés trackback címe:

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

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.

tájbor1001110 2021.12.07. 23:49:55

Hello, lesz folytatás? (:

tájbor1001110 2022.09.15. 11:20:22

Végre nekiálltam az assembly-nek, az itt alkalmazott környezetben (VICE, TASM74).
Megpróbáltam a saját, számomra 'egyértelműbbnek' tűnő logikával, miszerint nem csökkentem, hanem növelem az X értékét:
(Nyilván van valamilyen előnye az X csökkentésének, gyorsabb vagy akármi miatt jobb.)

init
lda char
ldx #$00
...
sta scr,x
sta scr+$0100,x
sta scr+$0200,x
sta scr+$0300,x ;ezt már nem bírja el...
inx
bne cycle
inc char
jmp init
...

A kód lefut, egészen addig amíg hozzá nem adom az scr+$0300,x sort!
A fordítás nem dob hibát, gondoltam az utolsó 255byte írásakor már valamit csak felülírok a memóriában... Aztán bepötyögtem a Te kódodat (ugyanazokkal a label-ekkel/konstansokkal) és ugyanaz az eredmény, a kód lefordul de nem fut le, a felső sor valahanyadik oszlopába rak egy "!" karaktert és ennyi...
Hol lehet a bukfenc?

tájbor1001110 2022.09.15. 11:23:54

Csak egy megjegyzés a fenti kommentemhez:
"A fordítás nem dob hibát..."

Felesleges megjegyzés. Nyilván nem, hiszen szintaktikailag nincs a kódban hiba. (:

Más: Nem lesz folytatása az assembly tutorialnak?

tájbor1001110 2022.09.15. 11:32:48

Ja még valami, a vason (C64-en) sem fut le a kód az utolsó 255byte képernyő memóriába írásakor... (sta $0700,x)

Dey · http://c64rulez.blog.hu 2022.09.15. 11:47:33

@tájbor1001110: Mi a fene. Érdekes :O
Előszedek egy emut, és ránézek majd.

Hát... Lehet folytatás, ha ekkora az igény rá :)

tájbor1001110 2022.09.15. 13:18:56

Köszi. (:
Persze, van igény. Szvsz ez egy jó leírás volt. Tetszett az is pl. hogy nem CBM studio-t meg kick assemblert használsz. Nem kell semmi csili-vili, a nyers vason szeretném megtanulni aztán majd később lehet lazázni. Jó lenne pl. látni azt is hogy hogyan lehet lebegőpontos számokat kezelni (nem a basic rutinjával), persze ezt csak szorgalminak, valamikor a végén.

tájbor1001110 2022.09.16. 08:56:22

Ne haragudj, aludtam rá egyet és észrevettem hogy a "*=$1000" direktívából hiányzik a '$'. (((: Bocsi!

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