Problematika ukazatelů v překladačích jazyka C pro systém DOS

3. 6. 2025
Doba čtení: 41 minut

Sdílet

Autor: Depositphotos
Jaký byl největší problém systému DOS? Neexistence pokročilejších funkcí, nutnost explicitní konfigurace hardwaru, či něco jiného? Z pohledu programátorů se jednalo o práci s pamětí organizované systémem segment:offset.

Obsah

1. Problematika ukazatelů v překladačích jazyka C pro systém DOS

2. Adresování stylem segment:offset

3. Rozsah paměti IBM PC s mikroprocesorem 8086 či 8088

4. Rozsah adresovatelné paměti mikroprocesoru 80286

5. Může offset přesáhnout hranici jednoho segmentu?

6. Paměťové modely překladačů jazyka C v reálném režimu

7. Praktická část: paměťové modely překladače Borland C++ 3.0

8. Velikosti ukazatelů a základní aritmetika s ukazateli v praxi

9. Funkce vracející větší z hodnot předaných přes ukazatel

10. Překlad pro paměťové modely s krátkými ukazateli pro data

11. Překlad pro paměťové modely s dlouhými ukazateli pro data

12. Alokace a přístup k prvkům pole přesahujícího velikost jednoho segmentu

13. Alokace rozsáhlého pole s jeho inicializací

14. Výsledek překladu

15. Pomocná makra pro práci s ukazateli se segmenty a offsety

16. Demonstrační příklad: nastavení grafického režimu 13h a vyplnění obrazovky barevným vzorkem

17. Výsledek překladu do strojového kódu

18. Překlad programové smyčky vyplňující obrazovou paměť

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

20. Odkazy na Internetu

1. Problematika ukazatelů v překladačích jazyka C pro systém DOS

Platforma IBM PC a operační systém DOS byly v mnoha ohledech problematické, a to jak z pohledu uživatelů, tak i (a to možná ještě více) vývojářů. Samotný tandem DOS+BIOS například nabízel pouze základní služby pro práci se soubory, procesy a do jisté míry i s HW zařízeními. Ovšem například způsob mapování mezi IO porty procesoru a fyzickými porty, mapování mezi paměťovou oblastí zařízení a adresovým prostorem z pohledu procesoru nebo výběr HW přerušení, která bude zařízení používat, se řešil dalšími prostředky, které ležely mimo možnosti DOSu a BIOSu. Typicky se tyto hodnoty nastavovaly přepínači umístěnými přímo na zařízení a pokud bylo nastavení nekorektní, samotný systém většinou ani nedokázal tuto situaci detekovat (a už vůbec ne ji opravit).

Ovšem problematický byl i samotný vývoj aplikací pro IBM PC s DOSem. Jádro problému spočívalo v tom, jakým způsobem mikroprocesory řady Intel 8088/8086 pracovaly s adresami (tedy z pohledu jazyka C s ukazateli). Nejednalo se o jedinou hodnotu, ale o dvojici hodnot segment+offset, které se většinou zapisovaly stylem segment:offset. Tento koncept do značné míry komplikoval ukazatelovou aritmetiku. Kvůli tomu, že samotná nutnost explicitně pracovat se segmenty komplikovala programový kód, nabízeli výrobci překladačů různé paměťové modely, které umožňovaly (pro menší aplikace nebo pro aplikace s malými nároky na operační paměť) existenci segmentů do jisté míry ignorovat. Problémy ovšem nastaly s přenositelností nebo s přechodem na „větší“ paměťový model.

A právě touto problematikou se budeme zabývat v dnešním článku.

2. Adresování stylem segment:offset

Jak jsme si již naznačili v úvodní kapitole, používaly mikroprocesory Intel 8088/8086 adresy reprezentované dvojicí hodnot nazývaných segment a offset. Obě tyto hodnoty byly šestnáctibitové, tj. jak segment, tak i offset byly v rozsahu 0×0000 až 0×ffff. Logická adresa se přitom přímo v mikroprocesoru počítala následujícím způsobem:

adresa = segment × 16 + offset

Samozřejmě se ovšem nemuselo provádět pomalé násobení, protože postačovalo hodnotu segmentu posunout doleva o čtyři bity, takže výpočet ve skutečnosti probíhal zhruba následovně:

adresa = (segment << 4) + offset

Z pohledu programátora byl segment uložen v jednom ze segmentových registrů nazvaných CS, DS, ES a SS. V typickém programu obsahoval CS segment s programovým kódem, DS segment s daty a SS segment se zásobníkem (a nikde není řečeno, že tyto segmenty nemohou být totožné). Registr ES se většinou mohl libovolně měnit a použít například pro přístup do obrazové paměti karty CGA:

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

Tento koncept má z pohledu programátora v céčku minimálně dvě nevýhody:

  1. Problematická aritmetika s ukazateli (už jen zvýšení ukazatele o jedničku může či nemusí fungovat)
  2. Problematické porovnání ukazatelů, protože i dvě rozdílné dvojice segment:offset mohou ve skutečnosti reprezentovat tutéž adresu

3. Rozsah paměti IBM PC s mikroprocesorem 8086 či 8088

Vraťme se na chvíli k prapůvodním mikroprocesorům Intel 8086/8088, se kterými byla další řada mikroprocesorů do značné míry kompatibilní. Připomeňme si, že tyto čipy měly fyzicky vyvedených pouze dvacet adresových vodičů a dokázaly tak adresovat přesně 1MB paměti (ani o bajt více – nedokázaly tedy přistoupit k HMA!). Výpočet adresy probíhal na základě kombinace segmentu a offsetu přesně takovým způsobem, jak bylo popsáno v předchozí kapitole.

Teoreticky tedy může adresa (říkejme jí logická adresa) přesáhnout přes 1MB v případě, že použijeme vysoké číslo segmentu (0×f000 a výše) i offsetu. Ovšem jak bude vypadat fyzická adresa (přenesená na adresovou sběrnici) na mikroprocesorech 8086/8088? Napoví nám obrázky jejich pinů, v nichž nalezneme i adresové vodiče:

Obrázek 1: Piny mikroprocesoru Intel 8086.

Obrázek 2: Piny mikroprocesoru Intel 8088.

U mikroprocesoru Intel 8086 nalezneme mj. i piny AD0AD15 a taktéž piny A16A19. Naproti tomu u čipu Intel 8088 se jedná o piny AD0AD7 následované piny A8A19. Žádné jiné rozdíly mezi oběma čipy přitom z externího HW pohledu neexistují. Piny začínající znaky AD se přitom používají pro adresovou i datovou sběrnici (data jsou multiplexována, což byl velmi často používaný způsob – nejprve se jedním směrem přenesla adresa a poté se provedlo čtení či zápis dat), zatímco piny začínající jen znakem A značí vodiče pouze adresové sběrnice. Co to znamená? Intel 8086 má externí datovou sběrnici se šestnácti vodiči a adresovou sběrnici s dvaceti vodiči. Naproti tomu Intel 8088 má pouze osmibitovou externí datovou sběrnici (zlevnění celého systému na úkor rychlosti), ovšem adresová sběrnice má pořád dvacet vodičů.

To ovšem v důsledku znamená, že fyzická adresa nepřesáhne rozsah jednoho megabajtu, ale „přeteče“ do nultého segmentu.

4. Rozsah adresovatelné paměti mikroprocesoru 80286

Mikroprocesor Intel 80286, který představuje druhou generaci čipů z rodiny 80×86, má oproti mikroprocesorům Intel 8086/8088 odlišně koncipované piny, což je dobře patrné z následujícího obrázku:

Obrázek 3: Piny mikroprocesoru Intel 80286.

Vidíme zde dva důležité rozdíly. Prvním rozdílem je oddělení datové sběrnice (která je mimochodem stále šestnáctibitová) od sběrnice adresové. To znamená, že jsou vyvedeny samostatné vodiče D0D15 pro obousměrné přenosy dat a není zapotřebí řešit multiplexing dat a adres. Pro nás je ovšem dnes důležitější fakt, že adresových vodičů je nyní více; konkrétně se jedná o piny A0A23. To tedy znamená, že lze adresovat 224 bajtů, protože lze opět pracovat s jednotlivými bajty a nikoli jen s celými šestnáctibitovými slovy. Fyzický paměťový rozsah mikroprocesorů Intel 80286 je tedy 16MB – o mnoho více, než tomu bylo v případě původní první generace čipů z rodiny 80×86.

Aby bylo možné využít celý paměťový rozsah, podporují mikroprocesory 80286 takzvaný chráněný režim, v němž lze navíc (jak již jeho název napovídá) na HW úrovni zajistit například to, že se nepřekročí nastavená velikost zásobníku, že se nebude (například virem) modifikovat programový kód atd. Ke konceptu chráněného režimu se ještě vrátíme. Jednalo se o důležitou technologii, která však v případě 80286 nebyla plně využívána (a to hned  několika důvodů).

Co se však stane na počítači vybaveném mikroprocesorem 80286 (s adresovým rozsahem 224 bajtů) v případě, že výpočet přesáhne přes hranici jednoho megabajtu? To je totiž docela dobře možné, což naznačuje i další tabulka:

Segment (16b) Offset (16b) Fyzická adresa (24b) Poznámka
ffff 0000 ffff0 jsme stále v rozsahu 1MB
ffff 000f fffff jsme stále v rozsahu 1MB
f000 ffff fffff jsme stále v rozsahu 1MB
ffff 0010 100000 první adresa, která se již nevejde do rozsahu 1MB
f001 ffff 100000 odlišná reprezentace stejné adresy
ffff ffff 10ffef přesáhli jsme rozsah 1MB – nejvyšší možná adresa

Z této tabulky je patrné, že fyzická adresa skutečně může přesáhnout rozsah 1MB, což na původních čipech 8086/8088 nebylo možné, už jen z toho důvodu, že neexistovat adresový vodič A20 (tedy dvacátý první vodič, protože je indexujeme od nuly).

O kolik bajtů (či adres) přesáhneme onu mýtickou hranici jednoho megabajtu si můžeme snadno vypočítat:

0xffff0 + 0xffff - 0xfffff = 65520 bajtů/adres

K dispozici tedy máme prakticky celých 64kB navíc (kromě šestnácti bajtů na konci). Dnes se to sice může zdát jako zanedbatelná hodnota, ale právě kvůli oněm šedesáti čtyřem kilobajtů se komplikoval návrh PC i BIOSu až do cca roku 2009.

5. Může offset přesáhnout hranici jednoho segmentu?

Víme již, že výpočet adresy probíhá na základě kombinace segmentu a offsetu. Jak segment, tak i offset jsou šestnáctibitové hodnoty, které se kombinují způsobem naznačeným výše:

adresa = segment × 16 + offset

Jenže to není přesná (resp. ucelená) informace. Je nutno dodat, jakým způsobem byl vypočítán offset. Mikroprocesory řady Intel 8086/8088 totiž podporovaly tyto adresovací režimy:

Použitý zápis v assembleru Adresovací režim
přímá adresa (16bit) displacement/direct
   
[BX] register indirect
[SI] register indirect
[DI] register indirect
   
[BX+SI] based indexed mode
[BX+DI] based indexed mode
[BP+SI] based indexed mode
[BP+DI] based indexed mode
   
[BP+offset8_bit] based mode
[BX+offset8_bit] based mode
[SI+offset8_bit] indexed mode
[DI+offset8_bit] indexed mode
   
[BP+offset16_bit] based mode
[BX+offset16_bit] based mode
[SI+offset16_bit] indexed mode
[DI+offset16_bit] indexed mode
   
[BX+SI+offset8_bit] based indexed displacement
[BX+DI+offset8_bit] based indexed displacement
[BP+SI+offset8_bit] based indexed displacement
[BP+DI+offset8_bit] based indexed displacement
   
[BX+SI+offset16_bit] based indexed displacement
[BX+DI+offset16_bit] based indexed displacement
[BP+SI+offset16_bit] based indexed displacement
[BP+DI+offset16_bit] based indexed displacement

Co to znamená v praxi? Řekněme, že v segmentovém registru DS (data segment) bude uložena nula a v registru BX bude uložena hodnota 0×8000. Následně použijeme instrukci:

mov DS:[BX+0x8001], AL

Otázka zní, do jaké fyzické buňky RAM se uloží obsah registru AL? Máme přitom k dispozici dvě varianty výpočtu. Triviální dosazení do vzorce pro výpočet adresy by vedlo k výpočtu:

0x0000 × 16 + 0x8000 + 0x8000 = 0x10000

Ovšem můžeme si také uvědomit, že sčítačka pro offsety je realizována v šestnáctibitové aritmeticko-logické jednotce a ve skutečnosti tedy proběhne spíše něco takového:

0x0000 × 16 + (0x8000 + 0x8000) & 0xffff = 0x00000

Ve skutečnosti je správný druhý výpočet, protože offset je na čipech Intel 8086/8088 vždy šestnáctibitový. To znamená, že veškeré výpočty offsetu uvedené v předchozí tabulce za všech předpokladů vedou k hodnotám v rozsahu 0×0000 až 0×ffff a nikdy tedy nepřekročíme velikost segmentu.

Poznámka: tato vlastnost sice přímo vychází s interní realizace výpočtů adresy na čipech 8086/8088, ovšem později musela být emulována – přesněji řečeno emulována v reálném režimu.

6. Paměťové modely překladačů jazyka C v reálném režimu

Z výše uvedených informací je zřejmé, že práce s adresami ve formátu segment:offset je poměrně problematická. A navíc existují aplikace (krátké utilitky, rezidentní programy, krátká dema – intra atd.), které se spokojí s maximálně 64kB pro programový kód a/nebo 64kB pro data. Tvůrci překladačů jazyka C se snažili podporovat i tyto méně náročné aplikace tím, že umožnili používání „krátkých ukazatelů“ (near pointers), což jsou ukazatele platné pouze v rámci jednoho segmentu. Díky tomu, že segment nebude měněn, bude veškerá ukazatelová aritmetika či porovnání ukazatelů opět korektní.

Takto vznikl koncept paměťových modelů. Původně existovaly čtyři modely:

Model Ukazatel na data
krátký dlouhý
Ukazatel na
kód
krátký Small Compact
dlouhý Medium Large
Poznámka: termínem „krátký“ jsou myšleny ukazatele obsahující jen offset (16bitů) a termínem „dlouhý“ ukazatele reprezentované dvojicí segment:offset.

Záleželo tedy jen na programátorovi, který z modelů si vybere. Typicky pro rezidentní programy (typu ovladač myši) postačoval model s maximálně 64kB kódu a 64kB dat. Mnohé i krátké programy potřebovaly větší prostor pro data (až do limitu 1MB) a rozsáhlé aplikace pak větší prostor pro data i pro programový kód (oba limity jsou 1MB).

Překladače C pro systém DOS.

Obrázek 4: Podporu pro paměťové modely je nutné explicitně nainstalovat (Borland C++ 2.0).

7. Praktická část: paměťové modely překladače Borland C++ 3.0

Všechny důležité rysy ukazatelů reprezentovaných stylem segment:offset i paměťových modelů si ukážeme na typickém dobovém představiteli céčkových překladačů. Překladač programovacího jazyka C a C++ nazvaný Borland C++ 3.0 podporoval v operačním systému DOS celkem šest různých paměťových modelů. Tyto modely jsou vypsány v následující tabulce:

# Paměťový model Ukazatele Stručný popis modelu
1 Tiny krátké 64kB pro program i data, všechny čtyři segmentové registry jsou totožné (a tedy limit 64kB se vztahuje na celek)
2 Small krátké 64kB programový kód, 64kB pro data
3 Medium dlouhé pro program, krátké pro data 1MB programový kód, 64kB pro data
4 Compact krátké pro program, dlouhé pro data 64kB programový kód, 1MB pro data
5 Large dlouhé 1MB programový kód, 1MB pro data
6 Huge dlouhé 1MB pro programový kód, 1MB pro data

Paměťový model Huge jako jediný neomezuje délku paměťových struktur na 64kB. Týká se to většinou polí, které tuto délku mohou překročit – ovšem situace není zdaleka tak jednoduchá, jak by to mohlo z tohoto popisu vypadat (viz další text).

Překladače C pro systém DOS.

Obrázek 5: Podporu pro paměťové modely je nutné explicitně nainstalovat (Borland C++ 3.0).

8. Velikosti ukazatelů a základní aritmetika s ukazateli v praxi

Volba paměťového modelu určuje i velikost ukazatelů. Ovšem navíc měl programátor možnost velikost ukazatelů specifikovat přímo takzvaným modifikátorem. Ukažme si tuto vlastnost na jednoduchém příkladu, který si uvedeme ve třech modifikacích. Vytvoříme ukazatel obsahující hodnotu 0×ffff (tedy vlastně segment je buď nespecifikován nebo je nulový), vypíšeme jeho hodnotu a následně ji zvýšíme o jedničku:

#include <stdio.h>
 
int main(void) {
    unsigned char *ptr = (unsigned char*)0xffff;
    printf("%p\n", ptr);
    ptr++;
    printf("%p\n", ptr);
    return 0;
}

V paměťovém modelu Tiny se pracuje jen s offsety, protože všechny segmentové registry se nemění. Proto bude ukazatel šestnáctibitový a vypíše se:

ffff
0000
Poznámka: jak se dalo předpokládat, hodnota ukazatele přetekla z maximální na minimální hodnotu.

Zkusme se nyní přepnout do paměťového režimu s limitem 1MB pro data. Zde je již možné používat dlouhé ukazatele, které používají modifikátor far:

#include <stdio.h>
 
int main(void) {
    unsigned char far *ptr = (unsigned char*)0xffff;
    printf("%p\n", ptr);
    ptr++;
    printf("%p\n", ptr);
    return 0;
}

Mohlo by se zdát, že nyní zvýšení hodnoty ukazatele povede k tomu, že se změní jeho segmentová část. Ovšem není tomu tak, protože to by pro překladač znamenalo vygenerování dalších pomocných instrukcí (neexistuje instrukce typu „zvyš dvojici ES:DI“). Z hodnoty 0000:ffff se ve skutečnosti stane hodnota 0000:0000! Zkusme si to přímo v IDE:

Překladače C pro systém DOS.

Obrázek 6: Poněkud neslavný pokus o zvýšení hodnoty ukazatele.

Pokud je nutné pracovat s ukazateli přesahujícími hranici segmentů a současně podporujícími ukazatelovou aritmetiku, je nutné použít modifikátor huge:

#include <stdio.h>
 
int main(void) {
    unsigned char huge *ptr = (unsigned char*)0xffff;
    printf("%p\n", ptr);
    ptr++;
    printf("%p\n", ptr);
    return 0;
}

Nyní je již vše korektní, ovšem za cenu delšího a pomalejšího programu:

Překladače C pro systém DOS.

Obrázek 7: Zvýšení „huge“ ukazatele o jedničku.

9. Funkce vracející větší z hodnot předaných přes ukazatel

Vliv různých paměťových modelů je nejvíce patrný u funkcí, které jako svůj argument (nebo argumenty) akceptují ukazatel a popřípadě taktéž ukazatel vrací. Proto si pro prozkoumání vlastností jednotlivých modelů naprogramujeme funkci pro nalezení větší hodnoty z dvojice, přičemž tato dvojice hodnot je předána nepřímo – přes ukazatele. A i návratovou hodnotou nebude přímo větší prvek, ale pouze ukazatel na něj. V jazyku C by implementace takové funkce mohla vypadat následovně:

#include <stdio.h>
 
typedef unsigned int uint;
 
uint* larger_value(uint *x, uint *y) {
    if (*x > *y) {
        return x;
    } else {
        return y;
    }
}

Otestujeme základní funkcionalitu:

int main(void) {
    uint a = 1;
    uint b = 2;
 
    printf("%d\n", *larger_value(&a, &b));
    printf("%d\n", *larger_value(&b, &a));
 
    return 0;
}
Poznámka: v praxi by bylo vhodné připsat podmínku pro test NULLových hodnot ukazatelů. Nyní by nám to ovšem komplikovalo výslednou sekvenci instrukcí.
Překladače C pro systém DOS.

Obrázek 8: Výběr paměťového modelu pro celý projekt (Borland C++ 3.0).

10. Překlad pro paměťové modely s krátkými ukazateli pro data

Nyní se pokusme výše uvedenou funkci larger_value přeložit v paměťových modelech s krátkými ukazateli na data. Jedná se o paměťové modely Tiny, Small a Medium.

Začneme s nejjednodušším modelem Tiny. Oba ukazatele se předávají jako dvoubajtové hodnoty (přes zásobník) a návratová hodnota funkce má taktéž dva bajty. Interně se pro adresování používá segmentový registr DS, který není zapotřebí explicitně udávat. Výsledek (ukazatel) se vrací v registru AX:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:_TEXT
_larger_value   proc    near
        push    bp
        mov     bp,sp
        push    si
        push    di
        mov     si,word ptr [bp+4]
        mov     di,word ptr [bp+6]
   ;    
   ;        if (*x > *y) {
   ;    
        mov     ax,word ptr [si]
        cmp     ax,word ptr [di]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     ax,si
        pop     di
        pop     si
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     ax,di
   ;    
   ;        }
   ;    }
   ;    
        pop     di
        pop     si
        pop     bp
        ret
_larger_value   endp

V paměťovém modelu Small se vygeneruje naprosto totožný kód. Sice platí, že CS!=DS, to ovšem nijak nezmění způsob překladu funkce, protože CS se použije jen instrukcí RET a DS naopak pro všechny přístupy do paměti:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:_TEXT
_larger_value   proc    near
        push    bp
        mov     bp,sp
        push    si
        push    di
        mov     si,word ptr [bp+4]
        mov     di,word ptr [bp+6]
   ;    
   ;        if (*x > *y) {
   ;    
        mov     ax,word ptr [si]
        cmp     ax,word ptr [di]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     ax,si
        pop     di
        pop     si
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     ax,di
   ;    
   ;        }
   ;    }
   ;    
        pop     di
        pop     si
        pop     bp
        ret
_larger_value   endp

Paměťový model Medium vyžaduje, aby adresa subrutiny byla uložena ve dvojici segment:offset. To znamená, že se na zásobníku posunou adresy obou argumentů (což je patrné, nyní se používá BP+6 a BP+8 a nikoli BP+4 a BP+6), ovšem to je jediná změna. Zbytek kódu zůstane stejný, i když jeho volání bude nepatrně pomalejší:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:LARGER_TEXT
_larger_value   proc    far
        push    bp
        mov     bp,sp
        push    si
        push    di
        mov     si,word ptr [bp+6]
        mov     di,word ptr [bp+8]
   ;    
   ;        if (*x > *y) {
   ;    
        mov     ax,word ptr [si]
        cmp     ax,word ptr [di]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     ax,si
        pop     di
        pop     si
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     ax,di
   ;    
   ;        }
   ;    }
   ;    
        pop     di
        pop     si
        pop     bp
        ret
_larger_value   endp

Všechny čtyři varianty si můžeme porovnat instrukci po instrukci. Kód v prvních dvou sloupcích je totožný:

; TINY                                ; SMALL                              ; MEDIUM
_larger_value   proc    near          _larger_value   proc    near         _larger_value   proc    far
        push    bp                            push    bp                           push    bp
        mov     bp,sp                         mov     bp,sp                        mov     bp,sp
        push    si                            push    si                           push    si
        push    di                            push    di                           push    di
        mov     si,word ptr [bp+4]            mov     si,word ptr [bp+4]           mov     si,word ptr [bp+6]
        mov     di,word ptr [bp+6]            mov     di,word ptr [bp+6]           mov     di,word ptr [bp+8]
        mov     ax,word ptr [si]              mov     ax,word ptr [si]             mov     ax,word ptr [si]
        cmp     ax,word ptr [di]              cmp     ax,word ptr [di]             cmp     ax,word ptr [di]
        jbe     short @1@86                   jbe     short @1@86                  jbe     short @1@86
        mov     ax,si                         mov     ax,si                        mov     ax,si
        pop     di                            pop     di                           pop     di
        pop     si                            pop     si                           pop     si
        pop     bp                            pop     bp                           pop     bp
        ret                                   ret                                  ret
@1@86:                                @1@86:                               @1@86:
        mov     ax,di                         mov     ax,di                        mov     ax,di
        pop     di                            pop     di                           pop     di
        pop     si                            pop     si                           pop     si
        pop     bp                            pop     bp                           pop     bp
        ret                                   ret                                  ret
_larger_value   endp                  _larger_value   endp                 _larger_value   endp

11. Překlad pro paměťové modely s dlouhými ukazateli pro data

Poněkud odlišným způsobem dopadne překlad stejné funkce pro paměťové modely, ve kterých se data mohou nacházet v libovolném segmentu (tedy v rozsahu jednoho megabajtu). Jedná se o modely Compact, Large a Huge.

Nejdříve se podívejme na překlad pro model Compact, ve kterém je velikost programového kódu omezena na 64kB. Povšimněte si, že nyní jsou ukazatele předány každý ve čtyřech bajtech. Segment ukazatelů se přenese do registru ES instrukcí LES a následně se porovnávané hodnoty přečtou přes „plnou“ adresu ES:BX. Výsledná adresa (4 bajty) je vrácena ve dvojici registrů DX a AX:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:_TEXT
_larger_value   proc    near
        push    bp
        mov     bp,sp
   ;    
   ;        if (*x > *y) {
   ;    
        les     bx,dword ptr [bp+4]
        mov     ax,word ptr es:[bx]
        les     bx,dword ptr [bp+8]
        cmp     ax,word ptr es:[bx]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     dx,word ptr [bp+6]
        mov     ax,word ptr [bp+4]
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     dx,word ptr [bp+10]
        mov     ax,word ptr [bp+8]
   ;    
   ;        }
   ;    }
   ;    
        pop     bp
        ret
_larger_value   endp

V paměťovém modelu Large je provedena jedna změna – návratová adresa subrutiny je čtyřbajtová (segment:offset), takže jsou posunuty relativní adresy argumentů. Ostatní kód ovšem zůstává beze změny:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:LARGER_TEXT
_larger_value   proc    far
        push    bp
        mov     bp,sp
   ;    
   ;        if (*x > *y) {
   ;    
        les     bx,dword ptr [bp+6]
        mov     ax,word ptr es:[bx]
        les     bx,dword ptr [bp+10]
        cmp     ax,word ptr es:[bx]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     dx,word ptr [bp+8]
        mov     ax,word ptr [bp+6]
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     dx,word ptr [bp+12]
        mov     ax,word ptr [bp+10]
   ;    
   ;        }
   ;    }
   ;    
        pop     bp
        ret
_larger_value   endp

V paměťovém modelu Huge se navíc na zásobníkový rámec ukládá i obsah registru DS, protože ten se může uvnitř subrutiny změnit (a taktéž mění). Opět platí, že zbytek kódu zůstává prakticky beze změny:

   ;    
   ;    uint* larger_value(uint *x, uint *y) {
   ;    
        assume  cs:LARGER_TEXT
_larger_value   proc    far
        push    bp
        mov     bp,sp
        push    ds
        mov     ax,LARGER_DATA
        mov     ds,ax
   ;    
   ;        if (*x > *y) {
   ;    
        les     bx,dword ptr [bp+6]
        mov     ax,word ptr es:[bx]
        les     bx,dword ptr [bp+10]
        cmp     ax,word ptr es:[bx]
        jbe     short @1@86
   ;    
   ;            return x;
   ;    
        mov     dx,word ptr [bp+8]
        mov     ax,word ptr [bp+6]
        pop     ds
        pop     bp
        ret
@1@86:
   ;    
   ;        } else {
   ;            return y;
   ;    
        mov     dx,word ptr [bp+12]
        mov     ax,word ptr [bp+10]
   ;    
   ;        }
   ;    }
   ;    
        pop     ds
        pop     bp
        ret
_larger_value   endp

Opět si nyní porovnejme výsledky získané překladem pro různé paměťové modely. Základ zůstává stále stejný – přístup k hodnotám přes dlouhé ukazatele segment:offset, ovšem odlišuje se práce s návratovou adresou ze subrutiny a v případě paměťového modelu Huge i úschova a obnova registru DS:

; COMPACT                              ; LARGE                                 ; HUGE
_larger_value   proc    near           _larger_value   proc    far             _larger_value   proc    far
        push    bp                             push    bp                              push    bp
        mov     bp,sp                          mov     bp,sp                           mov     bp,sp
                                                                                       push    ds
                                                                                       mov     ax,LARGER_DATA
                                                                                       mov     ds,ax
        les     bx,dword ptr [bp+4]            les     bx,dword ptr [bp+6]             les     bx,dword ptr [bp+6]
        mov     ax,word ptr es:[bx]            mov     ax,word ptr es:[bx]             mov     ax,word ptr es:[bx]
        les     bx,dword ptr [bp+8]            les     bx,dword ptr [bp+10]            les     bx,dword ptr [bp+10]
        cmp     ax,word ptr es:[bx]            cmp     ax,word ptr es:[bx]             cmp     ax,word ptr es:[bx]
        jbe     short @1@86                    jbe     short @1@86                     jbe     short @1@86
        mov     dx,word ptr [bp+6]             mov     dx,word ptr [bp+8]              mov     dx,word ptr [bp+8]
        mov     ax,word ptr [bp+4]             mov     ax,word ptr [bp+6]              mov     ax,word ptr [bp+6]
                                                                                       pop     ds
        pop     bp                             pop     bp                              pop     bp
        ret                                    ret                                     ret
@1@86:                                 @1@86:                                  @1@86:
        mov     dx,word ptr [bp+10]            mov     dx,word ptr [bp+12]             mov     dx,word ptr [bp+12]
        mov     ax,word ptr [bp+8]             mov     ax,word ptr [bp+10]             mov     ax,word ptr [bp+10]
                                                                                       pop     ds
        pop     bp                             pop     bp                              pop     bp
        ret                                    ret                                     ret
_larger_value   endp                   _larger_value   endp                    _larger_value   endp

12. Alokace a přístup k prvkům pole přesahujícího velikost jednoho segmentu

Z předchozího textu vyplývá, že je obecně poměrně problematické vytvářet pole (nebo další paměťové struktury), jejichž velikost přesahuje hranici jednoho segmentu. Překladač Borland C/C++ ovšem dokáže provádět alokaci větších struktur, a to ve všech paměťových modelech kromě modelu Tiny. Pro tento účel slouží funkce nazvaná farmalloc, která se podobá funkci malloc, ovšem s tím rozdílem, že akceptuje velikost zapsanou hodnotou typu unsigned long a nikoli pouze unsigned int (povšimněte si, že v ANSI/ISO C je to již odlišné – tam máme ke stejnému účelu předepsán specializovaný typ size_t – jakékoli přenosy takových programů na jiné systémy jsou tedy minimálně problematické). V každém případě vrací funkce farmalloc ukazatel typu void far *, tedy „dlouhý“ ukazatel typu segment:offset. A právě ten využijeme v dalším demonstračním příkladu.

13. Alokace rozsáhlého pole s jeho inicializací

Alokace pole, jehož velikost přesahuje hranici jednoho segmentu, může vypadat následovně. Zavoláme funkci farmalloc a následně pole v programové smyčce vyplníme nulami:

#include <stdio.h>
 
int main(void) {
#define LENGTH 100000
    long i;
    unsigned char far *array = (unsigned char far*)farmalloc(LENGTH);
    printf("%p\n", array);
    for (i=0; i<LENGTH; i++) {
        array[i] = 0;
    }
    return 0;
}
POZOR: zejména u funkcí malloc a farmalloc je více než vhodné zkontrolovat, jestli se nevrátila hodnota NULL. V DOSu totiž není nikdy dostatek operační paměti. O to více to platí v případě, že se program spouští přímo z integrovaného vývojového prostředí.

14. Výsledek překladu

Opět se podívejme na způsob překladu výše uvedeného demonstračního příkladu do strojového kódu. V první části je zavolána funkce nazvaná farmalloc pro alokaci paměti a získání „dlouhého“ ukazatele na nový paměťový blok. V registrech AX:DX je funkci farmalloc předána vyžadovaná kapacita (65536+34464=100000):

_TEXT   segment byte public 'CODE'
   ;    
   ;    int main(void) {
   ;    
        assume  cs:_TEXT
_main   proc    near
        push    bp
        mov     bp,sp
        sub     sp,8
   ;    
   ;    #define LENGTH 100000
   ;        long i;
   ;        unsigned char *array = (unsigned char*)farmalloc(LENGTH);
   ;    
        mov     ax,1
        mov     dx,34464
        push    ax
        push    dx
        call    near ptr _farmalloc
        pop     cx
        pop     cx

Dále se získaný ukazatel uloží na zásobník do místa pro lokální proměnnou.

        cwd
        mov     word ptr [bp-6],dx
        mov     word ptr [bp-8],ax

Následuje inicializace pole, což je programová smyčka, která vlastně vůbec není optimalizována a při každé iteraci se hned několikrát musí přistoupit do operační paměti. Povšimněte si způsobu zvýšení ukazatele přes dvojici instrukcí ADD+ADC (nižší slovo, vyšší slovo):

   ;    
   ;        for (i=0; i<LENGTH; i++) {
   ;    
        mov     word ptr [bp-2],0
        mov     word ptr [bp-4],0
        jmp     short @1@114
@1@58:
   ;    
   ;            array[i] = 0;
   ;    
        les     bx,dword ptr [bp-8]
        add     bx,word ptr [bp-4]
        mov     byte ptr es:[bx],0
        add     word ptr [bp-4],1
        adc     word ptr [bp-2],0
@1@114:
        cmp     word ptr [bp-2],1
        jl      short @1@58
        jne     short @1@198
        cmp     word ptr [bp-4],34464
        jb      short @1@58
@1@198:
   ;    
   ;        }
   ;        return 0;
   ;    
        xor     ax,ax
        jmp     short @1@226
@1@226:
   ;    
   ;    }
   ;    
        mov     sp,bp
        pop     bp
        ret

15. Pomocná makra pro práci s ukazateli se segmenty a offsety

Překladače Borland C/C++ mj. obsahují i hlavičkový soubor dos.h, ve kterém kromě funkcí, které jsou specifické pro operační systém DOS, nalezneme i trojici maker určených pro zpracování „dlouhých“ ukazatelů. Jedno z maker lze použít pro konstrukci takového ukazatele z předaného segmentu a offsetu, další dvě makra naopak slouží pro získání segmentové či offsetové části ukazatele:

# Makro Stručný popis makra
1 MK_FP sestaví ukazatel z předaného segmentu a offsetu
2 FP_SEG získá z ukazatele pouze segmentovou část
3 FP_OFF získá z ukazatele pouze offsetovou část

Zajímavé je, že jsou tato makra interně založena na konverzi ukazatelů s využitím modifikátorů _seg a near. Jejich definice totiž vypadají následovně:

#define MK_FP( seg,ofs )( (void _seg * )( seg ) +( void near * )( ofs ))
#define FP_SEG( fp )( (unsigned )( void _seg * )( void far * )( fp ))
#define FP_OFF( fp )( (unsigned )( fp ))

16. Demonstrační příklad: nastavení grafického režimu 13h a vyplnění obrazovky barevným vzorkem

V dnešním posledním demonstračním příkladu spojíme všechny znalosti získané v předchozích kapitolách. Vytvoříme jednoduché (nutno říci, že velmi triviální) demo, které nejdříve provede přepnutí do grafického režimu standardní grafické karty VGA s rozlišením 320×200 pixelů a s 256 barvami (tento režim ostatně velmi dobře známe). Následně vyplníme obrazovku barevným vzorkem, přičemž pro jednoduchost nebudeme modifikovat barvovou paletu. Demo by mělo vytvořit tento obrázek:

Překladače C pro systém DOS.

Obrázek 9: Takto by měl vypadat výsledek po spuštění dema v DOSu nebo jeho emulátoru.

Obrazová paměť v grafických režimech karty VGA začíná na adrese A000:0000, takže si připravíme „dlouhý“ ukazatel s touto adresou. Použijeme přitom makro MK_FP:

unsigned char far *ptr = (unsigned char*)MK_FP(0xa000, 0000);
printf("%p\n", ptr);

Přepnutí do grafického režimu bude ve skutečnosti triviální. Postačuje nám totiž zavolat službu BIOSu číslo 10h, v registru AH předat číslo služby (což je v tomto případě služba číslo 0) a v registru AL předat číslo grafického režimu. Překladače Borland C/C++ podporují vložení kódu v assembleru, takže se přepnutí režimu realizuje následovně:

asm {
    mov ah, 00h
    mov al, 13h
    int 10h
}

Poslední částí dema je vyplnění obrazové paměti, tj. 320×200=64000 pixelů, barvovým vzorkem:

for (i=0; i<(unsigned)(320*200); i++) {
    *ptr++ = i;
}
Poznámka: pozor si musíme dát na to, že typ int je jen šestnáctibitový, takže musíme použít typ unsigned int.

Celý zdrojový kód dema bude vypadat následovně:

#include <dos.h>
#include <stdio.h>
 
int main(void) {
    unsigned char far *ptr = (unsigned char*)MK_FP(0xa000, 0000);
    unsigned int i;
    printf("%p\n", ptr);
    getch();
 
    asm {
        mov ah, 00h
        mov al, 13h
        int 10h
    }
 
    for (i=0; i<(unsigned)(320*200); i++) {
        *ptr++ = i;
    }
    getch();
    return 0;
}

17. Výsledek překladu do strojového kódu

Zajímavé bude zjistit, jak vlastně dopadl překlad dema do strojového kódu. Na začátku se nastavují lokální proměnné, zejména pak ukazatel uložený na adresách [BP-2] a [BP-4]:

   ;    
   ;    int main(void) {
   ;    
        assume  cs:MODE13H_TEXT
_main   proc    far
        push    bp
        mov     bp,sp
        sub     sp,6
   ;    
   ;        unsigned char far *ptr = (unsigned char*)MK_FP(0xa000, 0000);
   ;    
        mov     word ptr [bp-2],40960
        mov     word ptr [bp-4],0

Následuje výpis ukazatele (nezajímavé) i volání funkce getch() čekající na stisk klávesy:

   ;    
   ;        unsigned int i;
   ;        printf("%p\n", ptr);
   ;    
        push    word ptr [bp-2]
        push    word ptr [bp-4]
        push    ds
        mov     ax,offset DGROUP:s@
        push    ax
        call    far ptr _printf
        add     sp,8
   ;    
   ;        getch();
   ;    
        call    far ptr _getch

Část zapsaná v assembleru se přeložila přesně tak, jak jsme ji zapsali (žádné optimalizace se zde ani neočekávaly):

        mov      ah, 00h
        mov      al, 13h
        int      10h

18. Překlad programové smyčky vyplňující obrazovou paměť

A konečně se podívejme na způsob překladu programové smyčky, která vyplní celou obrazovou paměť barvovým vzorkem. V jazyku C byla tato smyčka napsána triviálním způsobem, takže by se mohlo zdát, že její překlad bude proveden optimálně. Ovšem i při zapnutí všech dostupných optimalizací bude výsledek velmi pomalý a neoptimální:

   ;        for (i=0; i<(unsigned)(320*200); i++) {
   ;    
        mov     word ptr [bp-6],0
@1@170:
   ;    
   ;            *ptr++ = i;
   ;    
        les     bx,dword ptr [bp-4]
        mov     al,byte ptr [bp-6]
        mov     byte ptr es:[bx],al
        inc     word ptr [bp-4]
        inc     word ptr [bp-6]
        cmp     word ptr [bp-6],64000
        jb      short @1@170
   ;        }
Poznámka: povšimněte si, že se vlastně vůbec nevyužívají dostupné pracovní registry, takže tato část programu je evidentně připravena na ruční optimalizace provedené přímo v assembleru (stále mluvíme o překladači Borland C/C++ verze 3.0).

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

Demonstrační příklady napsané v jazyce C, které jsou primárně určené pro překlad s využitím překladačů Turbo C a (Open)Watcom C), byly uloženy 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ář:

Borland C:

# Příklad Stručný popis Adresa
1 add.c funkce pro součet dvou celých čísel naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add.c
2 add1.asm překlad přes assembler, externí symboly začínají pomlčkou https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add1.asm
3 add2.asm překlad přes assembler, externí symboly nejsou přejmenovány https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add2.asm
4 add3.asm výsledek překladu se zákazem optimalizací (externí symboly s pomlčkou) https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add3.asm
5 add4.asm výsledek překladu se zákazem optimalizací (externí symboly nejsou přejmenovány) https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add4.asm
6 add5.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add5.asm
7 memset.c funkce pro vyplnění paměťového bloku naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/memset.c
8 memset1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/memset1.asm
9 memset2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/memset2.asm
10 find_max.c funkce pro nalezení největšího prvku v poli naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/find_max.c
11 find_max1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/find_max1.asm
12 find_max2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/find_max2.asm
13 find_max3.asm výsledek překladu s povolením instrukcí procesorů 80286 https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/find_max3.asm
       
14 fill_a.c funkce pro vyplnění pole konstantou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/fill_a.c
15 fill_a1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/fill_a1.asm
16 fill_a2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/fill_a2.asm
       
17 add_f.c funkce pro součet dvou čísel s plovoucí řádovou čárkou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/add_f.c
18 add_f1.asm překlad přes assembler https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/add_f1.asm
19 add_f2.asm překlad přes assembler s eliminací skoků a dalšími optimalizacemi https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/add_f2.asm
       
20 pointersn.c zvýšení hodnoty krátkého ukazatele https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/pointersn.c
21 pointers_f.c zvýšení hodnoty dlouhého ukazatele https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/pointers_f.c
22 pointers_h.c zvýšení hodnoty obrovského ukazatele https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/pointers_h.c
23 larger.c nalezení většího z dvojice prvků přes ukazatel: funkce naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/larger.c
24 larger_t.asm překlad s využitím paměťového režimu tiny https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_t.asm
25 larger_s.asm překlad s využitím paměťového režimu small https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_s.asm
26 larger_m.asm překlad s využitím paměťového režimu medium https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_m.asm
27 larger_c.asm překlad s využitím paměťového režimu compact https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_c.asm
28 larger_l.asm překlad s využitím paměťového režimu large https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_l.asm
29 larger_h.asm překlad s využitím paměťového režimu huge https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/larger_h.asm
30 array.c alokace pole většího než 64kB: funkce naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/bc/array.c
31 array_t.asm překlad s využitím paměťového režimu tiny https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/array_t.asm
32 arrac_c.asm překlad s využitím paměťového režimu compact https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/arrac_c.asm
33 array_l.asm překlad s využitím paměťového režimu large https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/array_l.asm
34 array_h.asm překlad s využitím paměťového režimu huge https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/array_h.asm
35 mode13h.c základ práce s video pamětí: funkce naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/mode13h.c
36 mode13h.asm výsledek překladu do strojového kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/bc/mode13h.asm

(Open)Watcom pro platformu IBM PC:

# Příklad Stručný popis Adresa
1 add.c funkce pro součet dvou celých čísel naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add.c
2 add1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add1.asm
3 add2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add2.asm
4 memset.c funkce pro vyplnění paměťového bloku naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/memset.c
5 memset1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/memset1.asm
6 memset2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/memset2.asm
7 memset3.asm výsledek překladu do 32bitového kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/memset3.asm
8 find_max.c funkce pro nalezení největšího prvku v poli naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/find_max.c
9 find_max1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/find_max1.asm
10 find_max2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/find_max2.asm
11 find_max3.asm výsledek překladu do 32bitového kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/find_max3.asm
       
12 fill_a.c funkce pro vyplnění pole konstantou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/fill_a.c
13 fill_a1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/fill_a1.asm
14 fill_a2.asm výsledek překladu s povolením některých optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/fill_a2.asm
15 fill_a3.asm výsledek překladu s povolením všech optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/fill_a3.asm
16 fill_a4.asm výsledek překladu do 32bitového kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/fill_a4.asm
       
17 add_f.c funkce pro součet dvou čísel s plovoucí řádovou čárkou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add_f.c
18 add_f1.asm překlad přes assembler https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add_f1.asm
19 add_f2.asm překlad přes assembler s optimalizacemi https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add_f2.asm
20 add_f3.asm překlad přes assembler s využitím FP knihovny https://github.com/tisnik/8bit-fame/blob/master/compiler­s/watcom/add_f3.asm

GCC pro platformu x86–64:

# Příklad Stručný popis Adresa
1 add.c funkce pro součet dvou celých čísel naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compilers/gcc/add.c
2 add.asm výsledek překladu do assembleru s použitím optimalizací https://github.com/tisnik/8bit-fame/blob/master/compilers/gcc/add.asm
3 memset.c funkce pro vyplnění paměťového bloku naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/memset.c
4 memset1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/memset1.asm
5 memset2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/memset2.asm
6 find_max.c funkce pro nalezení největšího prvku v poli naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/find_max.c
7 find_max_default.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/find_max_default.asm
8 find_max_smallest.asm výsledek překladu s optimalizací na velikost kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/find_max_smallest.asm
9 find_max_vector.asm výsledek překladu s optimalizací na rychlost https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/find_max_vector.asm
       
10 fill_a.c funkce pro vyplnění pole konstantou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/fill_a.c
11 fill_a_default.s výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/fill_a_default.s
12 fill_a_O2.s výsledek překladu s přepínačem -O2 https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/fill_a_O2.s
13 fill_a_O9.s výsledek překladu s přepínačem -O9 https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/fill_a_O9.s
14 fill_a_smallest.s výsledek překladu při snaze o vytvoření nejkratšího kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/gcc/fill_a_smallest.s

DGJPP pro platformu IBM PC + DOS:

# Příklad Stručný popis Adresa
1 add.c funkce pro součet dvou celých čísel naprogramovaná v C https://github.com/tisnik/8bit-fame/blob/master/compilers/djgpp/add.c
2 add1.asm výsledek překladu do assembleru se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add1.asm
3 add2.asm výsledek překladu do assembleru s povolením optimalizací na rychlost https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add2.asm
4 add3.asm výsledek překladu do assembleru s povolením optimalizací na velikost kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add3.asm
5 add4.asm explicitní specifikace, že se nemusí vytvářet standardní zásobníkové rámce https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add4.asm
       
6 memset.c funkce pro vyplnění paměťového bloku naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/memset.c
7 memset1.asm výsledek překladu do assembleru se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/memset1.asm
8 memset2.asm výsledek překladu do assembleru s povolením optimalizací na rychlost https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/memset2.asm
9 memset3.asm výsledek překladu do assembleru s povolením optimalizací na velikost kódu https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/memset3.asm
       
10 find_max.c funkce pro nalezení největšího prvku v poli naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/find_max.c
11 findmax1.asm překlad bez povolení optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/findmax1.asm
12 findmax2.asm překlad s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/findmax2.asm
13 findmax3.asm překlad s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/findmax3.asm
       
14 fill_a.c funkce pro vyplnění pole konstantou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/fill_a.c
15 fill_a1.asm překlad bez povolení optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/fill_a1.asm
16 fill_a2.asm překlad s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/fill_a2.asm
17 fill_a3.asm optimalizace na velikost https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/fill_a3.asm
18 fill_a4.asm nejkratší možný kód https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/fill_a4.asm
       
19 add_f.c funkce pro součet dvou čísel s plovoucí řádovou čárkou naprogramovaná v jazyku C https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add_f.c
20 add_f1.asm výsledek překladu se zákazem optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add_f1.asm
21 add_f2.asm výsledek překladu s povolením optimalizací https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add_f2.asm
22 add_f3.asm výsledek překladu s povolením volání FPU knihovny https://github.com/tisnik/8bit-fame/blob/master/compiler­s/djgpp/add_f3.asm

20. Odkazy na Internetu

  1. DJGPP (Wikipedia)
    https://cs.wikipedia.org/wiki/DJGPP
  2. DJGPP home page
    http://www.delorie.com/djgpp/
  3. DJGPP Zip File Picker
    http://www.delorie.com/djgpp/zip-picker.html
  4. The Intel 8088 Architecture and Instruction Set
    https://people.ece.ubc.ca/~ed­c/464/lectures/lec4.pdf
  5. x86 Opcode Structure and Instruction Overview
    https://pnx.tf/files/x86_op­code_structure_and_instruc­tion_overview.pdf
  6. x86 instruction listings (Wikipedia)
    https://en.wikipedia.org/wi­ki/X86_instruction_listin­gs
  7. x86 assembly language (Wikipedia)
    https://en.wikipedia.org/wi­ki/X86_assembly_language
  8. Intel Assembler (Cheat sheet)
    http://www.jegerlehner.ch/in­tel/IntelCodeTable.pdf
  9. 25 Microchips That Shook the World
    https://spectrum.ieee.org/tech-history/silicon-revolution/25-microchips-that-shook-the-world
  10. Chip Hall of Fame: MOS Technology 6502 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-mos-technology-6502-microprocessor
  11. Chip Hall of Fame: Intel 8088 Microprocessor
    https://spectrum.ieee.org/tech-history/silicon-revolution/chip-hall-of-fame-intel-8088-microprocessor
  12. Jak se zrodil procesor?
    https://www.root.cz/clanky/jak-se-zrodil-procesor/
  13. Apple II History Home
    http://apple2history.org/
  14. The 8086/8088 Primer
    https://www.stevemorse.or­g/8086/index.html
  15. flat assembler: Assembly language resources
    https://flatassembler.net/
  16. FASM na Wikipedii
    https://en.wikipedia.org/wiki/FASM
  17. Fresh IDE FASM inside
    https://fresh.flatassembler.net/
  18. MS-DOS Version 4.0 Programmer's Reference
    https://www.pcjs.org/docu­ments/books/mspl13/msdos/dos­ref40/
  19. DOS API (Wikipedia)
    https://en.wikipedia.org/wiki/DOS_API
  20. Bit banging
    https://en.wikipedia.org/wi­ki/Bit_banging
  21. IBM Basic assembly language and successors (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Basic_assembly_lan­guage_and_successors
  22. X86 Assembly/Bootloaders
    https://en.wikibooks.org/wi­ki/X86_Assembly/Bootloaders
  23. Počátky grafiky na PC: grafické karty CGA a Hercules
    https://www.root.cz/clanky/pocatky-grafiky-na-pc-graficke-karty-cga-a-hercules/
  24. 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/
  25. Karta EGA: první použitelná barevná grafika na PC
    https://www.root.cz/clanky/karta-ega-prvni-pouzitelna-barevna-grafika-na-pc/
  26. RGB Classic Games
    https://www.classicdosgames.com/
  27. Turbo Assembler (Wikipedia)
    https://en.wikipedia.org/wi­ki/Turbo_Assembler
  28. Microsoft Macro Assembler
    https://en.wikipedia.org/wi­ki/Microsoft_Macro_Assembler
  29. IBM Personal Computer (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Personal_Computer
  30. Intel 8251
    https://en.wikipedia.org/wi­ki/Intel_8251
  31. Intel 8253
    https://en.wikipedia.org/wi­ki/Intel_8253
  32. Intel 8255
    https://en.wikipedia.org/wi­ki/Intel_8255
  33. Intel 8257
    https://en.wikipedia.org/wi­ki/Intel_8257
  34. Intel 8259
    https://en.wikipedia.org/wi­ki/Intel_8259
  35. Support/peripheral/other chips – 6800 family
    http://www.cpu-world.com/Support/6800.html
  36. Motorola 6845
    http://en.wikipedia.org/wi­ki/Motorola_6845
  37. The 6845 Cathode Ray Tube Controller (CRTC)
    http://www.tinyvga.com/6845
  38. CRTC operation
    http://www.6502.org/users/an­dre/hwinfo/crtc/crtc.html
  39. The 6845 Cathode Ray Tube Controller (CRTC)
    http://www.tinyvga.com/6845
  40. Motorola 6845 and bitwise graphics
    https://retrocomputing.stac­kexchange.com/questions/10996/mo­torola-6845-and-bitwise-graphics
  41. IBM Monochrome Display Adapter
    http://en.wikipedia.org/wi­ki/Monochrome_Display_Adap­ter
  42. Color Graphics Adapter
    http://en.wikipedia.org/wi­ki/Color_Graphics_Adapter
  43. 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/
  44. The Modern Retrocomputer: An Arduino Driven 6845 CRT Controller
    https://hackaday.com/2017/05/14/the-modern-retrocomputer-an-arduino-driven-6845-crt-controller/
  45. flat assembler: Assembly language resources
    https://flatassembler.net/
  46. FASM na Wikipedii
    https://en.wikipedia.org/wiki/FASM
  47. Fresh IDE FASM inside
    https://fresh.flatassembler.net/
  48. MS-DOS Version 4.0 Programmer's Reference
    https://www.pcjs.org/docu­ments/books/mspl13/msdos/dos­ref40/
  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. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  56. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  57. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  58. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  59. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  60. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  61. Programming from the Ground Up Book – Summary
    http://savannah.nongnu.or­g/projects/pgubook/
  62. DOSBox
    https://www.dosbox.com/
  63. The C Programming Language
    https://en.wikipedia.org/wi­ki/The_C_Programming_Langu­age
  64. Hercules Graphics Card (HCG)
    https://en.wikipedia.org/wi­ki/Hercules_Graphics_Card
  65. Complete 8086 instruction set
    https://content.ctcd.edu/cou­rses/cosc2325/m22/docs/emu8086in­s.pdf
  66. Complete 8086 instruction set
    https://yassinebridi.github.io/asm-docs/8086_instruction_set.html
  67. 8088 MPH by Hornet + CRTC + DESiRE (final version)
    https://www.youtube.com/wat­ch?v=hNRO7lno_DM
  68. Area 5150 by CRTC & Hornet (Party Version) / IBM PC+CGA Demo, Hardware Capture
    https://www.youtube.com/wat­ch?v=fWDxdoRTZPc
  69. 80×86 Integer Instruction Set Timings (8088 – Pentium)
    http://aturing.umcs.maine­.edu/~meadow/courses/cos335/80×86-Integer-Instruction-Set-Clocks.pdf
  70. Colour Graphics Adapter: Notes
    https://www.seasip.info/Vin­tagePC/cga.html
  71. Restoring A Vintage CGA Card With Homebrew HASL
    https://hackaday.com/2024/06/12/res­toring-a-vintage-cga-card-with-homebrew-hasl/
  72. Demoing An 8088
    https://hackaday.com/2015/04/10/de­moing-an-8088/
  73. Warnings Are Your Friend – A Code Quality Primer
    https://hackaday.com/2018/11/06/war­nings-are-your-friend-a-code-quality-primer/
  74. Defending Against Compiler-Based Backdoors
    https://blog.regehr.org/archives/1241
  75. Reflections on Trusting Trust
    https://www.win.tue.nl/~a­eb/linux/hh/thompson/trus­t.html
  76. Coding Machines (povídka)
    https://www.teamten.com/law­rence/writings/coding-machines/
  77. Stage0
    https://bootstrapping.mira­heze.org/wiki/Stage0
  78. Projekt stage0 na GitHubu
    https://github.com/oriansj/stage0
  79. Bootstraping wiki
    https://bootstrapping.mira­heze.org/wiki/Main_Page
  80. Bootstrapped 6502 Assembler
    https://github.com/robinluc­key/bootstrap-6502
  81. IBM Basic assembly language and successors (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Basic_assembly_lan­guage_and_successors
  82. X86 Assembly/Bootloaders
    https://en.wikibooks.org/wi­ki/X86_Assembly/Bootloaders
  83. What is a coder's worst nightmare?
    https://www.quora.com/What-is-a-coders-worst-nightmare/answer/Mick-Stute
  84. Tiny C Compiler
    https://bellard.org/tcc/
  85. Welcome to C–
    https://www.cs.tufts.edu/~nr/c--/index.html
  86. c4 – C in four functions
    https://github.com/rswier/c4
  87. Tiobe index
    https://www.tiobe.com/tiobe-index/
  88. Lattice C (Wikipedia)
    https://en.wikipedia.org/wi­ki/Lattice_C
  89. Aztec C (Wikipedia)
    https://en.wikipedia.org/wiki/Aztec_C
  90. Digital Mars (Wikipedia)
    https://en.wikipedia.org/wi­ki/Digital_Mars
  91. Stránky projektu Open Watcom
    https://openwatcom.org/
  92. Repositář Open Watcom
    https://github.com/open-watcom/open-watcom-v2
  93. Watcom C/C++ (Wikipedia)
    https://en.wikipedia.org/wi­ki/Watcom_C/C%2B%2B
  94. Turbo C (Wikipedia)
    https://en.wikipedia.org/wiki/Turbo_C
  95. Borland C++ (Wikipedia)
    https://en.wikipedia.org/wi­ki/Borland_C%2B%2B
Neutrální ikona do widgetu na odběr článků ze seriálů

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.