Obsah
2. Řízení způsobu zobrazení barev kartou CGA ovládáním portu 3D9
3. Nastavení korektní barvové palety a vykreslení rastrového obrázku
4. Úplný zdrojový kód aplikace, která zobrazí rastrový obrázek i s korektní barvovou paletou
5. Změna intenzity všech barev na obrazovce
6. Úplný zdrojový kód aplikace pro změnu intenzity barev na obrazovce
7. Postupná změna barvy pozadí (16 různých možností)
8. Úplný zdrojový kód příkladu pro postupnou změnu barev pozadí
9. Programové vykreslení jediného pixelu v monochromatickém barvovém režimu
10. Výpočet adresy pro zápis pixelu
11. Výpočet masky pixelu a vlastní vykreslení
12. Úplný kód příkladu, který po svém spuštění vykreslí diagonální úsečku z jednotlivých pixelů
13. První optimalizace: realizace násobení 8×8 bitů namísto 16×16 bitů
14. Náhrada součinu za bitové posuny
16. Vykreslení pixelů na neprázdném pozadí
17. Korektní vykreslení bílých pixelů vůči jakémukoli pozadí
18. Úplný zdrojový kód dnešního posledního demonstračního příkladu
19. Repositář s demonstračními příklady
1. Krátké shrnutí předchozí části – korektní vykreslení všech sudých i lichých řádků rastrového obrázku
Připomeňme si, že v předchozím článku jsme si ukázali, jakým způsobem se přistupuje do video RAM grafické karty CGA, jak je tato paměť organizovaná a jak se do ní může vykreslit nějaký statický obrázek. Přitom jsme vycházeli ze screenshotu získaného ze slavné hry Golden Axe:
Obrázek 1: Statický rastrový obrázek, který budeme postupně vykreslovat.
Již jsme úspěšně vyřešili problém s uložením sudých a lichých obrazových řádků v různých částech video RAM, takže prozatím máme dostatek znalostí pro vykreslení následujícího obrázku (s nekorektní barvovou paletou):
Obrázek 2: Korektní vykreslení jak lichých, tak i sudých řádků.
Vykreslovací rutina je volána (dvakrát) následovně:
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
V dnešním článku si ukážeme ovládání barvové palety ve standardních grafických režimech. A taktéž se podíváme na to, jakým způsobem je možné vykreslit jednotlivé pixely. Jedná se sice o tu nejprimitivnější grafickou entitu, ale dále uvidíme, že samotné vykreslování pixelů je „díky“ struktuře grafické paměti relativně složitou operací.
2. Řízení způsobu zobrazení barev kartou CGA ovládáním portu 3D9
Připomeňme si, že v grafickém režimu s rozlišením 320×200 a čtyřmi barvami bylo možné zvolit barvovou paletu. Tato paleta však nemohla být libovolná (naproti tomu v textovém režimu je možné použít šestnáct barev), protože existovaly pouze dvě fixní barvové palety, přičemž při výběru barev se IBM skutečně „předvedla“, protože její nevkusná barevná schémata byla prakticky nepoužitelná :-) První paleta fixně obsahovala barvy zelenou, červenou a hnědou (red, green, brown – ovšem již víme, že hnědá byla tvořena speciálním obvodem v monitoru z tmavě žluté), druhá paleta barvy azurovou, fialovou a bílou (cyan, magenta, white). Je patrné, že obě palety se liší pouze přidáním modré barvové složky.
Pozadí, tedy čtvrtá barva, mohla být zvolena libovolně, resp. z nabídky CGA – tedy jedna z patnácti možných barev. Navíc bylo možné ovlivnit i intenzitu barev popředí – zvýšit či snížit ji o 33%.
Volba barvové palety, barvy pozadí i intenzity barev, se provádí zápisem na port dostupný na adrese 3d9. Význam jednotlivých bitů je následující:
| Bit | Význam v textovém režimu | Význam v grafických režimech |
|---|---|---|
| 0–3 | barva okraje | barva pozadí (pro pixely s nulovou hodnotou) |
| 4 | intenzita pozadí | intenzita pixelů (pro rozlišení 320×200) |
| 5 | × | „modrá“ varianta barvové palety |
| 6 | × | × |
| 7 | × | × |
3. Nastavení korektní barvové palety a vykreslení rastrového obrázku
Pro nastavení barvové palety bude nutné změnit hodnoty bitů řídicího registru grafické karty CGA. Tyto bity jsou ovladatelné přes port dostupný (z pohledu mikroprocesoru) na adrese 3d9. Pro zápis na tento port (ale samozřejmě i na jakýkoli jiný port) se používá instrukce OUT. Ta nabízí několik adresovacích režimů:
| Varianta | Stručný popis |
|---|---|
| imm8, al | zápis hodnoty uložené v registru AL na osmibitový port s uvedenou osmibitovou adresou |
| imm8, ax | zápis hodnoty uložené v registru AX na šestnáctibitový port s uvedenou osmibitovou adresou |
| dx, al | zápis hodnoty uložené v registru AL na osmibitový port, jehož adresa je uložena v registru DX |
| dx, ax | zápis hodnoty uložené v registru AX na šestnáctibitový port, jehož adresa je uložena v registru DX |
První dvě instrukce jsou sice kratší z pohledu velikosti kódu, ale trvají delší dobu. To nás ale vlastně nemusí trápit, protože porty karty CGA neleží v rozsahu 0-FF, ale výše (3d9), takže jsme stejně nuceni použít variantu s registrem DX, ve kterém je adresa portu uložena. Potřebujeme vynulovat pátý bit (tedy zakázat modrou složku v barvách), takže celá sekvence instrukcí, která změní barvovou paletu, bude vypadat následovně:
mov dx, 0x3d9 ; port s rizenim graficke palety mov al, 0x10 ; zmena barevne palety out dx, al ; pres port 0x3d9
Obrázek 3: Obrázek s korektní barvovou paletou (porovnejte s originálem – obrázkem číslo 1).
4. Úplný zdrojový kód aplikace, která zobrazí rastrový obrázek i s korektní barvovou paletou
Podobně, jako tomu bylo i v předchozích dvou částech tohoto seriálu, si nyní ukážeme úplný zdrojový kód jednoduché aplikace, která po svém spuštění vykreslí rastrový obrázek v rozlišení 320×200 pixelů a čtyřmi barvami i s korektní barvovou paletou:
; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Korektni vykresleni vsech sudych i lichych radku obrazku.
; Nastaveni barvove palety.
;
; 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 dx, 0x3d9 ; port s rizenim graficke palety
mov al, 0x10 ; zmena barevne palety
out dx, al ; pres port 0x3d9
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"
5. Změna intenzity všech barev na obrazovce
Bitem číslo 4 na portu 3d9 se řídí intenzita všech barev na obrazovce. Jedná se většinou o zbytečnou volbu, protože nízké intenzity všech barev (v grafickém režimu) povedou k málo kontrastnímu obrazu, ovšem nic nám pochopitelně nebrání v tom si tuto volbu otestovat:
mov dx, 0x3d9 ; port s rizenim graficke palety mov al, 0x00 ; zmena barevne palety, nizka intenzita out dx, al ; pres port 0x3d9
Výsledný obrázek by měl vypadat zhruba následovně:
Obrázek 4: Obrázek po přepnutí barvové palety tak, aby se používaly nízké intenzity barev.
6. Úplný zdrojový kód aplikace pro změnu intenzity barev na obrazovce
Opět si pro jistotu uvedeme úplný zdrojový kód příkladu, který po svém spuštění zobrazí (stále stejný) rastrový obrázek, nyní ovšem s nízkou intenzitou barev:
; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Korektni vykresleni vsech sudych i lichych radku obrazku.
; Nastaveni barvove palety.
; Nastaveni nizke intenzity.
;
; 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 dx, 0x3d9 ; port s rizenim graficke palety
mov al, 0x00 ; zmena barevne palety, nizka intenzita
out dx, al ; pres port 0x3d9
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"
7. Postupná změna barvy pozadí (16 různých možností)
Zajímavější jsou bity 0–3 portu 3d9. Těmito bity se totiž v grafických režimech určuje barva pozadí (tedy barva pixelů s nulovou hodnotou), nezávisle na ostatních třech barvách popředí (foreground). V textových režimech se pomocí těchto bitů určuje pouze barva okraje obrazovky, protože popředí i pozadí znaků je řešeno odlišnou technikou.
Vraťme se však ke grafickým režimům a barvě pozadí. Velmi jednoduchým způsobem lze implementovat programovou smyčku, která po stisku libovolné klávesy (přesněji řečeno skoro libovolné klávesy – Shift atd. se nepočítá) změní barvu pozadí a umožní nám tak projít všemi šestnácti možnostmi. Realizace této smyčky není ve skutečnosti příliš složitá, jak je to ostatně patrné i z následující sekvence osmi programových řádků:
mov cl, 0x10 ; zmena palety, vychozi intenzita barev
mov dx, 0x3d9 ; port s rizenim graficke palety
opak:
mov al, cl ; hodnota zapisovana na port 0x3d9 do registru AL
inc cl ; dalsi barva
out dx, al ; pres port 0x3d9
wait_key ; cekani na klavesu
jmp opak ; opakovat cele znovu
Podívejme se na několik výsledných obrázků tak, jak je aplikace postupně zobrazí:
Obrázek 5: Na pozadí je použita barva číslo 1.
Obrázek 6: Na pozadí je použita barva číslo 2.
Obrázek 7: Na pozadí je použita barva číslo 3.
Obrázek 8: Na pozadí je použita barva číslo 4.
…a tak dále…
8. Úplný zdrojový kód příkladu pro postupnou změnu barev pozadí
Opět se, nyní již bez dalšího podrobnějšího popisu, podívejme na výsledný program:
; Vykresleni rastroveho obrazku ziskaneho z binarnich dat.
; Korektni vykresleni vsech sudych i lichych radku obrazku.
; Nastaveni barvove palety.
; Nastaveni nizke intenzity.
;
; 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
mov cl, 0x10 ; zmena palety, vychozi intenzita barev
mov dx, 0x3d9 ; port s rizenim graficke palety
opak:
mov al, cl ; hodnota zapisovana na port 0x3d9 do registru AL
inc cl ; dalsi barva
out dx, al ; pres port 0x3d9
wait_key ; cekani na klavesu
jmp opak ; opakovat cele znovu
exit ; (sem se rizeni nedostane)
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"
9. Programové vykreslení jediného pixelu v monochromatickém barvovém režimu
Nyní již víme, jakým způsobem je možné vykreslit rastrový obrázek, a to nezávisle na nastaveném grafickém režimu. Prakticky stejný kód tedy bude funkční jak pro monochromatický režim s rozlišením 640×200 pixelů, tak i pro čtyřbarevný režim s rozlišením 320×200 pixelů. Ovšem nyní se zaměřme na zdánlivě jednodušší operaci – jak na obrazovku vykreslit jediný pixel. Popis začneme u monochromatického režimu, který je v tomto ohledu jednodušší.
Připravíme si kostru podprogramu, který bude v pracovním registru AX očekávat x-ovou souřadnici pixelu (0 až 639) a v registru BX y-ovou souřadnici (tedy prozatím 0 až 199). Náš podprogram se bude jmenovat putpixel:
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
...
...
...
ret
Provést musíme tři operace:
- Výpočet adresy, do níž se bude zapisovat (bude to jediný bajt)
- Výpočet masky pixelu, tedy o jaký bit v rámci bajtu se bude jednat
- Vlastní zápis, popř. přepis bitu/bitů na vypočtené adrese
10. Výpočet adresy pro zápis pixelu
Nejprve si vypočteme adresu bajtu v rámci video RAM, do kterého se bude zapisovat. V závislosti na y-ové souřadnici se bude jednat o bajt na stránce 0×b800 (prvních 8000 bajtů video RAM) nebo naopak o bajt na stránce o 8192 adres vyšší (druhých 8000 bajtů video RAM). Asi nejpřirozenější je zde test nejnižšího bitu y-ové souřadnice (tedy sudý:lichý) s podmíněným skokem:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16 ; přechod na druhou stránku Video RAM
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
Nyní tedy máme připraven registr ES (extra segment) a další adresování bude probíhat pouze uvnitř tohoto segmentu.
Vypočteme horizontální posun (v rámci jediného obrazového řádku) založený na x-ové souřadnici. Tuto souřadnici musíme vydělit osmi (osm pixelů na bajt), což se nejrychleji provede opakováním instrukce SHR (shift arithmetic right). Každá z těchto instrukcí je vykonána za dva takty:
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
Poněkud složitější je vertikální posun, kdy musíme y-ovou souřadnici pixelu vydělit dvěma (spodní bit volí stránku video RAM) a následně vynásobit 80. Prozatím použijeme operaci násobení MUL, která v tomto případě vynásobí obsah registru AX registrem DX a výsledek (obecně se nevejde do šestnácti bitů) je uložen do registrového páru DX:AX. My však víme, že v našem konkrétním případě se bez problémů vejdeme do třinácti bitů, takže DX můžeme ignorovat:
mov ax, bx ; y-ova souradnice
shr ax, 1 ; ignorovat nejnizsi bit s lichym/sudym radkem
mov dx, 80 ; vynasobit delkou radku v bajtech
mul dx ; AX - relativni posun v y-ovem smeru
Obě adresy, tedy horizontální i vertikální posun, stačí sečíst a výsledek uložit do registru DI:
add di, ax ; pricist vertikalni posun k posunu horizontalnimu
11. Výpočet masky pixelu a vlastní vykreslení
Nyní ještě musíme vypočítat masku pixelu, tj. který bit se bude v rámci celého bajtu měnit. To není příliš složité, protože pokud máme v registru AX x-ovou souřadnici, použijeme nejnižší tři bity pro posun. Takže nejdříve tyto tři bity získáme vymaskováním s hodnotou 7 (1+2+4):
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
Nyní již můžeme hodnotu v registru CL použít pro posun bitové masky 0b10000000 doprava, a to právě o CL bitů:
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
V AL je nyní maska pixelu a v registrovém páru ES:DI je adresa bajtu, ve které je pixel zakódován. Takže nám pouze stačí provést zápis:
stosb ; vlastni vykresleni pixelu
12. Úplný kód příkladu, který po svém spuštění vykreslí diagonální úsečku z jednotlivých pixelů
Použijme nyní výše uvedenou proceduru putpixel pro vykreslení diagonální úsečky. Budeme postupně měnit souřadnice uložené v registrech AX a BX a 200× zavoláme putpixel:
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
Výsledek:
Obrázek 9: Výsledná diagonální úsečka.
A takto vypadá úplný zdrojový kód demonstračního příkladu, který úsečku vykreslí:
; Vykresleni pixelu, zakladni varianta se 16bitovym nasobenim.
;
; preklad pomoci:
; nasm -f bin -o gfx_6.com gfx_6_putpixel_1.asm
;
; nebo pouze:
; nasm -o gfx_6.com gfx_6_putpixel_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 320x200 se ctyrmi barvami
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
wait_key
exit
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
mov ax, bx ; y-ova souradnice
shr ax, 1 ; ignorovat nejnizsi bit s lichym/sudym radkem
mov dx, 80 ; vynasobit delkou radku v bajtech
mul dx ; AX - relativni posun v y-ovem smeru
add di, ax ; pricist vertikalni posun k posunu horizontalnimu
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
stosb ; vlastni vykresleni pixelu
ret
13. První optimalizace: realizace násobení 8×8 bitů namísto 16×16 bitů
V předchozím demonstračním příkladu jsme při výpočtu vertikálního posunu využili násobení dvou šestnáctibitových hodnot, přičemž výsledkem byla hodnota 32bitová, uložená v registrovém páru DX:AX. Ovšem to není nutné, protože y-ová souřadnice nepřesáhne hodnotu 199 a násobíme konstantou 80. Obě hodnoty jsou tedy osmibitové a tudíž můžeme použít kratší variantu násobení (instrukce MUL):
mov al, bl ; y-ova souradnice
shr al, 1 ; ignorovat nejnizsi bit s lichym/sudym radkem
mov dl, 80 ; vynasobit delkou radku v bajtech
mul dl ; AX - relativni posun v y-ovem smeru
add di, ax ; pricist vertikalni posun k posunu horizontalnimu
Kolik času ušetříme? Čas násobení se zkrátí ze 118 až 133 taktů na 70 až 77 taktů. Mimochodem – zde je patrné, jak pomalé je vykonávání instrukcí řízených mikrokódem.
Ověřme si výsledek:
Obrázek 10: Výsledná diagonální úsečka.
A takto vypadá úplný zdrojový kód tohoto příkladu:
; Vykresleni pixelu, varianta s osmibitovym nasobenim.
;
; preklad pomoci:
; nasm -f bin -o gfx_6.com gfx_6_putpixel_2.asm
;
; nebo pouze:
; nasm -o gfx_6.com gfx_6_putpixel_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 ; nastaveni grafickeho rezimu 320x200 se ctyrmi barvami
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
wait_key
exit
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
mov al, bl ; y-ova souradnice
shr al, 1 ; ignorovat nejnizsi bit s lichym/sudym radkem
mov dl, 80 ; vynasobit delkou radku v bajtech
mul dl ; AX - relativni posun v y-ovem smeru
add di, ax ; pricist vertikalni posun k posunu horizontalnimu
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
stosb ; vlastni vykresleni pixelu
ret
14. Náhrada součinu za bitové posuny
Ve skutečnosti je ovšem možné (a vhodné) nahradit operaci násobení za kombinaci bitových posunů a součtů, minimálně v situaci, kdy se násobí relativně „kulatou“ konstantou 80. Součin n*80 je totiž možné nahradit za n*64 + n*16 a tyto částečné součiny již můžeme zaměnit za bitové posuny, tedy za n << 6 + n << 4. V assembleru to bude vypadat následovně.
Původní operace:
mov al, bl ; y-ova souradnice
shr al, 1 ; ignorovat nejnizsi bit s lichym/sudym radkem
mov dl, 80 ; vynasobit delkou radku v bajtech
mul dl ; AX - relativni posun v y-ovem smeru
Nová operace:
mov ax, bx ; y-ova souradnice
and al, 0xfe ; nejnizsi bit urcuje lichy/sudy radek -> nyni ignorovat
shl ax, 1 ; y*4
shl ax, 1 ; y*8
shl ax, 1 ; y*16
add di, ax ; pricist cast y-oveho posunu
shl ax, 1 ; y*32
shl ax, 1 ; y*64
; -> y*16 + y*64 = y*80
Rychlosti:
mov al, bl 2
shr al, 1 2
mov dl, 80 4
mul dl 70-77
---------------------------
78-85 cyklů
mov ax, bx 2
and al, 0xfe 4
shl ax, 1 2
shl ax, 1 2
shl ax, 1 2
add di, ax 3
shl ax, 1 2
shl ax, 1 2
-------------------------
19 cyklů
Zde je tedy výhoda ručních optimalizací značná!
Opět si zkontrolujme výsledek:
Obrázek 11: Pixely vykreslené bez použití operace součinu.
15. Výsledný zdrojový kód
Opět se pro úplnost podívejme na výsledný zdrojový kód:
; Vykresleni pixelu, varianta bez nasobeni.
;
; preklad pomoci:
; nasm -f bin -o gfx_6.com gfx_6_putpixel_3.asm
;
; nebo pouze:
; nasm -o gfx_6.com gfx_6_putpixel_3.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 320x200 se ctyrmi barvami
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
wait_key
exit
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
mov ax, bx ; y-ova souradnice
and al, 0xfe ; nejnizsi bit urcuje lichy/sudy radek -> nyni ignorovat
shl ax, 1 ; y*4
shl ax, 1 ; y*8
shl ax, 1 ; y*16
add di, ax ; pricist cast y-oveho posunu
shl ax, 1 ; y*32
shl ax, 1 ; y*64
add di, ax ; pricist zbytek y-oveho posunu
; -> y*16 + y*64 = y*80
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
stosb ; vlastni vykresleni pixelu
ret
16. Vykreslení pixelů na neprázdném pozadí
V předchozím textu jsem naznačil, že naše subrutina pro vykreslení jednoho pixelu není zcela korektní. Konkrétně nebude plně funkční ve chvíli, kdy se namísto prázdného pozadí kreslí na již vyplněnou bitmapu. Výsledek nebude uspokojivý – podívejte se sami, jak se v trpaslíkovi objevily černé pruhy, které jsou široké přesně osm pixelů:
Obrázek 12: Pixely se nejenom vykreslí, ale navíc se vymaže i jejich okolí (7 pixelů).
Tohoto efektu jsme dosáhli následujícím demonstračním příkladem, který v sobě spojuje vykreslení bitmapy s vykreslením jednotlivých pixelů:
; Vykresleni pixelu, varianta bez nasobeni.
; Pixely se prekresli pres obrazek.
;
; preklad pomoci:
; nasm -f bin -o gfx_6.com gfx_6_putpixel_4.asm
;
; nebo pouze:
; nasm -o gfx_6.com gfx_6_putpixel_4.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 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
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
add ax, 30
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
wait_key
exit
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
mov ax, bx ; y-ova souradnice
and al, 0xfe ; nejnizsi bit urcuje lichy/sudy radek -> nyni ignorovat
shl ax, 1 ; y*4
shl ax, 1 ; y*8
shl ax, 1 ; y*16
add di, ax ; pricist cast y-oveho posunu
shl ax, 1 ; y*32
shl ax, 1 ; y*64
add di, ax ; pricist zbytek y-oveho posunu
; -> y*16 + y*64 = y*80
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
stosb ; vlastni vykresleni pixelu
ret
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"
17. Korektní vykreslení bílých pixelů vůči jakémukoli pozadí
Prozatím jsme pixely vykreslovali zápisem celého bajtu (což je dobře, bitový přístup do paměti možný není), ovšem tak, že se zapsal vždy jeden bílý pixel a ostatní pixely byly vybarveny černě, bez ohledu na jejich předchozí barvu:
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
stosb ; vlastni vykresleni pixelu
Ovšem aby se nepoškodilo již nakreslené pozadí, musíme namísto přepisu všech osmi bitů v bajtu provést operaci OR, kterou nastavíme jediný bit:
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
or [es:di], al ; vlastni vykresleni pixelu
Instrukce STOSB je rychlejší; nová operace je o cca 12 instrukčních cyklů delší. Ovšem to se dalo čekat, protože se provádí čtení z paměti, vlastní operace OR a následuje zpětný zápis do paměti.
Výsledek bude vypadat následovně:
Obrázek 13: Nyní je již vše v pořádku, minimálně pro bílé pixely.
18. Úplný zdrojový kód dnešního posledního demonstračního příkladu
Dnešní poslední demonstrační příklad ve své úplné podobě (kód má již celých 129 bajtů!!!) bude vypadat následovně:
; Vykresleni pixelu, varianta bez nasobeni. Korektni maskovani.
; Pixely se prekresli pres obrazek.
;
; preklad pomoci:
; nasm -f bin -o gfx_6.com gfx_6_putpixel_4.asm
;
; nebo pouze:
; nasm -o gfx_6.com gfx_6_putpixel_4.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 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
mov ax, 0
opak:
mov bx, ax ; y-ová souřadnice
push ax
add ax, 30
call putpixel ; vykreslení pixelu
pop ax
inc ax ; pusun x+=1, y+=1
cmp ax, 200 ; hranice obrazovky?
jne opak ; ne-opakujeme
wait_key
exit
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
putpixel:
mov dx, 0xb800 ; zacatek prvni stranky Video RAM
test bx, 1 ; test, zda se jedna o sudy nebo lichy radek na obrazovce
jz odd_line
add dx, 8192/16
odd_line:
mov es, dx ; nyni obsahuje ES bud prvni stranku Video RAM nebo stranku druhou
mov cl, al
and cl, 7 ; pouze spodni 3 bity x-ove souradnice
shr ax, 1
shr ax, 1
shr ax, 1 ; x/8
mov di, ax ; horizontalni posun pocitany v bajtech
mov ax, bx ; y-ova souradnice
and al, 0xfe ; nejnizsi bit urcuje lichy/sudy radek -> nyni ignorovat
shl ax, 1 ; y*4
shl ax, 1 ; y*8
shl ax, 1 ; y*16
add di, ax ; pricist cast y-oveho posunu
shl ax, 1 ; y*32
shl ax, 1 ; y*64
add di, ax ; pricist zbytek y-oveho posunu
; -> y*16 + y*64 = y*80
mov al, 0x80 ; vypocitat masku pixelu
shr al, cl
or [es:di], al ; vlastni vykresleni pixelu
ret
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"
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 |
| 19 | gfx4_image7.asm | nastavení barvové palety před vykreslením obrázku, snížená intenzita barev | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image7.asm |
| 20 | gfx4_image8.asm | postupná změna barvy pozadí | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx4_image8.asm |
| 21 | gfx6_putpixel1.asm | vykreslení pixelu, základní varianta se 16bitovým násobením | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel1.asm |
| 22 | gfx6_putpixel2.asm | vykreslení pixelu, varianta s osmibitovým násobením | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel2.asm |
| 23 | gfx6_putpixel3.asm | vykreslení pixelu, varianta bez násobení | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel3.asm |
| 24 | gfx6_putpixel4.asm | vykreslení pixelu přes obrázek, nekorektní chování (přepis obrázku) | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel4.asm |
| 25 | gfx6_putpixel5.asm | vykreslení pixelu přes obrázek, korektní varianta pro bílé pixely | https://github.com/tisnik/8bit-fame/blob/master/pc-dos/gfx6_putpixel5.asm |
20. Odkazy na Internetu
- The Intel 8088 Architecture and Instruction Set
https://people.ece.ubc.ca/~edc/464/lectures/lec4.pdf - x86 Opcode Structure and Instruction Overview
https://pnx.tf/files/x86_opcode_structure_and_instruction_overview.pdf - x86 instruction listings (Wikipedia)
https://en.wikipedia.org/wiki/X86_instruction_listings - x86 assembly language (Wikipedia)
https://en.wikipedia.org/wiki/X86_assembly_language - Intel Assembler (Cheat sheet)
http://www.jegerlehner.ch/intel/IntelCodeTable.pdf - 25 Microchips That Shook the World
https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world - Chip Hall of Fame: MOS Technology 6502 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor - Chip Hall of Fame: Intel 8088 Microprocessor
https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-intel-8088-microprocessor - Jak se zrodil procesor?
https://www.root.cz/clanky/jak-se-zrodil-procesor/ - Apple II History Home
http://apple2history.org/ - The 8086/8088 Primer
https://www.stevemorse.org/8086/index.html - flat assembler: Assembly language resources
https://flatassembler.net/ - FASM na Wikipedii
https://en.wikipedia.org/wiki/FASM - Fresh IDE FASM inside
https://fresh.flatassembler.net/ - MS-DOS Version 4.0 Programmer's Reference
https://www.pcjs.org/documents/books/mspl13/msdos/dosref40/ - INT 21 – DOS Function Dispatcher (DOS)
https://www.stanislavs.org/helppc/int21.html - DOS API (Wikipedia)
https://en.wikipedia.org/wiki/DOS_API - Bit banging
https://en.wikipedia.org/wiki/Bit_banging - IBM Basic assembly language and successors (Wikipedia)
https://en.wikipedia.org/wiki/IBM_Basic_assembly_language_and_successors - X86 Assembly/Bootloaders
https://en.wikibooks.org/wiki/X86_Assembly/Bootloaders - Počátky grafiky na PC: grafické karty CGA a Hercules
https://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/ - 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/ - Karta EGA: první použitelná barevná grafika na PC
https://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/ - RGB Classic Games
https://www.classicdosgames.com/ - Turbo Assembler (Wikipedia)
https://en.wikipedia.org/wiki/Turbo_Assembler - Microsoft Macro Assembler
https://en.wikipedia.org/wiki/Microsoft_Macro_Assembler - IBM Personal Computer (Wikipedia)
https://en.wikipedia.org/wiki/IBM_Personal_Computer - Intel 8251
https://en.wikipedia.org/wiki/Intel_8251 - Intel 8253
https://en.wikipedia.org/wiki/Intel_8253 - Intel 8255
https://en.wikipedia.org/wiki/Intel_8255 - Intel 8257
https://en.wikipedia.org/wiki/Intel_8257 - Intel 8259
https://en.wikipedia.org/wiki/Intel_8259 - Support/peripheral/other chips – 6800 family
http://www.cpu-world.com/Support/6800.html - Motorola 6845
http://en.wikipedia.org/wiki/Motorola_6845 - The 6845 Cathode Ray Tube Controller (CRTC)
http://www.tinyvga.com/6845 - CRTC operation
http://www.6502.org/users/andre/hwinfo/crtc/crtc.html - 6845 – Motorola CRT Controller
https://stanislavs.org/helppc/6845.html - The 6845 Cathode Ray Tube Controller (CRTC)
http://www.tinyvga.com/6845 - Motorola 6845 and bitwise graphics
https://retrocomputing.stackexchange.com/questions/10996/motorola-6845-and-bitwise-graphics - IBM Monochrome Display Adapter
http://en.wikipedia.org/wiki/Monochrome_Display_Adapter - Color Graphics Adapter
http://en.wikipedia.org/wiki/Color_Graphics_Adapter - Color Graphics Adapter and the Brown color in IBM 5153 Color Display
https://www.aceinnova.com/en/electronics/cga-and-the-brown-color-in-ibm-5153-color-display/ - The Modern Retrocomputer: An Arduino Driven 6845 CRT Controller
https://hackaday.com/2017/05/14/the-modern-retrocomputer-an-arduino-driven-6845-crt-controller/ - flat assembler: Assembly language resources
https://flatassembler.net/ - FASM na Wikipedii
https://en.wikipedia.org/wiki/FASM - Fresh IDE FASM inside
https://fresh.flatassembler.net/ - MS-DOS Version 4.0 Programmer's Reference
https://www.pcjs.org/documents/books/mspl13/msdos/dosref40/ - INT 21 – DOS Function Dispatcher (DOS)
https://www.stanislavs.org/helppc/int21.html - DOS API (Wikipedia)
https://en.wikipedia.org/wiki/DOS_API - IBM Basic assembly language and successors (Wikipedia)
https://en.wikipedia.org/wiki/IBM_Basic_assembly_language_and_successors - X86 Assembly/Arithmetic
https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic - Art of Assembly – Arithmetic Instructions
http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter6/CH06–2.html - ASM Flags
http://www.cavestory.org/guides/csasm/guide/asm_flags.html - Status Register
https://en.wikipedia.org/wiki/Status_register - Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/library/l-gas-nasm/index.html - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Is it worthwhile to learn x86 assembly language today?
https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1 - Why Learn Assembly Language?
http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language - Is Assembly still relevant?
http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant - Why Learning Assembly Language Is Still a Good Idea
http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html - Assembly language today
http://beust.com/weblog/2004/06/23/assembly-language-today/ - Assembler: Význam assembleru dnes
http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz - Programming from the Ground Up Book – Summary
http://savannah.nongnu.org/projects/pgubook/ - DOSBox
https://www.dosbox.com/ - The C Programming Language
https://en.wikipedia.org/wiki/The_C_Programming_Language - Hercules Graphics Card (HCG)
https://en.wikipedia.org/wiki/Hercules_Graphics_Card - Complete 8086 instruction set
https://content.ctcd.edu/courses/cosc2325/m22/docs/emu8086ins.pdf - Complete 8086 instruction set
https://yassinebridi.github.io/asm-docs/8086_instruction_set.html - 8088 MPH by Hornet + CRTC + DESiRE (final version)
https://www.youtube.com/watch?v=hNRO7lno_DM - Area 5150 by CRTC & Hornet (Party Version) / IBM PC+CGA Demo, Hardware Capture
https://www.youtube.com/watch?v=fWDxdoRTZPc - 80×86 Integer Instruction Set Timings (8088 – Pentium)
http://aturing.umcs.maine.edu/~meadow/courses/cos335/80×86-Integer-Instruction-Set-Clocks.pdf