Moderná počítačová grafika sa v dnešnej dobe reprezentuje prostredníctvom priemyselných grafických štandardov DirectX alebo OpenGL vytvorených na začiatku 90. rokov 20.storočia. Do vtedy bolo vykresľovanie počítačovej grafiky realizované tzv. softvérovým spôsobom, teda spôsobom, kde na vykresľovaní grafickej informácie neparticipoval hardware za plnej účasti, resp. špecifickým spôsobom.
Aj keď grafické knižnice neboli koncipované v rámci 3D vykresľovania v zmysle polygónov v trojrozmernom priestore, poskytovali programátorom určité grafické benefity, najmä čo sa týka pamäťových nárokov.
Pomerne častou grafickou knižnicou vo vtedajšej dobe bola knižnica graphics.h v jazyku C/C++ vytvorená pre kompiler GCC. Bola aplikovateľná aj pre vtedajší programovací jazyk Pascal a teda vývojové prostredie Turbo Pascal a Turbo C od už neexistujúcej spoločnosti Borland.
Bola to veľmi jednoducho použiteľná grafická knižnica. Navyše je možné túto knižnicu používať aj v dnešnej modernej dobe. A to buď použitím modernejších IDE, alebo klasickom DOS emulátore DOSBox. Samozrejme existujú aj rôzne variácie na báze Pascalu vo forme Free Pascalu a mnohých iných verzií.
Vyskúšal som nainštalovať graphics.h podľa tutoriálu v IDE Code::Blocks podľa dostupného návodu: (v originálnom znení)
1. Install Code::Blocks
Make sure you have installed Code::Blocks IDE on your machine. If you don't have this IDE or have any issue with compiler download and install it from here. http://sourceforge.net/projects/codeblocks/files/Binaries/16.01/Windows/codeblocks-16.01mingw-setup.exe
2. Download the required header files
We need few files to be included in the lib folder of Code::Blocks. Download the files from here https://github.com/SagarGaniga/Graphics-Library
3. Include graphics.h and winbgim.h
Copy and Paste the graphics.h and winbgim.h files into include folder of Code::Blocks directory.
Path: C:\Program Files (x86)\CodeBlocks\MinGW\include
4. Include libbgi.a
Copy and paste libbgi.a file in the lib folder of Code:Blocks
Path: C:\Program Files (x86)\CodeBlocks\MinGW\lib
5. Add Link Libraries in Linker Setting
In the Code::Blocks application go to, Settings > Compiler
In the Global Compiler setting, click on the Linker Settings
In Link Libraries, Add and browse to C:\Program Files (x86)\CodeBlocks\MinGW\lib\ and select libbgi.a.
Paste this in the Other Linker Option tab of Linker Settings (i.e. on the right-hand side)
-lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
Save the setting and restart the application
Vyskúšal som napísať jednoduchý program na vykreslenie kružnice pomocou funkcie circle().
#include<stdio.h>
#include<conio.h>
#include<graphics.h>
int main()
{
int gd = DETECT, gm;
initgraph(&gd, &gm, "C:\\TURBOC3\\BGI");
circle(100, 100, 50);
getch();
closegraph();
return 0;
}
Program mi na prvé spustenie nefungoval. Bolo nutné prepísať súbor main.cc na main.cpp, aby prekladač rozpoznal podľa prípony súboru C++ kompiler, prekladač. Následne je nutné doinštalovať IDE Turbo C++, aj keď tam nie je priama súvislosť s IDE Code::Blocks. Je to z toho dôvodu, ako je možné vidieť v zdrojovom kóde, že je potrebné definovať BGI (Borland Graphics Interface), teda samotnú grafickú knižnicu vo forme priamej cesty k adresáru BGI.
Analogickým spôsobom je možné napísať rovnaký program v DOS emunlátore DOSBox v IDE Turbo C++.
A výsledný programový výstup bude vyzerať nasledovne.
Takže je možné vidieť použitie grafickej knižnice graphics.h je možné aplikovať na DOS emulátore a teda pracovať v pôvodnom DOS režime alebo použiť novší ekvivalent napr. IDE Code::Blocks. Samozrejme je možné použiť aj vývojové prostredie Visual Studio, avšak sú tam určité obmedzenia, zmeny. Konkrétne by bolo potrebné zadovážiť binárny súbor vo forme libbgi.lib, namiesto pôvodného súboru libbgi.a, ktorý je používaný pre prostredie Code::Blocks.
Otázka je, či je možné napísať jednoduchý ovládač k grafickej karte, ktorý by len vykreslil pixely na obrazovku. Žiadna ďalšia špeciálna funkcionalita by sa nepožadovala.
Je to nemožné ?
Je to možné a dokonca je to relatívne veľmi jednoduché. Existuje určité vývojové prostredie MASM32 pre písanie zdrojových kódov v programovacom jazyku Assembler. Základom všetkého je vykreslenie 1 pixelu na obrazovku počítača. Pretože ostatné zložitejšie funkcie, ako napríklad kreslenie čiary (Line), kružnice (Circle) a podobne, pozostávajú len z vykreslenia jednotlivých pixelov určitým algoritmom vykreslenia. Otázka na fóre teda bola koncipovaná nasledovne:
Teda, autor chcel zobraziť 1 pixel na obrazovke počítača. Odpoveď bola taká, že teda možné to je a konkrétne bol uvedený zdrojový kód s nastavením pixelu a definovaním farby.
include \masm32\MasmBasic\Res\MbGui.asm ; install the library
Event Paint
For_ count=0 To 900
invoke SetPixel, PtDC, count, 100, RgbCol(0, 0, count) ; horizontal line
invoke SetPixel, PtDC, count, count, RgbCol(count, 0, 0) ; diagonal
invoke SetPixel, PtDC, 100, count, RgbCol(0, count, 0) ; vertical
Next
GuiEnd
Takže je možné vykresliť 1 pixel na obrazovku počítača pomocou programovacieho jazyka Assembler. Ono, samozrejme bolo to možné refaktorovať do iného vývojového prostredia a iného programovacieho jazyka, napr. jazyka C/C++, ale aj jazyka vyššej úrovne s tým, že by bola napísaná určitá "knižnica" práve v jazyku ASM na poskytnutie funkcie pre vykreslenie pixelu.
Ďalšia otázka je, akým spôsobom je možné pristúpiť k grafickej karte počítača. Pretože ako je možné vidieť v zdrojovom kóde, bola použitá nejaká "ASM knižnica", teda knižnica pre Assembler, ktorá v sebe definovala ďalšie náležitosti ohľadne použitých funkcií, konkrétne funkcie SetPixel. V útrobách prilinkovaného súboru MbGui.asm by bola ďalšia funkcionalita ohľadne určitých volaní pre akési "grafické jadro". Na otázku z fóra, kde sa autora dopytoval odpovede, či teda je možné napísať grafický ovládač v assembleri dostal nasledujúcu odpoveď.
Ako je možné vidieť, odpoveď je na úrovni toho, že teda grafický ovládač nemá priamy prístup k grafickej karte, ale namiesto toho interaguje s určitým firmwarom ako sprostredkovateľom medzi programovým kódom, operačným systémom a grafickou kartou. Problém je ten, že daný firmware nie je dostatočne dobre zdokumentovaný a teda obsahuje len akési "BLOB jadro" vo forme binárneho kódu, teda strojového jazyka. Samotný zdrojový kód pre interakciu medzi programovým kódom a priameho prístupu k grafickej karte neexistuje.
Na druhej strane som zistil v čase úpravy tohto článku (dňa 4.12.2025), že autor Dave Plummer, ktorý vystupuje na YouTube s kanálom Dave's Garage, https://www.youtube.com/watch?v=2bVhyB7Av8s, že existuje spôsob ako vykresliť 1 pixel na obrazovku počítača bez toho, aby ste museli programovať komplikované grafické ovládače videokarty. Na tomto mieste uvádzam trochu skrátenú verziu jeho zdrojového kódu, ktorý je prístupný vo forme videa a možno niekde na jeho GitHube.
GitHub: https://github.com/indicatrix-git/retro-code/blob/main/DirectAccessGraphics.cpp
#include stdio.h
#include stdlib.h
#include string.h
typedef unsigned char byte;
//Screen memory is placed at A000-BFFF, 320x200 pixels, mapped right to left within each horizontal byte
byte * screen = (byte *) 0xA000;
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 200
#define CHARWIDTH 8
#define CHARHEIGHT 8
#define BYTESPERROW (SCREEN_WIDTH / 8)
#define BYTESPERCHARROW (BYTESPERROW * 8)
#define CHARSPERROW (SCREEN_WIDTH / CHARWIDTH)
#define ROWSPERCOLUMN (SCREEN_HEIGHT / CHARHEIGHT)
void ClearScreen(byte val)
{
int totalBytes = SCREEN_HEIGHT * BYTESPERROW;
int i;
for(i = 0; i < totalBytes; i++)
screen[i] = val;
}
void SETPIXEL(int x, int y, byte b)
{
byte * pb = screen;
pb += x >> 3;
pb+= y * BYTESPERROW;
if(b)
*(pb) |= (0b10000000 >> (x & 7));
else
*(pb) &= ~(0b10000000 >> (x & 7));
}
void DRAWPIXEL(int x, int y)
{
byte * bp = screen;
pb += x >> 3;
pb += y * BYTESOERROW;
*(pb) |= (0b10000000 >> (x & 7));
}
void DrawLine(int x0, int y0, int x1, int y1, byte val)
{
int dx = abs(x1-x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1-y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy)/2, e2;
while(1)
{
SETPIXEL(x0, y0, val);
if(x0 == x1 && y0 == y1)
break;
e2 = err;
if(e2 > -dx)
{
err -= dy;
x0 += sx;
}
if(e2 < dy)
{
err += dx;
y0 += sy;
}
}
}
int main(void)
{
ClearScreen(0x00);
return 0;
}
Ako je možné vidieť, prístup do grafickej karty sa realizuje veľmi jednoducho. Uvedie sa adresa na strane pointera, smerníka. Nič viac. Jazyk C umožňuje prístup k adrese v pamäti. Stále som to používal, ak som programoval v jazyku C, avšak ako adresu pre prakticky čokoľvek som nikdy explicitne nedefinoval. Teda, bola definovaná implicitne, pre konkrétny prípad zamýšľaného alokovaného objektu. Napríklad pre nejaké číslo som definoval pointer na adresu, kde toto číslo bolo možné zapísať. Ale nikdy som nedefinoval aká adresa by to mala byť. To je v podstate tá finta v tomto prípade. Teraz definujete presnú adresu, rozsah adries video pamäte, kde sa má zapísať nejaký pixel, nejaké číslo. Trik spočíva v tom, že musíte vedieť rozsah platnosti adries video pamäte. Ale zaujímavé je, že vám stačí vedieť len toto a nič iné. Je to defacto priamy prístup do pamäte, aj keď nie je nejaký super optimalizovaný, ale je to spôsob, akým môžte zapísať 1 pixel do obrazovej pamäte. Iný autor mi na toto napísal, že v podstate je to ľahké preto, lebo video pamäť ako taká sa mapuje do RAM pamäte. Aj keď nie som si istý, či myslel priamo toto, pretože autor tohto kódu uvádza explicitne zápis do adries video pamäte, nie nejakej dodatočnej obrazovej pamäte.
V každom prípade konkrétne tento príklad nefunguje pre dnešné moderné počítače, pretože pravdepodobne je nutné revidovať rozsah video adries, nakoľko daný rozsah video adries nekorešponduje pre bežný typ video kariet. Vyskúšal som to pre prostredie operačného systému MS DOS prostredníctvom emulátoru DOSBOX. Neevidoval som problém s pamäťou a syntakticky to bolo všetko v poriadku, okrem prefixu binárneho čísla. Na nasledujúcom odkaze je možné vyčítať, že prefix pre binárne číslo 0b nebol pre staršie kompilery jazyk C podporovaný. Konkrétne je to citované na tomto mieste:
(https://autoconfig.stm32world.com/images/4/4c/C_Binary_Literal_Prefix_.pdf)3. Why 0b Was Absent in K and R C
The original definition of C, documented in the first edition of "The C Programming Language" by Brian Kernighan and Dennis Ritchie (K and R C, 1978), included support for decimal, octal (prefix 0), and hexadecimal (prefix 0x or 0X) integer literals.10 However, it lacked a standard way to represent binary literals. This omission was not an oversight but rather a reflection of the language's history, design philosophy, and the computing environment of the time.
Revidoval som zápis z 0b na celé číslo po konverzii z binárneho čísla. Teda číslo 0b10000000 zodpovedá v decimálnom zobrazení číslu 128. Bitové operácie je možné realizovať aj báze celých čísel, teda vykonávate bitové operácie nad decimálnymi číslami, čoho výsledkom bude binárna operácia. Ak som prepísal danú binárnu adresu na decimálnu adresu, teda 128, tak program prestal vykazovať chyby, avšak reálne nič nevykreslil. Daný program nedefinuje farbu pixelu, možno je čierny pixel na čiernom pozadí a preto to nie je vidieť. Po zmene hodnoty "val" v parametre funkcie Setpixel() nenastala žiadna zmena a v uvedenom čase som nevedel program revidovať za účelom definovania konkrétnej farby pixelu, ak bol vôbec v tomto smere nejaký problém.
Ďalší príklad od iného autora, ktorého celé meno som nevedel nájsť na jeho webstránkach, takže uvádzam len jeho webstránku a pravdepodobné priezvisko Hackman, je príklad v programovom prostredí Turbo C++ a teda s emulátorom DOSBOX. Aj keď v predošlom príklade nebolo nutné kompilovať prostredníctvom kompilátora Turbo C++, v tomto prípade to je nutné, nakoľko daný zdrojový kód je funkčný len v tomto prostredí a s určitými malými zmenami aj v inom prostredí kompilátora (podľa slov autora kódu).
GitHub: https://github.com/indicatrix-git/retro-code/blob/main/DirectAccessPixel.cpp
//ORIGINAL AUTHOR: Hackman's Realm (https://lateblt.tripod.com/aboutme.htm)
#include //Needed for the REGS structure
void main()
{
unsigned char far* vgaspace = (unsigned char far*) 0xA0000000;
union REGS regs;
regs.h.ah = 0;
regs.h.al = 0x13;
int86(0x10, ®s, ®s);
vgaspace[0x200] = 0x44;
vgaspace[0x6000] = 0x123;
}
Ako je možné vidieť z uvedeného kódu v hlavičke sa volá BIOS. Podľa slov autora toto nie je nutné a teda programový kód od union po int86 by tam nemusel figurovať. Sú to registre počítača, ktoré definujú VGA rozhranie (0x13) a nič viac. Najdôležitejší je prvý riadok, definovanie vgaspace, čo je možné napísať aj priamo na 1 pixel takýmto spôsobom:
*(unsigned char far*) 0xA0000200 = 0x44;
Toto je minimálny kód na priamy prístup do video pamäte s pozíciou 200 a farbou 0x44 (odtieň žltej farby). Video pamäť začína od 0xA0000000, čo predstavuje súradnice [x, y] = [0, 0]. Výsledný screenshot pre predchádzajúci kód s dvoma pixelmi vyzerá nasledovne.
Takže ako je možné vidieť, vykreslili sa dva pixely, v odtieni žltej farby a fialovej farby na rôznych pozíciach. Je to neuveriteľné, ale funguje to :)
Takýmto spôsobom je možné vykresliť 1 pixel na obrazovku počítača. Autor uvádza vykreslenie pixelu aj v iných programovacích jazykoch, napr. BASIC, Assembler a PASCAL a existujú aj mnohé iné, ale problém je ten, že stále používa nejaký emulátor pre dané archaické programové prostredie. Spomína, že je možné refaktorovať kód aj do iných kompilátorov a teda každý kompilátor bude používať špecifické nastavenie pre niektoré súčasti, medzi inými aj napríklad register procesora. Osobne som to v novších kompileroch netestoval, ale to si nechám niekedy na dlhé zimné večery :)





0 comments:
Post a Comment