DPTP motor: ...
DPTP motor: ...
Írjunk játékot SEGA MEGA DRIVE konzolra - 6. VDP használata
Blog
2019-11-06 17:10
don_peter
Írások: 15
VDP avagy Video Display Processor.

Sok angol nyelvű weboldal foglalkozik a VDP témájával, és most jön a csavar: egyik sem magyar. ;) Aki pedig nem érti kellőképpen az angol nyelvet annak ez okozhat kihívást. Ilyen vagyok én is. Ettől független rendelkezem egy fajta akarttal, ami ha beindul és kellőképpen motivált vagyok, hajlamos vagyok addig molyolni felette, ameddig meg nem értem mi, miért is van. No ilyen ez a VDP is. A címből egyértelműen kiderül, hogy ez a Video Display Processor, a SEGA MD dedikált chipje, amely a képi megjelenésért felel. Ebbe tartoznak bele a spriteok, a háttér, előtér és úgy ám-blokk a grafika. Ez az a chip ami azért felel, hogy a TV vagy a monitorunk adjon valami optikai visszajelzést. Valamiért a készítők nagyon túl bonyolították ezt a hardveres elemet, így elég könnyen el lehet veszni az általa használt útvesztőkben.

Mielőtt bele kezdenénk a VDP kivesézésében ejtsünk pár szót a palettákról is mert az előző részben könnyedén átsiklottunk felette és fontos lehet a későbbiekben. A SEGA Mega Drive VDP 9bit-en tárolja a színeket, mind a 3 szín esetében 3-3-3bit mennyiségben. A 3bit így színenként összesen 8 különböző állapotot jelent, vagy is a 3 szín egyben: 8*8*8 = 512 különböző szín. A színeket előre meg kell határoznunk, egy paletta felvételénél maximum 16 színt tudunk megadni, ebből az első általában az átlátszó szín lesz illetve alapértelmezett beállítások alatt a hátteret is az 1-es szín fogja meghatározni. Ezt persze majd át lehet állítani, ha más kell. A VDP memória ezen szakaszát CRAM-nak nevezik és összesen 64 színnek van benne hely. Ezt a 64-et lebontva összesen 4 színpalettát jelet. Ezeket a palettákat szerencsére bármikor a programfutása közben is lehet cserélni, így nem feltétlen jelent korlátot a kevés szabad színmemória. Az előző részben már bemutatott (PALETTA) változó névvel deklaráltunk egy palettát ami 16 különböző szint jelent. Ezeket a színeket, mivel nem fér bele 8bit egységbe, vagy is 1 byte-ba a további 1 bit miatt, 16biten vagy is 2byte (word) szélességben tudjuk csak ábrázolni, ezért lehet az, hogy amikor mondjuk a fehér szint megadjuk, akkor bizony csak így tudjuk ábrázolni: 0x0EEE, ha ezt tovább bonjuk, vagy is bináris alakban írjuk fel a következőket kapjuk: 0000 1110 1110 1110, látható továbbá az is, hogy a bitek 1 bitet jobbra vannak tolva. Tehát ha csak színek bitjeit nézzük akkor: 111 111 111 == fehér szín. No akkor erről most ennyit, de majd amikor az első szöveges üzenetünket akarjuk a képernyőre kiírni még vissza térünk a paletták fontosságára..

Meg kell még említenem a Planes-eket, amelyek nem mások mint a különböző síkok, amelyek egymás felett vagy Sprite-ok esetében prioritásukat beállítva egymás előtt vagy mögött helyezkedhetnek el. Összesen 4 ilyen sík áll a rendelkezésünkre, kettő gördíthető, egy ablak sík ami fix, de a mérete és pozíciója testre szabható és a sprite sík.A gördíthető és ablak síkok képekből álló rácsot jeleníthetnek meg függően attól, hogy milyen VDP beállításokat alkalmazunk. 32x28 vagy 40x28 cellaméretet tudunk beállítani. A két gördülő sík képes pixel, vonalakat vagy vonal csoportokat (8 pixel magas) illetve az egész síkot jobbra vagy balra illetve függőleges irányban fel vagy le görgetni. Az ablak sík még nem teljesen világos a számomra, de ez az a sík amelyre a score bárt szokták fixálni, illetve ez egy olyan ablak, amely az A és B síkokat egyaránt kitakarja, ha használatban van. Pozíciója és méretét be tudjuk állítani, szóval biztosan hasznos lehet, ha jól tudjuk használni. Továbbá fontos még tudni, hogy az A sík takarja a B síkot, így érdemes minden esetben figyelembe venni, hogy melyik síkkal mit is akarunk kezdeni. A sprite sík eléggé jól kezelhető, X és Y koordinátákon képes megjeleníteni a mintákat és függőlegesen és vízszintesen is képes tükrözni. Az említett prioritásokkal lehet szabályozni az egyes sprite-ok kitakarását, itt srpite és sprite egymást kitakaró prioritásáról van szó, semmikép nem összekeverendő a síkok takarásával. Sprite-okról majd még később fogok írni, mert van mit.. ;)

Síkok

Nos akkor jöjjön a VDP címzése és úgy ám-blokk az egész témakör:

A SEGA egy elég homályos és bonyolult bitszerkezetű címzést használ a VDP memóriájának használatához. Gyakorlatilag az egy cím 2 műveletet von össze, az egyik a művelet típusát leíró bitsorozat, a másik pedig a hardveres címzés, amely lehetővé teszi, hogy elérjük magát a VDP-t. Egy ilyen címzés 32bit-el dolgozik, a bitszerkezet a következő képen tevődik össze:

    BBAA AAAA AAAA AAAA 0000 0000 BBBB 00AA

Az A jelöli a célcímet, a B tárolja a művelet típusát, a 0 pedig mindig 0.

Kezdjük a címmel, ezek voltak feljebb az A-val jelölt bitek.

    --DC BA98 7654 3210 ---- ---- ---- --FE

Kapásból látszik, hogy mindösszesen 16biten tárolódik a cím a többi a címzés megadásánál nem játszik, a furcsaság az magában az elrendezésben van. Példa képen, ha mondjuk a a VDP memóriába szeretnénk írni, oda ahol a grafikák (tiles) tárolódnak le, mondjuk az A síkra, akkor a 0xC000 címet kell megadjuk. Ezt a hexa számot alakítsuk át binárisra, hogy jobban prezentálja a bitszerkezet felépítését.

Cím hexában:      C    0    0    0       
Cím binárisan:    1100 0000 0000 0000
Helyi értékek:    FEDC BA98 7654 3210


Majd a fenti mintának (--DC BA98 7654 3210 ---- ---- ---- --FE) megfelelően behelyettesítünk:

    0000 0000 0000 0000 0000 0000 0000 0011

Ezután kell beállítanunk a többi bitet annak megfelelően, hogy mit szeretnénk csinálni. További 6 bit felhasználásával leírhatjuk a következő műveleteket. Emlékeztek? A B-vel jelölteket kell most beállítani:
    (BB-- ---- ---- ---- ---- ---- BBBB ----)


        000000 - VRAM olvasása
    100000 - VRAM írása
    000100 - CRAM olvasása
    110000 - CRAM írása
    001000 - VSRAM olvasása
    101000 - VSRAM írása


Itt a bitsorrend a következő lesz:

    10-- ---- ---- ---- ---- ---- 5432 ----

Nem egy egyértelmű és egyszerű felosztás, de ez van ezt kell szeretni. Ha mondjuk a VRAM címre akarunk írni a következő módon fog kinézni a a bitsorrend: (100000 - VRAM írása)

    01-- ---- ---- ---- ---- ---- 0000 ----

Ha most össze fésüljük a címzést és a művelet típusát, akkor a következőt kapjuk:

Bináris:    0100 0000 0000 0000 0000 0000 0000 0011
Hexa:     4    0    0    0    0    0    0    3

Ezzel azt értük el, hogy jeleztük SEGA-nak, hogy a VDP vezérlő portjára akarunk írni és azon belül is a 0xC000 VRAM címre. Ez a következő utasítással adhatjuk meg egyértelműen:

Utasítás      Adat             Cím
move.l        #0x40000003,     0x00C00004


Ahol a cím vagy is a 0x00C00004 a VDP vezérlőport I/O címe.

Ennél a résznél amúgy a legtöbb külföldi oldal elidőz annak ecsetelésével, hogy miért is kellett ennyire körmönfontan és bonyolult módon ilyen homályos és érthetetlen bitfelosztást képezni. Megvallóm őszintén, hogy engem is zavar kicsit, de ha egyszer az ember rájön, hogy miért és hogyan történnek az ilyen műveletek már kevésbé érdekli a miért, csak az számít, hogy mit tud alkotni ennek ismeretében.

Már csak egy valami maradt a VDP ezen részéből ki, az pedig az adatok írása. Ha már eljutottunk idáig, hogy tudjuk miképpen kell összerakjuk a címzést és kérést a VDP regiszter felé, már csak az adatok feltöltéséről kell gondoskodnunk. A VDP 2 adathosszt tud vagy képes fogadni, byte-os vagy word-ös hosszt, de az esetek többségében nekünk nem csak 1 vagy 2 byte hosszú adathalmazt kell beírnunk, hanem sokkal többet. Erre két lehetőségünk is van, az egyik, hogy olyan programot írunk, amely minden ciklusban manuálisan inkrementál egy regisztert addig ameddig vége nem lesz az adatcsomagnak, vagy és + használjuk a VDP regiszter egy kiegészítését, amely az automatikus inkrementálás névre hallgat. Ez az autoinkrement funkció képes arra, hogy VDP regiszterbe a rendeltetési hely címét automatikusan növeli minden egyes írás után, de ez még kiegészül azzal is, hogy ebben a módban nem csak byte vagy word hosszú adatokat írhatunk ki az adatportra, de akár dupla szó hosszúakat is.  Ezt az autoinkrement regiszter a VDP 15. regisztere, amelyet 2-esbe állíthatunk.

    move.w #0x8F02, 0x00C00004   ; Autoinkrement beállítása 2 byte-osra.

Ezzel a VDP-t ki is veséztük, jöjjön néhány már az előzőkben is csak felületesen taglalt funkció.

Adatok írása VDP-be:

Kezdjük az Paletta feltöltésével, tudod ezek azok, amelyek a színeket tartalmazzák. A 0-ás vagy is az első paletta címe a CRAM területen a 0x0000-án tartózkodik. Tehát a fentebb taglalt bitszerkezettel megoldva megadjuk a VDP-ének, hogy ide szeretnénk adatokat írni.:
1. műveleti típusa 110000
2. CRAM-on belüli cím, amelyre írni akarunk: 0x0000
3. CRAM címre és az írási művelet egyben: 0xC0000003

    move.l #0xC0000003, 0x00C00004 ; Beállítjuk VDP-t a CRAM területének 0x0000-ás címre való írásához

Ahol a cím vagy is a 0x00C00004 a VDP vezérlőport I/O címe.

Ezután, feltételezve, hogy az automatikus increment továbbra is 2 byte-ra van állítva, áthelyezhetjük a paletta adatokat a
VDP 0x00C00000-as adatportjába egy nagy ciklusban:

    lea Palette, a0            ; Betöltjük Palette kezdő címét a0-ás regiszterbe
    move.l #0x07, d0         ; Beállítjuk a ciklus számlálót 7-re
 
    @Loop:
    move.l (a0)+, 0x00C00000 ; Mozgatjuk az adatokat VDP adat portra és inkrementáljuk az adat címet
    dbra d0, @Loop


Ahol a 0x00C00000 cím, a VDP adatportja.

Eljutottunk arra a pontra, hogy valamit alkossunk is ebben a fejezetben. Túl nyers volt az előző és sajna még lesz egy ilyen rész, ugyan is a következőkben azt fogom bemutatni, hogyan írhatunk ki egy szöveget a képernyőre. Ha lelkileg felkészültünk, akkor hajrá.. ;)

Színpaletta és grafikai elemek össze illesztése:
Alapértelmezés szerint a színpalettánk 0. vagy is az első színe határozza meg a hátterünk színét. Fontos, hogy ez csak abban esetben van így, ameddig azt mi meg nem változtatjuk. Ezen kívül figyelnünk kell arra is, hogy mi az első grafikai elem amit betöltünk a VRAM területre, mert az a mintázat lesz alapértelmezetten a hátterünk mintázata, ha ez áttetsző, akkor áttetsző lesz a hátterünk, ha valami grafika, akkor annak a mintának megfelelő mintázatú lesz a hátterünk mintája. Ezt utóbbit nem tudjuk változtatni, tehát az első grafikai elem amit feltöltünk az jobb ha egy üres elem lesz. Majd ezt külön mutatni fogom.

Maradunk az A síknál és erre fogjuk a feliratunkat elkészíteni. Ennek a síknak a címe a 0xC000, bitsorozatunk a következő képen néz ki:

    ABBC DEEE EEEE EEEE

A bit - alacsony vagy magas sík (Ezt még nem tudom, de lehet köze van a síkok prioritásához)
B bit - Színpaletta azonosítója (0, 1, 2 vagy 3)
C bit - Vízszintes átlapolás (0 = alap, 1 = vízszintesen tükrözi)
D bit - Függőleges átlapolás (0 = alap, 1 = függőlegesen tükrözi)
E bit - a rajzolandó minta azonosítója

Ha egy mintát akarunk kirajzolni a képernyőre a 0. vagy is az első színpaletta segítségével, módosítások és manipulálás nélkül, akkor elég egyszerű a dolgunk. Írjuk ki az első mintának a 0. színpaletta 1 színével. Ez a betű egy N betű lesz majd és ha minden jól megy, akkor piros színnel fogja kiírni a képernyőre.
Állítsuk össze a bitsorozatunkat:
1. művelet típusa 100000 (írás)
2. 0xC000 címre írunk (A sík)
3. VDP parancs port 0x40000003

    move.l #0x40000003, 0x00C00004   ; Beállítjuk a VDP a VRAM területének 0xC000 cím írására (A sík)
  move.w #0x0001, 0x00C00000       ; Alacsony sík, 0. paletta, nincs forgatás, 1. grafika (title)


A következőben elkészítjük azt az N betűt, amelyre fentebb hivatkoztam, ez rém egyszerű, a ROM területre kell írjunk adatot, amely a következő egyszerű módon megtehető.:

Betuk:
   dc.l 0x00000000 ; Character 0
   dc.l 0x00000000
   dc.l 0x00000000
   dc.l 0x00000000
   dc.l 0x00000000
   dc.l 0x00000000
   dc.l 0x00000000
   dc.l 0x00000000

   dc.l 0x11101110 ; Character 1, N
   dc.l 0x10111010
   dc.l 0x10011010
   dc.l 0x10101010
   dc.l 0x10110010
   dc.l 0x10111010
   dc.l 0x11101110
   dc.l 0x00000000

Az első vagy is 0. betűnk egy üres hely, ami a hátterünket fogja megadni, fontos...., fentebb leírtam miért. A 1. helyen álló betű pedig az N betű. Ezeket az adatokat, a VRAM területre kell írni, kihagyva az eddigi bitsorozat összeillesztését egyből a kódra ugrok:

    move.l #0x40000000, 0x00C00004    ; Beállítjuk VDP-t VRAM 0x0000 címre való írására
    lea Betuk, a0                   ; Betöltjük a Betuk kezdő címét a0 cím regiszterbe
    move.l #0x0F, d0                ; 32*7 bit adat egy betű
 
    @Loop:
    move.l (a0)+, 0x00C00000        ; Adatokat mozgatjuk VDP adat portra és d0-át inkrementáljuk
    dbra d0, @Loop

És most kiíratjuk azt a fránya N betűt a képernyőre.. ;)

    move.l #0x40000003, 0x00C00004 ; Beállítjuk VDP-t a VRAM 0xC000-ás cím írására (A sík)
    move.w #0x0001, 0x00C00000       ; Kiírjuk az N betűt képernyőre


Értelem szerűen, ha veszünk fel több betűt, akkor azokat a (move.w #0x000?, 0x00C00000) utasítással a kérdőjel helyére behelyettesítve -az adott betű indexét- lehet majd kiíratni. Azt sem szabad elfelejteni, hogy a betűk feltöltését is ki kell kicsit javítani, még pedig annyiszor 8-at kell hozzá adni a (move.l #0x0F, d0) #0x0F-hez, ahány betűvel kiegészítettük a már kész (Betuk:) memóriánkat.

Betű kiírása
Ebben a blog bejegyzésben ennyire jutottunk, remélem hasznosnak találjátok és érthetően sikerült leírnom a dolgokat. A következőben egy kisebb szöveget fogok kiírni a képernyőre, esetleg azt pozicionáljuk is, hogy ne csak random az alap 0,0 pozícióba tegye ki. Addig is kellemes időtöltést a leírtakhoz, természetesen a forrást biztosítottam, hogy biztos legyen a siker élmény.

A letölthető oktató demót itt találod: DPTP_oktato.zip
Természetesen ezen cikk végéről sem maradhat le azon link mutatója, ahol feltehetitek kérdéseiteket a témában: Írjunk játékot SEGA MEGA DRIVE konzolra

Az erő legyen veletek.
DPTP motor: GDPR infomráció!
2018 Május 25.-től hatályba lépett adatvédelmi szabályzatról itt olvashatsz bővebben: Adatvédelmi tájékoztató
Az oldal további használatához el kell olvasnod és fogadnod az adatvédelmi és cookie-k használatával kapcsolatos tájékoztatonkat.

Elfoadom!
Nem fogadom el!