Struktura obrazové paměti grafické karty CGA, blokové přenosy a základy optimalizace

9. 7. 2024
Doba čtení: 30 minut

Sdílet

 Autor: Depositphotos
Ve třetím článku o programování her a dem pro IBM PC se „slavnou“ kartou CGA se zaměříme na důležité operace: přístup do obrazové paměti, využití vertikálního zatemnění pro vykreslování a volání blokových instrukcí pro zápis i přenos dat.

Obsah

1. Struktura obrazové paměti grafické karty CGA, blokové přenosy a základy optimalizace

2. Vyplnění obrazovky konstantní barvou, naivní varianta

3. Úplný zdrojový kód prvního demonstračního příkladu

4. Programová smyčka realizovaná instrukcí LOOP

5. Použití instrukce REP STOSB

6. Úplný zdrojový kód třetího demonstračního příkladu

7. Porovnání rychlosti tří variant vyplnění obrazovky bílou barvou

8. Synchronizace zápisu na obrazovku s vertikálním synchronizačním pulsem

9. Úplný zdrojový kód čtvrtého demonstračního příkladu

10. Odvození snímkové frekvence na základě trvání vyplnění obrazu

11. Vykreslení rastrového obrázku získaného z binárních dat

12. Úplný zdrojový kód pátého demonstračního příkladu

13. Blokový přenos dat operací REP MOSVB či REP MOVSW

14. Realizace přenosu dat operací REP MOVSB

15. Realizace přenosu dat operací REP MOVSW

16. Problematika rozdělení obrazové paměti na liché a sudé řádky

17. Vykreslení lichých řádků získaných z rastrového obrázku

18. Korektní vykreslení všech sudých i lichých řádků získaných z rastrového obrázku

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Struktura obrazové paměti grafické karty CGA, blokové přenosy a základy optimalizace

Ve třetím článku o programování her a grafických dem pro osobní mikropočítač IBM PC se „slavnou“ grafickou kartou CGA se zaměříme na některé důležité nízkoúrovňové operace. Popíšeme si strukturu obrazové paměti karty CGA v grafických režimech (výsledek nás nepotěší), dále si ukážeme přenosy rastrových dat do obrazové paměti a taktéž synchronizaci kódu s vertikální (snímkovou) frekvencí, což nám mj. zajistí konstantní rychlost běhu a navíc i zamezí „sněžení“, které na kartě CGA nastává při zápisu do obrazové paměti (už jsem říkal, že práce s touto kartou je peklem pro programátora?). Ovšem seznámíme se i s některými zajímavými instrukcemi mikroprocesoru Intel 8086/Intel 8088, například s instrukcemi pro blokové přenosy dat (a proč je na CGA nevyužijeme v plné síle). Toto téma do jisté míry souvisí s optimalizacemi, takže si porovnáme některé algoritmy realizované několika různými způsoby.

2. Vyplnění obrazovky konstantní barvou, naivní varianta

Ukažme si nyní, jakým způsobem je možné vyplnit (skoro) celou obrazovku bílou barvou ve chvíli, kdy máme nastaven grafický režim s rozlišením 640×200 pixelů a možností nastavení každého pixelu na barvu pozadí (černá) nebo barvu popředí (bílá). Nejprve zapíšeme do segmentového registru ES adresu segmentu 0×b800. To lze provést jen nepřímo, protože operace mov segmentový_registr, konstanta není podporována (jedno z omezení CISCové instrukční sady):

        mov ax, 0xb800    ; video segment
        mov es, ax        ; do segmentoveho registru ES

Dále vynulujeme pracovní registr BX použitý pro adresování (ze základních registrů pouze tento lze použít v roli adresního registru) v rámci video RAM (tedy segmentu 0×b800). Registr AL bude obsahovat hodnoty osmi pixelů (každý pixel=1 bit) a do registru CX zapíšeme počet bajtů video RAM (zde naschvál zapisuji nižší údaj, ale aby se obrazovka skutečně zaplnila, musíte použít hodnotu 16384).

        xor bx, bx        ; adresa pro zapis barev pixelu
        mov al, 255       ; zapisovana kombinace barev pixelu
        mov cx, 640*200/8 ; pocitadlo smycky

Následuje vlastní programová smyčka, v níž je operace vyplnění obrazovky realizována. Na adresu [ES:BX] jsou zapsány barvy osmi pixelů, adresa uložená v registru BX je zvýšena o jedničku, hodnota počitadla uložená v registru CX je naopak o jedničku snížena a pokud počitadlo nedosáhne nulové hodnoty, bude se programová smyčka opakovat:

fill_loop:
        mov [es:bx], al   ; zapis barev osmi pixelu
        inc bx            ; na dalsi pixel
        dec cx            ; snizeni hodnoty CL
        jnz fill_loop     ; skok pri nenulovosti vysledku
Poznámka: jedná se o velmi neefektivní zápis vyplnění oblasti paměti, což si ostatně ukážeme v dalších příkladech.

Obrázek 1: Výsledek vyplnění prvních 16000 bajtů obrazové paměti. Povšimněte si, že ve skutečnosti některé (sudé) řádky na konci vyplněny nejsou. K této problematice se ještě vrátíme.

3. Úplný zdrojový kód prvního demonstračního příkladu

Úplný zdrojový kód dnešního prvního demonstračního příkladu, který po svém spuštění vyplní (skoro) celou obrazovku bílou barvou, vypadá následovně:

; Vykresleni sady pixelu, vyplneni obrazovky.
;
; preklad pomoci:
;     nasm -f bin -o gfx_6.com gfx_6_fill_1.asm
;
; nebo pouze:
;     nasm -o gfx_6.com gfx_6_fill_1.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 6
        wait_key
 
        mov ax, 0xb800    ; video segment
        mov es, ax        ; do segmentoveho registru ES
        xor bx, bx        ; adresa pro zapis barev pixelu
        mov al, 255       ; zapisovana kombinace barev pixelu
        mov cx, 640*200/8 ; pocitadlo smycky
fill_loop:
        mov [es:bx], al   ; zapis barev osmi pixelu
        inc bx            ; na dalsi pixel
        dec cx            ; snizeni hodnoty CL
        jnz fill_loop     ; skok pri nenulovosti vysledku
 
        wait_key
        exit

4. Programová smyčka realizovaná instrukcí LOOP

V předchozím demonstračním příkladu jsme použili registr CX v roli počitadla programové smyčky. Zápis této smyčky vypadal zhruba následovně:

smyčka:
        ...
        ...
        ...
        dec cx            ; snizeni hodnoty CL
        jnz smyčka        ; skok pri nenulovosti vysledku

Ve skutečnosti je možné tyto dvě instrukce nahradit za jedinou instrukci LOOP, která provádí totožnou operaci, tj. snížení hodnoty CX následované skokem ve chvíli, kdy CX ještě nedosáhla nuly:

smyčka:
        ...
        ...
        ...
        loop smyčka    ; snizeni hodnoty CX, skok pri nenulovosti vysledku

Jak je tato smyčka výhodná nám ukáže následující tabulka:

Operace Bajtů Cyklů
DEC+JNZ 1+2 3+4 nebo 3+16
LOOP 2 5 nebo 17

Druhá varianta je tedy pomalejší a současně i delší, než varianta s instrukcí LOOP.

Poznámka: doba trvání instrukcí skoku (sem patří i LOOP) se liší podle toho, zda se skok provede či nikoli.

A takto vypadá výsledek:

; Vykresleni sady pixelu, vyplneni obrazovky.
;
; preklad pomoci:
;     nasm -f bin -o gfx_6.com gfx_6_ver_fill_2.asm
;
; nebo pouze:
;     nasm -o gfx_6.com gfx_6_ver_fill_2.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 6
        wait_key
 
        mov ax, 0xb800    ; video segment
        mov es, ax        ; do segmentoveho registru ES
        xor bx, bx        ; adresa pro zapis barev pixelu
        mov al, 255       ; zapisovana kombinace barev pixelu
        mov cx, 640*200/8 ; pocitadlo smycky
fill_loop:
        mov [es:bx], al   ; zapis barev osmi pixelu
        inc bx            ; na dalsi pixel
        loop fill_loop    ; snizeni hodnoty CX, skok pri nenulovosti vysledku
 
        wait_key
        exit

5. Použití instrukce REP STOSB

Ve skutečnosti je možné vyplnění (prakticky) celé obrazovky realizovat ještě snadněji, a to konkrétně opakováním instrukce STOSB. Samotná instrukce STOSB znamená „STOre String (byte)“ a provádí tyto operace:

  • Zápis hodnoty uložené v registru AL na adresu [ES:DI]
  • Zvýšení hodnoty registru DI o jedničku

Pokud se navíc před tuto instrukce předá prefix představovaný instrukcí REP, bude se celá operace STOSB opakovat CX-krát.

Poznámka: zde je jasně patrné, proč Intel 8086 patří mezi CISC procesory – realizace REP STOSB evidentně vyžaduje mikrokód a nikoli RISCovou pipeline.

Jedna iterace takto realizované programové smyčky trvá deset cyklů, k nimž musíme připočítat inicializačních devět cyklů.

6. Úplný zdrojový kód třetího demonstračního příkladu

; Vyplneni obrazovky instrukci REP STOSB
;
; preklad pomoci:
;     nasm -f bin -o gfx_6.com gfx_6_ver_fill_1.asm
;
; nebo pouze:
;     nasm -o gfx_6.com gfx_6_ver_fill_1.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 6        ; nastaveni grafickeho rezimu 640x200 se 2 barvami
        wait_key
 
        mov ax, 0xb800    ; video segment
        mov es, ax        ; do segmentoveho registru ES
        xor di, di        ; adresa pro zapis barev pixelu
        mov al, 255       ; zapisovana kombinace barev pixelu
        mov cx, 640*200/8 ; pocitadlo smycky
 
        rep stosb         ; vlastni vyplneni
 
        wait_key
        exit

7. Porovnání rychlosti tří variant vyplnění obrazovky bílou barvou

Nyní si všechny tři varianty vyplnění obrazovky bílou barvou porovnáme. Všechny smyčky zapisují v každé iteraci jediný bajt, ovšem doba trvání se bude odlišovat.

mov [es:bx], al   14  14
inc bx             3   3
dec cx             3   3
jnz fill_loop     16   4  (provedení/neprovedení skoku)
-------------------------
                  36  24

Celkově: 36×15999+24=574008 cyklů (to je hodně)

mov [es:bx], al   14  14
inc bx             3   3
loop fill_loop    17   5
-------------------------
                  34  22

Celkově: 34×15999+22=542118 cyklů (to je hodně)

rep stosb        17  9

Celkově: 17×15999+9=271057 cyklů.

Poznámka: poslední způsob je tedy zdaleka nejrychlejší, což programátory nutilo k ručním optimalizacím (obsazení pracovních registrů atd.).

8. Synchronizace zápisu na obrazovku s vertikálním synchronizačním pulsem

U všech režimů původní varianty grafické karty CGA se vyskytuje ještě jeden fenomén, který uživatelé jiných počítačů neznali – souběžný přístup mikroprocesoru a grafického řadiče do obrazové paměti nebyl kupodivu nijak ošetřen, což způsobovalo nechvalně známé „sněžení“ (snow). Jediná možnost, jak zabránit „sněžení“, byl zápis do obrazové paměti při vertikálním zatemnění elektronového paprsku (horizontální zatemnění má příliš krátký interval). A to byl další problém, protože karta CGA negenerovala přerušení před vertikálním zatemněním – tedy něco, co uměly již mnohé osmibitové mikropočítače. Takže čekání na zatemnění muselo probíhat tím nejhorším možným způsobem – kontinuálním čtením příslušného bitu ze stavového registru karty CGA.

Realizace čekání na vertikální přerušení je sice – samozřejmě pokud nám nevadí „pálení strojového času“ – vlastně až primitivně jednoduché. Postačuje nám totiž kontinuálně sledovat čtvrtý bit portu 0×3da. Pokud je tento bit nastaven, probíhá vertikální přerušení (a zápis je tedy možné provést); jinak neprobíhá. Jenže ono to v praxi není až tak jednoduché, protože i když je tento bit nastaven, tak vlastně nevíme, zda přerušení v dalším cyklu neskončí (nevíme, ve kterém okamžiku přerušení se nacházíme). Musíme tedy provádět dvě operace: čekání na dokončení aktuálního přerušení (pokud probíhá) a čekání na nové přerušení, tedy na jeho začátek. Celá realizace čekání na začátek vertikálního přerušení vypadá takto:

wait_sync:
        mov dx, 0x3da      ; adresa stavoveho registru graficke karty CGA
wait_sync_end:
        in al, dx          ; precteni hodnoty stavoveho registru
        test al, 8         ; odmaskovat priznak vertikalniho synchronizacniho pulsu
        jnz wait_sync_end  ; probiha - cekat na konec
wait_sync_start:
        in al, dx          ; precteni hodnoty stavoveho registru
        test al, 8         ; odmaskovat priznak vertikalniho synchronizacniho pulsu
        jz wait_sync_start ; neprobiha - cekat na zacatek
        ret                ; ok - synchronizacni kurz probiha, lze zapisovat do pameti

Synchronizovaný zápis do video RAM nám „prozradí“ její strukturu:

Obrázek 2: Začátek vyplňování (prozatím jen liché řádky).

Obrázek 3: Vyplnění prvních 8000 bajtů video RAM.

Obrázek 4: Pokračuje se vyplněním sudých řádků.

9. Úplný zdrojový kód čtvrtého demonstračního příkladu

Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu, v němž se na obrazovku zapisují pixely se synchronizací této operace, vypadá následovně:

; Vykresleni sady pixelu, vyplneni obrazovky.
;
; preklad pomoci:
;     nasm -f bin -o gfx_6.com gfx_6_ver_fill_1.asm
;
; nebo pouze:
;     nasm -o gfx_6.com gfx_6_ver_fill_1.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 6
        wait_key
 
        mov ax, 0xb800    ; video segment
        mov es, ax        ; do segmentoveho registru ES
        xor bx, bx        ; adresa pro zapis barev pixelu
        mov cx, 640*200/8 ; pocitadlo smycky
fill_loop:
        call wait_sync
        mov al, 255       ; zapisovana kombinace barev pixelu
        mov [es:bx], al   ; zapis barev osmi pixelu
        inc bx            ; na dalsi pixel
        loop fill_loop    ; snizeni hodnoty CX, skok pri nenulovosti vysledku
 
        wait_key
        exit
 
wait_sync:
        mov dx, 0x3da      ; adresa stavoveho registru graficke karty CGA
wait_sync_end:
        in al, dx          ; precteni hodnoty stavoveho registru
        test al, 8         ; odmaskovat priznak vertikalniho synchronizacniho pulsu
        jnz wait_sync_end  ; probiha - cekat na konec
wait_sync_start:
        in al, dx          ; precteni hodnoty stavoveho registru
        test al, 8         ; odmaskovat priznak vertikalniho synchronizacniho pulsu
        jz wait_sync_start ; neprobiha - cekat na zacatek
        ret                ; ok - synchronizacni kurz probiha, lze zapisovat do pameti

10. Odvození snímkové frekvence na základě trvání vyplnění obrazu

Předchozímu programu trvá vyplnění celé obrazovky poměrně dlouhou dobu. Vezměme tedy do ruky stopky a zkusme tuto dobu alespoň přibližně zjistit a odvodit z ní snímkovou frekvenci (tedy počet snímků zobrazených monitorem za sekundu).

Začneme s výchozím nastavením DOSBoxu, který emuluje karty VGA a SVGA. V této konfiguraci je obrazovka vyplněna za přibližně 230 sekund (měřeno běžnými stopkami, takže &plusm; 1 sekunda). Přitom se provedlo celkem 640×200/8=16000 zápisů. Z toho plyne>

16000/230 = 69,5

Za sekundu se tedy provedlo přibližně 69,5 zápisů, což odpovídá snímkové frekvenci, která dosahuje taktéž této hodnoty (necelých 70 Hz).

Ovšem korektní je měření ve chvíli, kdy je DOSBox nakonfigurován tak, aby emuloval počítač s kartou CGA. V souboru ~/.config/dosbox/dosbox-staging.conf je nutné nastavit řádek machine na hodnotu cga. Nyní bude vyplnění obrazovky trvat 268 sekund, což při stejném počtu 640×200/8=16000 zápisů vede ke snímkové frekvenci:

16000/268 = 59,70 Hz

To je velmi blízko skutečné snímkové frekvenci, která dosahuje 59,923 Hz. Tato na první pohled poněkud divná hodnota je odvozena od frekvence hodinového signálu 14,318181 MHz, která je dělena osmi a následně ještě 114. Tím získáme řádkovou frekvenci (přibližně 15699,8 řádků za sekundu) a podělením hodnotou 262 pak dostaneme onu snímkovou frekvenci 59,923 Hz.

Poznámka: jedná se o přibližně dvojnásobnou frekvenci televizní normy NTSC (29,97 Hz).

11. Vykreslení rastrového obrázku získaného z binárních dat

Ve druhé polovině dnešního článku se zaměříme na zdánlivě jednoduchou úlohu: jak na obrazovku řízenou grafickou kartou CGA vykreslit následující obrázek ze slavné hry Golden Axe:

Obrázek 5: Obrázek ze hry Golden Axe, který máme k dispozici v binární podobě (16000 bajtů čistých obrazových dat).

Z tohoto obrázku nejdříve musíme získat „surová“ rastrová data, tj. barvy jednotlivých pixelů. Obrázek využívá grafický režim s rozlišením 320×200 pixelů, přičemž každý pixel je uložen ve dvou bitech (tedy čtyři pixely v jednom bajtu). Pokud máme k dispozici data obrázku ve formátu BMP s bitovou hloubkou 8bpp, získáme z něho surová data ve vhodném formátu následujícím konvertorem, který přeskočí (a ignoruje) hlavičku BMP, paletu (dohromady 1162 bajtů – ovšem pozor, někdy vypadá hlavička jinak či chybí paleta – níže uvedený konvertor je až příliš primitivní, aby tyto nuance odhalil) a následně přečtené hodnoty pixelů uloží ve formátu 4 pixely na bajt:

#include <stdio.h>
 
#define PIXELS 320*200
 
int main(void) {
    FILE *fin;
    FILE *fout;
    int i;
 
    fin = fopen("golden.bmp", "r");
    /* preskok hlavicky */
    fseek(fin, 1162, SEEK_SET);
 
    fout = fopen("image.bin", "w");
 
    for (i=0; i<PIXELS/4; i++) {
        unsigned char pixels[4];
        unsigned int out;
        fread(pixels, 4, 1, fin);
 
        /* 4 pixely ulozit do jedineho bajtu */
        out = pixels[3] & 0x03 |
              ((pixels[2] & 0x03) << 2) |
              ((pixels[1] & 0x03) << 4) |
              ((pixels[0] & 0x03) << 6);
        printf("%x ", out);
        /* vystup */
        fputc(out, fout);
    }
 
    fclose(fin);
    fclose(fout);
}

Výsledkem by měl být v každém případě soubor image.bin, který má mít přesnou velikost 16000 bajtů.

Tento binární soubor můžeme připojit do výsledné přeložené aplikace. To je možné i při použití formátu COM, protože délka souboru nepřesáhne 64 kilobajtů a tedy data budou uložena ve stejném segmentu, jako programový kód. Při překladu se binární data (obrázku) načtou a uloží tímto příkazem assembleru NASM:

; pridani binarnich dat s rastrovym obrazkem
image:
    incbin "image.bin"
Poznámka: výsledné soubory COM budou tedy mít délku přibližně 160×x bajtů, kde xx odpovídá délce programového kódu (která ani v tom nejsložitějším případě ani zdaleka nepřesáhne 100 bajtů).

Co to znamená v praxi? Na adrese označené návěštím image je uloženo 16000 bajtů s daty rastrového obrázku, které musíme nějakým způsobem přenést do obrazové paměti, která začíná na začátku segmentu 0×d800. Budeme tedy potřebovat postupně zkopírovat 16000 bajtů. Nejprve si připravíme potřebné registry.

Do dvojice registrů DS:SI uložíme adresu prvního bajtu obrázku. Segmentový registr DS tedy nastavíme na stejnou hodnotu, jako segmentový registr pro kód CS. To sice není striktně nutné, protože tuto operaci za nás provede DOS při inicializaci programu, ale je lepší být explicitní. Povšimněte si, že nelze provádět přesuny dat přímo mezi segmentovými registry, takže si pomůžeme buď zásobníkem nebo například registrem AX:

mov ax, cs
mov ds, ax
mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
Poznámka: SI značí „source index“ a jedná se o typický registr obsahující zdrojovou adresu, resp. jejích spodních 16 bitů. Tato adresa se přičte k adrese příslušného segmentového registru posunuté o čtyři bity doleva:
adresa_zdroje = (DS << 4) + SI

Podobným způsobem si v registrech ES:DI připravíme cílovou adresu, tedy v našem případě adresu prvního bajtu v obrazové paměti (ta je rovna b0×b800:0 v zápisu segment:offset):

mov ax, 0xb800
mov es, ax
mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
Poznámka: zatímco SI znamená „source index“, tak DI značí „destination index“. Ten se typicky spojuje právě se segmentovým registrem ES (extra segment).

Zbývá nám nastavit si počitadlo přenášených bajtů, což je snadné. Pro tento účel se téměř vždy používá registr CX, popř. jeho spodním osm bitů CL:

mov cx, 16000   ; pocet zapisovanych bajtu

Vlastní programová smyčka, která přenese všech 16000 bajtů, může v naivním provedení vypadat následovně:

move:   mov al, [ds:si]   ; precteni jednoho bajtu z obrazku
        mov [es:di], al   ; zapis tohoto bajtu do video RAM
        inc si          ; posun ve zdrojovem "poli"
        inc di          ; posun ve video RAM
        loop move       ; opakovat CX-krat

Obrázek 6: Výsledek přesunu rastrových dat obrázku do obrazové paměti (špatné proložení sudých a lichých řádků).

Poznámka: zdaleka se nejedná o nejkratší ani o nejrychlejší řešení, ale ukazují se na něm některé vlastnosti čipu Intel 8086, zejména jeho adresovací režimy (segment+offset), specializované instrukce pro inkrementaci registrů a nám již známá instrukce LOOP pro snížení obsahu počitadla CX a skok v případě, že počitadlo ještě nedosáhlo nuly.

V případě assembleru NASM je možné vynechat hranaté závorky u výpočtu adresy, takže se zápis zkrátí následovně (zda je čitelnější, ponechám na laskavém čtenáři):

move:   mov al, ds:si   ; precteni jednoho bajtu z obrazku
        mov es:di, al   ; zapis tohoto bajtu do video RAM
        inc si          ; posun ve zdrojovem "poli"
        inc di          ; posun ve video RAM
        loop move       ; opakovat CX-krat

12. Úplný zdrojový kód pátého demonstračního příkladu

Opět se podívejme na úplný zdrojový kód demonstračního příkladu, který po svém spuštění vykreslí na obrazovku rastrový obrázek. Přitom výsledný obrázek nebude korektní; tuto chybu opravíme v rámci navazujících kapitol:

; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Zakladni varianta s explicitne zapsanou programovou smyckou.
;
; preklad pomoci:
;     nasm -f bin -o gfx_4.com gfx_4_image.asm
;
; nebo pouze:
;     nasm -o gfx_4.com gfx_4_image.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 4      ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
 
        mov ax, cs
        mov ds, ax
        mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
 
        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        mov cx, 16000   ; pocet zapisovanych bajtu
 
move:   mov al, ds:si   ; precteni jednoho bajtu z obrazku
        mov es:di, al   ; zapis tohoto bajtu do video RAM
        inc si          ; posun ve zdrojovem "poli"
        inc di          ; posun ve video RAM
        loop move       ; opakovat CX-krat
 
        wait_key
        exit
 
; pridani binarnich dat s rastrovym obrazkem
image:
    incbin "image.bin"

13. Blokový přenos dat operací REP MOSVB či REP MOVSW

V programu z předchozí kapitoly je přenos dat z kódového+datového segmentu do segmentu s video pamětí (Video RAM) řešen programovou smyčkou, v níž se přenáší bajty z adresy [DS:SI] na adresu [ES:DI]. Jako počitadlo této programové smyčky opět slouží registr CX. Smyčka vypadá následovně:

move:   mov al, ds:si   ; precteni jednoho bajtu z obrazku
        mov es:di, al   ; zapis tohoto bajtu do video RAM
        inc si          ; posun ve zdrojovem "poli"
        inc di          ; posun ve video RAM
        loop move       ; opakovat CX-krat

Jak dlouho ovšem potrvá přenesení jednoho bajtu? Zkusme si to spočítat, opět pro případ původního čipu Intel 8086 (a Intel 8088 je v tomto případě prakticky stejně rychlý):

mov al, ds:si   14
mov es:di, al   13+5
inc si           3
inc di           3
loop move       17
------------------------
                50 až 55

To je poměrně dlouhá doba! Ovšem již mikroprocesor Intel 8086 (a samozřejmě i Intel 8088) obsahoval instrukci movsb určenou pro přenos jednoho bajtu z adresy [DS:SI] na adresu [ES:DI] se zvýšením SIDI o jedničku. Navíc je možné i před tuto instrukci vložit prefix rep pro její opakování CX-krát. A navíc existuje i instrukce movsw určená pro přenos celých 16bitových slov. Rychlosti těchto instrukcí jsou následující:

Instrukce Délka (bajtů) Cyklů
movsb 1 18
movsw 1 26
rep movsb 2 9+17×n
rep movsw 2 9+28×n
Poznámka: použití rep movsw je tedy na procesoru Intel 8086 skoro dvakrát rychlejší, než rep movsb, ovšem u Intelu 8088 tomu tak není kvůli poloviční šířce externí datové sběrnice!

Pokud vám doby trvání instrukcí stále připadnou dlouhé, vězte, že na Pentiu trvají operace rep movs* pouze 3+n cyklů, kde n je počet přenášených bajtů, 16bitových slov či 32bitových slov. Tedy Pentium, i kdyby bylo taktované pouze na 4,77 MHz, jako původní PC s Intelem 8088, by stále bylo několikanásobně rychlejší, protože podobně se urychlila i většina ostatních operací.

14. Realizace přenosu dat operací REP MOVSB

Pro přenos obrazu z původního segmentu, do něhož je nahrán náš program, do segmentu video RAM můžeme využít jak rep movsb, tak i rep movsw. Nejprve použijeme rep movsb, tedy přenos po jednotlivých bajtech. Celkový počet opakování přenosové operace bude roven 16000:

mov cx, 16000   ; pocet zapisovanych bajtu

A blokový přenos bude proveden instrukcí movsb s prefixem rep:

rep movsb       ; provest cely blokovy prenos po bajtech

Úplný zdrojový kód takto upraveného programu bude vypadat následovně:

; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Varianta zalozena na instrukci REP MOVSB
;
; preklad pomoci:
;     nasm -f bin -o gfx_4.com gfx_4_image.asm
;
; nebo pouze:
;     nasm -o gfx_4.com gfx_4_image.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 4      ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
 
        mov ax, cs
        mov ds, ax
        mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
 
        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        mov cx, 16000   ; pocet zapisovanych bajtu
 
        rep movsb       ; provest cely blokovy prenos po bajtech
 
        wait_key
        exit
 
image:
    incbin "image.bin"

15. Realizace přenosu dat operací REP MOVSW

Rychlejší bude přenos pixelů nikoli po bajtech, ale po šestnáctibitových slovech. To znamená, že se počet přenosových operací sníží na polovinu:

mov cx, 16000/2 ; pocet zapisovanych slov

A vlastní přenos zahájíme instrukcí movsw s prefixem rep:

rep movsw       ; provest cely blokovy prenos po slovech

Opět se podívejme na úplný zdrojový kód takto upraveného programu:

; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Varianta zalozena na instrukci REP MOVSW
;
; preklad pomoci:
;     nasm -f bin -o gfx_4.com gfx_4_image.asm
;
; nebo pouze:
;     nasm -o gfx_4.com gfx_4_image.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 4      ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
 
        mov ax, cs
        mov ds, ax
        mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
 
        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        mov cx, 16000/2 ; pocet zapisovanych slov
 
        rep movsw       ; provest cely blokovy prenos po slovech
 
        wait_key
        exit
 
image:
    incbin "image.bin"

16. Problematika rozdělení obrazové paměti na liché a sudé řádky

Ve všech předchozích demonstračních příkladech, ve kterých jsme se snažili o vykreslení rastrového obrázku, bylo zřejmé, že vykreslení není korektní. Je tomu tak z toho důvodu, že v grafických režimech karty CGA (jak v monochromatickém, tak i ve „čtyřbarevném“) jsou v prvních osmi kilobajtech uloženy liché řádky (pokud řádky počítáme od jedničky) a ve druhých osmi kilobajtech pak řádky sudé. Mapa video RAM tedy vypadá zhruba takto:

====================================
0xb800         řádek 1
0xb800+80      řádek 3
0xb800+160     řádek 5
...            ...
...            ...
...            ...
0xb800+7920    řádek 199
0xb800+8000    nepoužitých 192 bajtů
====================================
0xd800         řádek 2
0xd800+80      řádek 4
0xd800+160     řádek 6
...            ...
...            ...
...            ...
0xd800+7920    řádek 200
0xb800+8000    nepoužitých 192 bajtů
====================================

Toto řešení sice může do určité míry vyhovovat v prokládaných (interlaced) režimech, ovšem znesnadňuje veškeré další operace, tj. vykreslování pixelů, úseček, bitmap atd.

17. Vykreslení lichých řádků získaných z rastrového obrázku

Upravme si tedy předchozí demonstrační příklady do takové podoby, aby se rastrový obrázek (který je uložen kontinuálně, tj. ve formě řádků 1, 2, 3, 4, … 99, 100, … 199, 200) vykreslil korektně. První úprava bude spočívat v tom, že vykreslíme pouze liché řádky a přitom budeme korektně ve vstupním obrázku přeskakovat řádky sudé – tedy budeme se posouvat o 80 bajtů bez přenosu. Realizace řešení poloviny našeho problému může vypadat následovně (registry budou nastaveny stejně, jako v předchozích příkladech):

        mov bl, 100     ; pocitadlo radku
 
outer_loop:
        mov cx, 80/2    ; velikost bloku ve slovech
        rep movsw       ; prenest jeden obrazovy radek
        add si, 80      ; preskocit lichy/sudy radek
        dec bl
        jnz outer_loop  ; opakovat smycku BL-krat

Povšimněte si, že původní blokový přenos realizovaný jedinou instrukcí rep movsb nebo rep movsw je nyní nutné rozdělit na přenos jednotlivých řádků – děkovné dopisy posílejte do IBM. A navíc je nutné registr SI zvýšit o 80 (bajtů), aby se ve vstupním obrázku přeskočil sudý řádek.

Výsledkem bude následující obrazovka:

Obrázek 7: Vykreslení pouze lichých řádků, korektní přeskočení řádků sudých.

Výsledný program vypadá následovně:

; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Korektni vykresleni vsech lichych radku obrazku.
;
; preklad pomoci:
;     nasm -f bin -o gfx_4.com gfx_4_image.asm
;
; nebo pouze:
;     nasm -o gfx_4.com gfx_4_image.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 4      ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
 
        mov ax, cs
        mov ds, ax
        mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
 
        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        mov bl, 100     ; pocitadlo radku
 
outer_loop:
        mov cx, 80/2    ; velikost bloku ve slovech
        rep movsw       ; prenest jeden obrazovy radek
        add si, 80      ; preskocit lichy/sudy radek
        dec bl
        jnz outer_loop  ; opakovat smycku BL-krat
 
        wait_key
        exit
 
image:
    incbin "image.bin"

18. Korektní vykreslení všech sudých i lichých řádků získaných z rastrového obrázku

Nyní již můžeme přistoupit k finální úpravě našeho vykreslovače obrázků. Nejprve budeme přenos poloviny obrazových řádků (sudých nebo lichých) realizovat v podprogramu, přičemž se předpokládá korektní nastavení adres [DS:SI] a [ES:DI]. Tento kód již známe, pouze je doplněn o instrukci ret na konci:

move_half_image:
        mov bl, 100     ; pocitadlo radku
outer_loop:
        mov cx, 80/2    ; velikost bloku ve slovech
        rep movsw       ; prenest jeden obrazovy radek
        add si, 80      ; preskocit lichy/sudy radek
        dec bl
        jnz outer_loop  ; opakovat smycku BL-krat
        ret

Následuje vykreslení lichých řádků s využitím tohoto podprogramu:

        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        call move_half_image

Dále nám již postačuje posunout se ve zdrojovém obrázku o jeden obrazový řádek a upravit adresu v registru DI tak, aby ukazoval na začátek druhé poloviny Video RAM (stačilo by i pouhé přičtení hodnoty 192, to je však poněkud matoucí):

        mov si, image+80; adresa prvniho pixelu na DRUHEM radku
        mov di, 8192    ; druha "stranka" video RAM
        call move_half_image

Výsledek:

Obrázek 8: Korektní vykreslení jak lichých, tak i sudých řádků.

Opět se podívejme na výslednou podobu demonstračního příkladu:

bitcoin školení listopad 24

; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Korektni vykresleni vsech sudych i lichych radku obrazku.
;
; preklad pomoci:
;     nasm -f bin -o gfx_4.com gfx_4_image.asm
;
; nebo pouze:
;     nasm -o gfx_4.com gfx_4_image.asm
 
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 4      ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
 
        mov ax, cs
        mov ds, ax
        mov si, image   ; nyni DS:SI obsahuje adresu prvniho bajtu v obrazku
 
        mov ax, 0xb800
        mov es, ax
        mov di, 0       ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        call move_half_image
 
        mov si, image+80; adresa prvniho pixelu na DRUHEM radku
        mov di, 8192    ; druha "stranka" video RAM
        call move_half_image
 
        wait_key
        exit
 
move_half_image:
        mov bl, 100     ; pocitadlo radku
outer_loop:
        mov cx, 80/2    ; velikost bloku ve slovech
        rep movsw       ; prenest jeden obrazovy radek
        add si, 80      ; preskocit lichy/sudy radek
        dec bl
        jnz outer_loop  ; opakovat smycku BL-krat
        ret
 
 
image:
    incbin "image.bin"

Obrázek 9: Po přepnutí barvové palety získáme originál (podrobnosti budou popsány příště).

19. Repositář s demonstračními příklady

Demonstrační příklady napsané v assembleru, které jsou určené pro překlad pomocí assembleru NASM, byly uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/8bit-fame. Jednotlivé demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně rozsáhlý) repositář:

# Příklad Stručný popis Adresa
1 hello.asm program typu „Hello world“ naprogramovaný v assembleru pro systém DOS https://github.com/tisnik/8bit-fame/blob/master/pc-dos/hello.asm
2 hello_shorter.asm kratší varianta výskoku z procesu zpět do DOSu https://github.com/tisnik/8bit-fame/blob/master/pc-dos/hello_shorter.asm
3 hello_wait.asm čekání na stisk klávesy https://github.com/tisnik/8bit-fame/blob/master/pc-dos/hello_wait.asm
4 hello_macros.asm realizace jednotlivých částí programu makrem https://github.com/tisnik/8bit-fame/blob/master/pc-dos/hello_macros.asm
       
5 gfx4_putpixel.asm vykreslení pixelu v grafickém režimu 4 https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_putpixel.asm
6 gfx6_putpixel.asm vykreslení pixelu v grafickém režimu 6 https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel.asm
7 gfx4_line.asm vykreslení úsečky v grafickém režimu 4 https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_line.asm
8 gfx6_line.asm vykreslení úsečky v grafickém režimu 6 https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_line.asm
       
9 gfx6_fill1.asm vyplnění obrazovky v grafickém režimu, základní varianta https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_fill1.asm
10 gfx6_fill2.asm vyplnění obrazovky v grafickém režimu, varianta s instrukcí LOOP https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_fill2.asm
11 gfx6_fill3.asm vyplnění obrazovky instrukcí REP STOSB https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_fill3.asm
12 gfx6_fill4.asm vyplnění obrazovky, synchronizace vykreslování s paprskem https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_fill4.asm
       
13 gfx4_image1.asm vykreslení rastrového obrázku získaného z binárních dat, základní varianta https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image1.asm
14 gfx4_image2.asm varianta vykreslení rastrového obrázku s využitím instrukce REP MOVSB https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image2.asm
15 gfx4_image3.asm varianta vykreslení rastrového obrázku s využitím instrukce REP MOVSW https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image3.asm
16 gfx4_image4.asm korektní vykreslení všech sudých řádků bitmapy https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image4.asm
17 gfx4_image5.asm korektní vykreslení všech sudých i lichých řádků bitmapy https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image5.asm
18 gfx4_image6.asm nastavení barvové palety před vykreslením obrázku https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image6.asm

20. Odkazy na Internetu

  1. The Intel 8088 Architecture and Instruction Set
    https://people.ece.ubc.ca/~ed­c/464/lectures/lec4.pdf
  2. x86 Opcode Structure and Instruction Overview
    https://pnx.tf/files/x86_op­code_structure_and_instruc­tion_overview.pdf
  3. x86 instruction listings (Wikipedia)
    https://en.wikipedia.org/wi­ki/X86_instruction_listin­gs
  4. x86 assembly language (Wikipedia)
    https://en.wikipedia.org/wi­ki/X86_assembly_language
  5. Intel Assembler (Cheat sheet)
    http://www.jegerlehner.ch/in­tel/IntelCodeTable.pdf
  6. 25 Microchips That Shook the World
    https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world
  7. Chip Hall of Fame: MOS Technology 6502 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor
  8. Chip Hall of Fame: Intel 8088 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-intel-8088-microprocessor
  9. Jak se zrodil procesor?
    https://www.root.cz/clanky/jak-se-zrodil-procesor/
  10. Apple II History Home
    http://apple2history.org/
  11. The 8086/8088 Primer
    https://www.stevemorse.or­g/8086/index.html
  12. flat assembler: Assembly language resources
    https://flatassembler.net/
  13. FASM na Wikipedii
    https://en.wikipedia.org/wiki/FASM
  14. Fresh IDE FASM inside
    https://fresh.flatassembler.net/
  15. MS-DOS Version 4.0 Programmer's Reference
    https://www.pcjs.org/docu­ments/books/mspl13/msdos/dos­ref40/
  16. INT 21 – DOS Function Dispatcher (DOS)
    https://www.stanislavs.or­g/helppc/int21.html
  17. DOS API (Wikipedia)
    https://en.wikipedia.org/wiki/DOS_API
  18. Bit banging
    https://en.wikipedia.org/wi­ki/Bit_banging
  19. IBM Basic assembly language and successors (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Basic_assembly_lan­guage_and_successors
  20. X86 Assembly/Bootloaders
    https://en.wikibooks.org/wi­ki/X86_Assembly/Bootloaders
  21. Počátky grafiky na PC: grafické karty CGA a Hercules
    https://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/
  22. Co mají společného Commodore PET/4000, BBC Micro, Amstrad CPC i grafické karty MDA, CGA a Hercules?
    https://www.root.cz/clanky/co-maji-spolecneho-commodore-pet-4000-bbc-micro-amstrad-cpc-i-graficke-karty-mda-cga-a-hercules/
  23. Karta EGA: první použitelná barevná grafika na PC
    https://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/
  24. RGB Classic Games
    https://www.classicdosgames.com/
  25. Turbo Assembler (Wikipedia)
    https://en.wikipedia.org/wi­ki/Turbo_Assembler
  26. Microsoft Macro Assembler
    https://en.wikipedia.org/wi­ki/Microsoft_Macro_Assembler
  27. IBM Personal Computer (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Personal_Computer
  28. Intel 8251
    https://en.wikipedia.org/wi­ki/Intel_8251
  29. Intel 8253
    https://en.wikipedia.org/wi­ki/Intel_8253
  30. Intel 8255
    https://en.wikipedia.org/wi­ki/Intel_8255
  31. Intel 8257
    https://en.wikipedia.org/wi­ki/Intel_8257
  32. Intel 8259
    https://en.wikipedia.org/wi­ki/Intel_8259
  33. Support/peripheral/other chips – 6800 family
    http://www.cpu-world.com/Support/6800.html
  34. Motorola 6845
    http://en.wikipedia.org/wi­ki/Motorola_6845
  35. The 6845 Cathode Ray Tube Controller (CRTC)
    http://www.tinyvga.com/6845
  36. CRTC operation
    http://www.6502.org/users/an­dre/hwinfo/crtc/crtc.html
  37. 6845 – Motorola CRT Controller
    https://stanislavs.org/hel­ppc/6845.html
  38. The 6845 Cathode Ray Tube Controller (CRTC)
    http://www.tinyvga.com/6845
  39. Motorola 6845 and bitwise graphics
    https://retrocomputing.stac­kexchange.com/questions/10996/mo­torola-6845-and-bitwise-graphics
  40. IBM Monochrome Display Adapter
    http://en.wikipedia.org/wi­ki/Monochrome_Display_Adap­ter
  41. Color Graphics Adapter
    http://en.wikipedia.org/wi­ki/Color_Graphics_Adapter
  42. Color Graphics Adapter and the Brown color in IBM 5153 Color Display
    https://www.aceinnova.com/en/e­lectronics/cga-and-the-brown-color-in-ibm-5153-color-display/
  43. The Modern Retrocomputer: An Arduino Driven 6845 CRT Controller
    https://hackaday.com/2017/05/14/the-modern-retrocomputer-an-arduino-driven-6845-crt-controller/
  44. flat assembler: Assembly language resources
    https://flatassembler.net/
  45. FASM na Wikipedii
    https://en.wikipedia.org/wiki/FASM
  46. Fresh IDE FASM inside
    https://fresh.flatassembler.net/
  47. MS-DOS Version 4.0 Programmer's Reference
    https://www.pcjs.org/docu­ments/books/mspl13/msdos/dos­ref40/
  48. INT 21 – DOS Function Dispatcher (DOS)
    https://www.stanislavs.or­g/helppc/int21.html
  49. DOS API (Wikipedia)
    https://en.wikipedia.org/wiki/DOS_API
  50. IBM Basic assembly language and successors (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Basic_assembly_lan­guage_and_successors
  51. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wi­ki/X86_Assembly/Arithmetic
  52. Art of Assembly – Arithmetic Instructions
    http://oopweb.com/Assembly/Do­cuments/ArtOfAssembly/Volu­me/Chapter6/CH06–2.html
  53. ASM Flags
    http://www.cavestory.org/gu­ides/csasm/guide/asm_flag­s.html
  54. Status Register
    https://en.wikipedia.org/wi­ki/Status_register
  55. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/develo­perworks/library/l-gas-nasm/index.html
  56. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/gryga­rek/asm/asmlinux.html
  57. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  58. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  59. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  60. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  61. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  62. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  63. Programming from the Ground Up Book – Summary
    http://savannah.nongnu.or­g/projects/pgubook/
  64. DOSBox
    https://www.dosbox.com/
  65. The C Programming Language
    https://en.wikipedia.org/wi­ki/The_C_Programming_Langu­age
  66. Hercules Graphics Card (HCG)
    https://en.wikipedia.org/wi­ki/Hercules_Graphics_Card
  67. Complete 8086 instruction set
    https://content.ctcd.edu/cou­rses/cosc2325/m22/docs/emu8086in­s.pdf
  68. Complete 8086 instruction set
    https://yassinebridi.github.io/asm-docs/8086_instruction_set.html
  69. 8088 MPH by Hornet + CRTC + DESiRE (final version)
    https://www.youtube.com/wat­ch?v=hNRO7lno_DM
  70. Area 5150 by CRTC & Hornet (Party Version) / IBM PC+CGA Demo, Hardware Capture
    https://www.youtube.com/wat­ch?v=fWDxdoRTZPc
  71. 80×86 Integer Instruction Set Timings (8088 – Pentium)
    http://aturing.umcs.maine­.edu/~meadow/courses/cos335/80×86-Integer-Instruction-Set-Clocks.pdf
ikonka

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.