Názory k článku Problematika ukazatelů v překladačích jazyka C pro systém DOS

  • Článek je starý, nové názory již nelze přidávat.
  • 3. 6. 2025 10:20

    atarist

    No já si s pointery a paměťovými modely strašně dlouho nelámal hlavu, protože takové ty typicky školní projekty v pohodě jely v 64kB limitu (na VŠ, FIT). Potom ale člověk narazil v praxi a to velmi rychle. Třeba udělat si backbuffer VGAčka 640x480, to je cirka 150kB, tedy tři segmenty. A TL;DR; tady už BC narážel i v huge režimu, ne vždycky totiž platilo, že si upravuje i segmentovou část ukazatele.

    Jediné řešení - jít to Watcomu nebo DJGPP (nebo už rovnou na Windows :-), protože zdaleka ne všechny předměty povolovaly Linux.

  • 3. 6. 2025 16:21

    CHe

    Aký bol vlastne dôvod zavedenia segmentácie s tým bizarným prekryvom? Ak by boli segmenty bez prekryvu (tzn. výpočet efektívnej adresy DS << 16 | DX) a existovali by inštrukcie pre aritmetiku nad pármi seg:offs registrov (ideálne vrátane blokových), kompatibilita s 8080 kódom by zostala zachovaná v podobnej miere, aj tiny/small modely by fungovali rovnako úsporne, no zároveň by sa dal výrazne príčetnejšie písať kód operujúci mimo hraníc 16b adr. priestoru. V princípe by to bolo analogické riešenie k 8-bitovým CPU so 16b adresovaním pármi 8b registrov.

    3. 6. 2025, 16:25 editováno autorem komentáře

  • 3. 6. 2025 17:07

    atarist

    IMHO Intel o tom moc nepřemýšlel a rozšířil možnosti CPU 8085 (to je taková lepší 8080). Kdyby to bylo, jak píšeš, bylo by to super, protože by od začátku mohla být paměť s 32bitovou adresou - něco co neuměla ani 68000.

    Jedinej důvod, který mě napadá, je ten, že takto se nemusí použít celých 64kB například na zásobník, ale může to být fine-tunovatelné po 16 bajtech. Jinak je to celý špatně :-)

  • 3. 6. 2025 18:58

    kvr kvr

    Ten pár registrů z 8-bitů moc efektivní není, bo musí přepočítat oba registry. U těch x86 segmentů se naopak dá paměť rozdělit na dostatečně malé kousky, aby jakákoliv struktura začínala na offset 0 a posun v poli mění jenom jeden registr.

    Ono to v zásadě nějak stačilo docela dlouho. Peklo pak bylo adresování ve VGA, ale VGA přišla až po 32-bitové i386 . To, že byl 16-bitový DOS majoritní systém ještě 10 let po uvedené i386, je jiná pohádka. Tím nechcu říct, že to byl dobrý design...

    Ohledně 68000 - ta právě uměla 32-bitové adresy od začátku, ne? (pominu-li fyzické limity)

  • 3. 6. 2025 19:06

    atarist

    no myslel jsem to tak, ze externe + adresove registry to bylo omezeno na 24 bitu. Potom s tim taky malicko bojovali, aby dali plnych 32 bitu adresovych registru (ale min, nez na 86, tam to bylo jine peklicko)

    s tim VGA/386 to je pravda, proste se tam drzel DOS mnohem dyl, nez bylo zdravo

  • 3. 6. 2025 18:49

    forth

    Pohybovat segment o 16 bajtu ti umozni mit zarovnane 64k pole s ofsetem od nuly. A prijdes max o 15 bajtu na vypln.

    Tvuj navrh vyzaduje komplikovane vypocty pro kazdy pristup do pole. Nebo umet nejakou instrukci [SEG:OFS]+n*INDEX.

    Tvuj navrh ma problem s tim ze kdyz nebude pole zarovnane, tak klidne muze zacinat s OFS 64K-100. Takze po 100 bajtech musis zvednout SEG. Nemuzes pouzivat OFS snadno jako INDEX. Jedine ze to zarovnas, ale to mas jen 16 moznosti pro megabajt.

    Museli by obetovat neco pro budoucnost, v dobe kdy tam maji 20 pinu. Takze bys kupoval v dobe co procesor byl drazsi nez clovek jen omezenejsi verzi co meli.

    Proste to lepili jako snehovou kouli, nebo jako kdyz privaris k autu autokaravan, a pak dalsi jeste vetsi a dalsi. Pokud by tam nebyl ten predchozi a rovnou presli na vetsi tak by to mohlo vypadat jinak...

    Nejakej smysl to davat muselo. Hlupaci to nebyli. .)

    3. 6. 2025, 18:53 editováno autorem komentáře

  • 3. 6. 2025 20:42

    P_V

    Paměť byla v těch dobách drahá, takže i 64k bylo docela dost. Nejspíš ta segmentace byla určena pro počítače s něčím jako 128 nebo 256 kB paměti, kde byl 64 kB segment tak akorát a s většími datovými strukturami se ani moc nepočítalo. 1 MB paměti bylo scifi...

  • 3. 6. 2025 20:43

    CHe

    Jj, pravda, umožňovalo to mať de facto kdekoľvek v 20b adresovom priestore 64k štruktúru, lacno adresovateľnú jedným 16b registrom. To je v situácii s jedinou 16b sčítačkou vcelku hodnotný benefit (ale za cenu skomplikovania rozšíriteľnosti do budúcna). Pre porovnateľne rýchle adresovanie v tom druhom prípade by to chcelo širšiu než 16b sčítačku.

    3. 6. 2025, 20:44 editováno autorem komentáře

  • 3. 6. 2025 21:06

    CHe

    (resp. takto je realizovateľné úplne bez sčítačky, len bitwise operáciami niečo, čo by v tom druhom prípade vyžadovalo buď sčítačku, navyše >16b pre random access, alebo špecificky písanú logiku s podmieneným kaskádovým inc/dec pri slučkách, prípade hw implementáciu rep inštrukcií, ktorá by to zahŕňala)

  • 4. 6. 2025 10:46

    ventYl

    Scitacky su v tom procesore (minimalne) dve. Jedna v generatore adries a druha v ALU. Scitacka sa da spravit tak, ze bude mat nizku latenciu kvoli carry, takze to az taky problem nie je. Procesor ma zaroven tiez iba jednu adresnu zbernicu nie nejakej velmi valnej rychlosti, takze viac scitaciek v generatore adries nepotrebuje.

    Ze generovanie adresy bez scitacky iba maskovanim nie je uplne super napad ukazuju jednotky MPU v mikrokontrolleroch, ktore casto taky pristup pouzivaju na definiciu regionov.

    Aby sa procesor vyhol nutnosti mat scitacky v jednotke MPU, definuje region podobne ako masku siete - bity bazovej adresy regionu, ktore spadaju do jeho tela musia byt nulove. To ale vynucuje, ze kazdy region musi byt zarovnany na adresu rovnu alebo vacsiu velkosti regionu, ktora zaroven musi byt mocninou 2.

    To setsakra komplikuje alokaciu pamate, pretoze staci, ze region narastie o par bytov z 254 na 257 bytov a kludne sa moze stat, ze sa cely region musi presunut na ine miesto.

  • 4. 6. 2025 10:31

    ventYl

    TL;DR: Uspora pamate a rozsahu registrov.

    V tej dobe (1980s) bolo ovela dolezitejsie setrit pamatou, nez podporovat velky adresny priestor. 16MB RAM v tej dobe mohlo stat gazilion penazi. Vacsina PC v tej dobe nebola osadena ani plnymi 640kB RAM s ktorymi ten model v pohode dokazal pracovat.

    Ak by segmentove registre pracovali s granularitou 64kB, vznikne jeden z dvoch problemov:
    - Bud budem plytvat vsetkou RAM medzi koncom poslednych dat v predoslom segmente a zaciatkom noveho segmentu
    - Alebo pridem o rozsah (max. 64kB) indexovacich registrov tym, ze data naalokujem na nenulovom offsete v ramci segmentu.
    - Alebo navyse este zabezpecim vacsiu nez nutnu fragmentaciu pamate, pretoze okrem toho, kde su obsadene miesta v pamati musim pri alokacii blokov zohladnit aj to, kde su hranice segmentov. 32kB volny blok na konci jedneho segmentu a 32kB volny blok na zaciatku hned dalsieho segmentu sa proste v takom systeme nedaju naalokovat ako jeden 64kB blok, lebo to nejde rozumne zaadresovat.

    Riesenie s prekryvajucimi sa segmentami poskytuje slusnu granularitu 16 bytov pri sucasnom maximalnom rozsahu indexovacich registrov 64kB.

    Pre 8086tku a dajme tomu aj 286tku to bolo zitelne riesenie. Na 386tke uz spravili v Inteli tu chybu, ze dovolili subezne pouzivanie segmentacie a strankovania.

  • 4. 6. 2025 14:24

    CHe

    Ad: Ak by segmentove registre pracovali s granularitou 64kB, vznikne jeden z dvoch problemov:

    preto som tam explicitne uvádzal tú druhú premisu "... a existovali by inštrukcie pre aritmetiku nad pármi seg:offs registrov (ideálne vrátane blokových [prenosov])"

    Čo by umožňovalo de facto lineárne adresovanie bez ohľadu na hranice segmentov. Všetky adresovacie režimy vyžadujúce sčítanie offsetov (MOV AX, [BX+n]; MOV AX, [SI+n]; MOV AX, [BX+SI]; MOV AX, [BX+SI+n], REP ...) by museli byť samozrejme realizované tak, aby si pri pretečení/podtečení offsetu vedeli korektne inkrementnúť/de­krementnúť aj segment.

  • 4. 6. 2025 14:40

    CHe

    Nie je komplikovaná, je to úplne priamočiara realizácia lineárneho 32b adresového priestoru v rámci 16b procesora. Presne rovnako, ako sa v 8-bitových CPU používal spravidla 16b lineárny adresový priestor (vyžadujúci pár 8b registrov, spoločne tvoriacich 16b adresový register).

  • 4. 6. 2025 14:55

    ventYl

    No, to znie pekne len do momentu, kym si clovek uvedomi, ze napr. taku dvojicu 8 bitovych registrov slo v instrukcnej sade 8080 pouzit ako jeden 16-bitovy register iba v doslova par instrukciach. A neslo pouzit hocijake dva registre, ale iba konkretne dva registre.

    Tam to malo svoj dovod - zjednodusilo to zapojenie procesora a vsetky vypocty adresy musel robit software. Spocitanu adresu potom slo priamo vystavit na adresnu zbernicu. Samozrejme to ale bolo pomale.

    x86tka trpela na maly pocet registrov uz aj bez toho, takze by toto riesenie bud muselo fungovat na viacerych dvojiciach registrov, alebo by bolo zbytocne zlozite do tej miery, ze priame pouzitie 32-bitovych registrov by uz bolo jednoduchsim riesenim.

    Nie je to priamociare, ale zbytocne komplikovane.

  • 4. 6. 2025 15:37

    CHe

    No v rámci 16b architektúry je to priamočiare riešenie, keďže to umožňuje ponechať drvivú väčšinu CPU 16-bitovú, bez potreby ťahania širších interných zberníc a nárastu množstva tranzistorov. Širšie musí fungovať len minimálna časť, týkajúca sa adresovania. To je dosť podstatný aspekt, čo sa týka nákladov (v čase vzniku). Plus teda to širšie adresovanie museli riešiť tak či tak, akurát zvolili riešenie 1. natvrdo, bez rezervy našité na 20b adr. zbernicu a 2. hodne komplikujúce kód operujúci na širších než 64k štruktúrach resp. štruktúrach ležiacich naprieč segmentami.

    A čo sa týka 8080 a inštrukcií so 16b operandami, Z80 možnosti v tomto smere hodne rozšíril.

  • 3. 6. 2025 21:16

    Michal Kubeček

    Ten komentář o problematičnosti segmentace na 8086 z pohledu pointerové aritmetiky IMHO zapomíná na skutečnost, že podle standardu jazyka C je rozumné fungování pointerové aritmetiky garantováno pouze v rámci prvků jednoho pole (a navíc pointeru bezprostředně za jeho poslední prvek). Jinak řečeno, a priori např. nemůžeme vzít dva náhodné pointery na stejný typ, odečíst je a očekávat smysluplný výsledek.

    V praxi to samozřejmě vždycky nějak funguje a vývojáři si na to zvykli natolik, že na toto omezení dávno zapomněli, pokud o něm tedy vůbec kdy věděli. IMHO to ale znamená, že ten problém zmiňovaný v článku nijak zásadní není. Stačí, aby pole "žilo" buď v rámci jednoho segmentu nebo nějaké posloupnosti navazujících segmentů, a pointer, který dostaneme aplikací operátoru & na prvek pole, byl vždy reprezentován hodnotou z tohoto segmentu, a při korektním používání žádný problém nevznikne.

  • 3. 6. 2025 21:40

    Pavel Tišnovský
    Zlatý podporovatel

    Myslíš problém pointerové aritmetiky jako takové nebo porovnání ukazatelů? To první je spíš past na vývojáře, který si musí hlídat, co se mu vlastně vrátí (buď rozdíl offsetů, a když má štěstí, tak rozdíl celých adres). To druhé - tyjo ono to v BC vždycky fungovalo tak napůl a ještě to záleželo na volbě Fast huge pointers (to v podstatě vypnulo normalizaci ukazatelů, ale i když je zapnutá, tak nikde není jistota, že se někde neobjeví nenormalizovaný ukazatel).

    V podstatě, pokud se dělalo se strukturami pod 64kB, tak to bylo v pohodě, jinak začaly problémy - typicky se nesahalo na volby překladače, aby to nějak drželo dohromady :-)

    PS: byla jiná doba, žádné velké automatické testování se nekonalo; asi na to ani nebyla infrastruktura

  • 3. 6. 2025 21:54

    Michal Kubeček

    Myslel jsem pointerovou aritmetiku obecně. K porovnávání se tam píše tohle:

    6.5.9.6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.

    přičemž k té poslední části je poznámka pod čarou:

    Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior.

    Celkově mi z toho vychází, že pokud nebudu v programu spoléhat na nedefinované chování nebo si pointery "cucat z prstu", neměla by ta nejednoznačná reprezentace vést k neočekávaným nebo nelogickým výsledkům ani při porovnávání.

    Dnes je samozřejmě všechno jednodušší, když máme 64-bitové procesory a na segmenty se můžeme vykašlat.

  • 4. 6. 2025 10:16

    atarist

    no a praxe je taková, že například pointer ukazuje na začátek VideoRAM. Co teď s tím? Objekt to žádný není, prostě mám pointer typu (řekněme) unsigned char*. Pointerovou aritmetiku v tomto případě naprosto chceme, možnost přičítat offsety taky a mělo by to fungovat. V rámci offsetů to funguje vždy, mimo ně IMHO jen v Huge režimu u huge ukazatelů - jinak ne jestli si pamatuju.

  • 3. 6. 2025 21:50

    kvr kvr

    Nemyslím, že by někdo i dneska odčítal náhodné pointery - jaký by to mělo význam?

    Ten problém se segmentací je, že můžu mít pole větší než 64 kB a pak posun v rámci pole, stejně jako spočítání offset prvku, vyžaduje 32-bitovou operaci a výsledek (a v případě pointeru změnu segment i offset). Nevím, jakého typu je off_t (pokud vůbec byl v tehdejším standardu).

    Tedy jenom pro Huge memory model - musel jsem si ty názvy připomenout. Klobouk dolů - psát o tomhle musel být masochismus :-D .

  • 3. 6. 2025 22:25

    forth

    Podle me provnavat 2 pointery a resit problem ze ukazatel na stejne adresove misto v pameti muze byt reprezentovan vice kombinacemi nedava moc smysl.

    Pokud je to vyssi jazyk tak te to ma odstinit od toho ze ty hodnoty mohou byt ruzne, protoze se prekryvaji a videt to v C jenom jako vyslednou 20 bitovou adresu.

    Pokud si to placas v assembleru tak zase existuje jen jedno "normalizovane" reseni kdy mas segment na te hodnote, aby ofset mohl byt nulovy na zacatku.

    Pokud do jednoho 64k segmentu davas vice mensich poli a delas si z toho datovy segment, tak si taky najdes jednu hodnotu, kde prvni pole zacina a kterou chapes jako "normalizovanou". Urcite se ti nechce porad cachrovat se segmenty. A pokud jo, tak je nahodne neporovnavas.

    Pokud ti pak vystane potreba resit zda to ukazuje na stejne misto v ramci pole, tak ti staci porovnat jen offset.

    Kdyz je to vic nez 64k tak si to nejak resis sam. Mas vic moznosti. Muzes si vybrat co se ti hodi. Pokud mas velke pole 16 bajtovych polozek, tak muzes mit zase nulovy offset a porovnavat jen segmenty.

    Nemyslim si ze by pri navrhu procesoru tohle videli jako velky nedostatek. Skoda jen ze nepreskocili prvni iteraci a rovnou nezvolili zarovnani 256 bajtove, ze by prvni limit nebyl mega ale rovnou tech 16 mega.

    PS: Priznam se ze jsem v tom dost plaval (a ted si to zase nepamatuji), protoze jak nebyl dostatek informaci, tak bylo pro me tezke poznat ktery segmentovy registr instrukce vlastne pouziva... Mam pocit ze to slo zmenit prefixem, ale to byla instrukce o bajt delsi. Takze kdyz jsem psat 256 bajtove intro do souteze, misto abych se ucil na zkousku z fotogrammetrie, tak to byl dost horor i bez toho samomodifikujiciho se kodu pro Bresenhamuv algoritmus kresleni cary v SVGA...

    3. 6. 2025, 22:28 editováno autorem komentáře

  • 3. 6. 2025 23:33

    forth

    Kdyz se koukam na tu tabulku:

    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

    Tak pokud budu ignorovat podvarianty s delkou kodu tak C resi jen 3 pripady.

    1) Mas jen 64 kb na vsechny data, takze segment se nemeni a C te od nej odstini a mas 16 bitovy pointer (jen offset).

    2) Mas 1 mb na data, ale alokovat muzes najednou max. 64kb na pole. To same co predchozi vidis zas jen 16 bitovy pointer (jen offset). Jen nesmis porovnavat ruzne pole mezi sebou, protoze maji ruzny segment.

    3) C ti povoli vetsi jak 64 kb pole. Pak se to ma idealne chovat jako 20 bitovy pointer i kdyz v 32 bitovem cisle. Spravne by ten popisek u Large mel byt celkove 1MB pro data max 64kb velka.

    Zajimalo by me zda jde v tom druhem rezimu udelat vetsi pole diky tomu ze C umi delat vicerozmerna pole vice zpusoby a pokud alokujes nejaky rozmer zvlast a vyssi je jen pole pointeru... No, v te dobe jsem stejne neumel a delal v BP, protoze mel lepsi vysledny kod. BC produkoval vetsi binarky. Pridaval na konec nazvy fci a jeste neumel automaticky vynechat z kodu fce co se nepouzili. Volajici navic uklizel zasobnik po kazdem volani. Hruza, clovek se snazil prejit na C a pak prislo jen zklamani. :)

  • 4. 6. 2025 0:15

    forth

    Aha tak aby mohlo v LARGE modelu fungovat pole pointeru na az 64kb dalsi pole tak ten pointer musi byt fyzicky 32 bitovy, takze te C nemuze odstinit a tvrdit ti ze je to 16 bitove, protoze segment nesmis prekrocit/zmenit.
    On je vlastne pointer jakoby nezavisly na jaky objekt ukazuje. Jakoby anonymni a pokud skryjes ze kazdy objekt ma vlastni segment, tak by C ani nevedelo k jakemu teda segmentu pointer odkazuje... Musel by si to odvodit z kodu k cemu to bylo inicializovany. Takze uz by ten pointer nebyl jakoby nezavisly. Komplikace.

    Nenapada me u kodu jaky mit jiny pointer nez na fci.

    Model   Kód    Funkční pointer   Data    Datový pointer Max data
    TINY    near    16 bit (near)   near    16 bit (near)   64 KB celkem (kód + data)
    SMALL   near    16 bit (near)   near    16 bit (near)   64 KB
    COMPACT far     32 bit (far)    near    16 bit (near)   64 KB
    MEDIUM  near    16 bit (near)   far     32 bit (far)    >64 KB
    LARGE   far     32 bit (far)    far     32 bit (far)    >64 KB (objekty max 64 KB)
    HUGE    far     32 bit (far)    far     32 bit (huge)   >64 KB, i pole přes 64 KB

    Tohle je asi komplexnejsi tabulka jak to vypadalo.

  • 4. 6. 2025 10:18

    atarist

    "Pokud je to vyssi jazyk tak te to ma odstinit od toho ze ty hodnoty mohou byt ruzne, protoze se prekryvaji a videt to v C jenom jako vyslednou 20 bitovou adresu."

    právěže to vidíš jako segment:offset i přímo v tom céčku. V podstatě nikdy z toho nikdo výslednou 20bitovou adresu nepočítá (pokud to nenapíšeš explicitně sám), pořád to vnitřně je jako pár dvou 16bitových hodnot. Jsou na to ta tři makra zmíněná v článku a nějaký funkce okolo.

  • 4. 6. 2025 14:29

    Pavel Tišnovský
    Zlatý podporovatel

    cely ten problem se vlastne resi jen kvuli tomu, ze bazove adresy (a bazove registry) jsou 16bitove a ne 32bitove, coz by vsechny problemy vyresilo na dlouhou dobu dopredu. Nebo jeste jinak - kdyby si IBM zvolila Motorolu 68000, tak by se to neresilo, ale IBM melo svoje pravidla (dva dodavatele) a to Motorola nesplnovala.

  • 4. 6. 2025 17:54

    CHe

    Tých faktorov prečo nie 68k tam bolo viacero, okrem second sources aj nedostupnosť variantu s 8b dátovou zbernicou (ktorú kvôli nákladom preferovali) dostatočne včas - 68008 bol uvedený rok po IBM 5150; nižšia hustota kódu; horšia nadväznosť na CP/M ekosystém a 8080 tooling; vyššia cena. Plus je otázka, či by to bolo vo výsledku podobne úspešné, pokojne by sa mohlo stať, že by nakoniec prerazila nejaká konkurenčná platforma s 8086/88 práve kvôli tej jednoduchej migrácii existujúceho CP/M kódu.

  • 4. 6. 2025 18:02

    Pavel Tišnovský
    Zlatý podporovatel

    jako souhlas, je to taková spekulace. Na druhou stranu platformy s Motorolou byly všechny dost fajn a aspoň ten Macintosh přežil a na M68 jel docela dlouho.

  • 4. 6. 2025 19:20

    atarist

    na druhou stranu to nastavilo smer vyvoje IBM PC - neustale flikovani bez myslenky na nejaky dlouhodobejsi vyvoj, jen zmeny, ktery uz proste musely byt a nekompatibilita

  • 5. 6. 2025 9:32

    cblusb

    A proto PC zvítězilo na celé čáře, přes všechny potíže. 386, chráněný režim a V86 režim nakonec splinily požadavky na pokročilejší operační systémy a zpětnou kompatibilitu.
    Nebyl bych tak přísný, 80. léta byla bouřlivá dekáda a vidět do budoucnosti bylo tehdy nanejvýš obtížné.

    5. 6. 2025, 09:33 editováno autorem komentáře

  • 5. 6. 2025 9:53

    atarist

    spis uspelo proto, ze ty PC skladala kazda garazovka, vetsinou s "vypujcenym" BIOSem (jedina vec, kterou mela IBM licensovanou) - to by dneska uz v zadnem pripade neproslo. Nevyhrala technologie, ale cena, zatimco konkurenti si to rozdavali mezi sebou. Apple taky chvili zkousel jit cestou licencovani vyroby, ale potom z toho seslo (a ty klony nebyly cenove marny).

  • 5. 6. 2025 10:45

    Michal Kubeček

    Nevyhrala technologie, ale cena,

    Takhle ale trh do značné míry funguje. Můžete vymyslet úžasnou super sofistikovanou technologii, ale pokud ji nebudete schopen vyrábět - a hlavně prodávat - za cenu, která osloví dostatečný počet zákazníků, ekonomický úspěch z toho nebude.

    Koneckonců to bylo vidět už u těch domácích osmibitových počítačů. Bylo ZX Spectrum technologicky nejlepší? Ani omylem, byl to šílený bastl a některá designová rozhodnutí byla dost šílená. Ale právě díky tomu se jim podařilo dostat na cenu, která oslovila dostatečný počet zákazníků, aby síťový efekt dokonal zbytek. S PC to bylo do určité miry podobné.

    A neplatí to jen pro počítače. Byl to Fiat, kdo koupil Alfu, Maserati nebo Ferrari, ne naopak. Nebo se podívejte na seznam značek patřících pod Volkswagen Group nebo Swatch Group, spousta z nich je daleko uznávanější odborníky i laiky, ale ekonomická realita je prostě jiná.

  • 5. 6. 2025 11:05

    atarist

    myslim ze spolu souhlasime - vyhralo levnejsi a skalovatelny reseni.

    ovsem soucasne si stojim za tim, ze to proste nalajnovalo Wintelackou cestu - narovnavace na ohybace vsude, ale ono se to srovnalo (zaplatili to lidi svym casem)

  • 5. 6. 2025 15:21

    Michal Kubeček

    Cena vyhraje zcela vzdy.

    To právě zase ne, nesmí se to přehnat. Je potřeba najít rozumnou míru, aby byla cena dostatečně nízká na to, aby bylo zboží pro většinu dostupné, ale kvalita pořád dostatečná na to, aby pro většinu byl výrobek rozumně použitelný.

  • 5. 6. 2025 12:02

    forth

    Co se vlastne stane kdyz takovy 16 bitovy DOSovsky program se dostane s IP do situace kdy nasledujici insrukce pretece na zacatek toho CS segmentu a nebo inkrementuje CS o...1? 4096? Jinou hodnotu?

    Zkousel jsem napsat:

    .model medium
    .stack 100h
    
    .data
    data_seg segment
        msg_old db 'Overflow!$'
        msg_run db 'Test start...', 0Dh, 0Ah, '$'
        msg_new db 'Next segment!$'
    data_seg ends
    
    .code
    CODE1 segment
    assume cs:CODE1
    assume ds:data_seg
    
        mov ax, data_seg      ; Zajistí správné DS i zde!
        mov ds, ax
    
        mov dx, offset msg_old
        mov ah, 09h
        int 21h
    
        mov ax, 4C00h
        int 21h
    
    
        org 0FFEEh
    start1:
        mov ax, data_seg
        mov ds, ax
    
        mov dx, offset msg_run
        mov ah, 09h
        int 21h
    
        REPT 6
            nop
        ENDM
    
    ;    skok na druhý kódový segment, offset 0xF000
    ;    push cs
    ;    push offset start2
    ;    retf        ; far return - skočí do CODE2:0xF000
    CODE1 ends
    
    .code
    CODE2 segment
    assume cs:CODE2
    assume ds:data_seg
    
    start2:
        mov ax, data_seg      ; Zajistí správné DS i zde!
        mov ds, ax
    
        mov dx, offset msg_new
        mov ah, 09h
        int 21h
    
        mov ax, 4C00h
        int 21h
    CODE2 ends
    
    end start1

    Test spociva v tom, ze mam za sebou dva 64kb bloky programu. Program zacina na konci prvniho a vypise hlasku ze zacal. Na zacatku prvniho bloku je vypsani ze pretekl a ukonceni programu. Na zacatku druheho bloku je vypsani ze je v dalsim segmentu a ukonceni programu.

    C:\TASM\EXAMPLES>TASM \l TEST_07.ASM
    Turbo Assembler  Version 4.1  Copyright (c) 1988, 1996 Borland International
    
    Assembling file:   TEST_07.ASM
    *Warning* TEST_07.ASM(44) REPT(6) Location counter overflow
    Error messages:    None
    Warning messages:  1
    Passes:            1
    Remaining memory:  461k
    
    C:\TASM\EXAMPLES>TLINK TEST_07.OBJ
    Turbo Link  Version 7.1.30.1. Copyright (c) 1987, 1996 Borland International
    
    C:\TASM\EXAMPLES>TEST_07.EXE
    Test start...
    Next segment!

    Napsalo to sice ze to prelezlo do dalsich 64kb, ale je to DOSBOX, a jeste to nebezi na 486 nebo necem takovem.
    Navic tohle je uz nekolikata iterace programu a ty predchozi skoncili chybou napriklad vypsaly misto jedne ze dvou moznych variant treti variantu ktere nemuze nastat: "Test start... Test start..." a radne se ukoncily. Kdyz jsem pridal znovu nastavit DS register a drobne to upravil tak jsem dostla tento vysledek. Bez neho to nekam pretece a vypise celou obrazovky nahodnych znaku nez se sekne.

    x@user:~/Programovani/dosprog/TASM/EXAMPLES$ xxd TEST_07.EXE | grep -v '0000 0000 0000 0000 0000 0000 0000 0000'
    00000000: 4d5a 4101 8200 0300 2000 0000 ffff 0000  MZA..... .......
    00000010: 0001 0000 eeff 1300 3e00 0000 0100 fb71  ........>......q
    00000020: 6a72 0000 0000 0000 0000 0000 0000 0000  jr..............
    00000030: 0000 0000 0000 0000 0000 0000 0000 0100  ................
    00000040: 1300 efff 1300 0100 1310 0000 0000 0000  ................
    00000300: 4f76 6572 666c 6f77 2124 5465 7374 2073  Overflow!$Test s
    00000310: 7461 7274 2e2e 2e0d 0a24 4e65 7874 2073  tart.....$Next s
    00000320: 6567 6d65 6e74 2124 0000 0000 0000 0000  egment!$........
    00000330: b810 008e d8ba 0000 b409 cd21 b800 4ccd  ...........!..L.
    00000340: 2100 0000 0000 0000 0000 0000 0000 0000  !...............
    00010310: 0000 0000 0000 0000 0000 0000 0000 b810  ................
    00010320: 008e d8ba 0a00 b409 cd21 9090 9090 9090  .........!......
    00010330: b810 008e d8ba 1a00 b409 cd21 b800 4ccd  ...........!..L.
    00010340: 21                                       !
    x@user:~/Programovani/dosprog/TASM/EXAMPLES$ python3 exe_info2.py TEST_07.EXE
    Hlavička EXE souboru (MZ):
    
    Addr  Hex bytes     Hodnota         Popis
    -------------------------------------------------------------
    0000  4d 5a         MZ              Signatura 'MZ' (ASCII MZ)
    0002  41 01         321             Počet použitých bajtů v poslední stránce
    0004  82 00         130             Celkem stránek o velikosti 512 bajtů
    0006  03 00         3               Počet relokací
    0008  20 00         32 (=512 bajtů) Velikost hlavičky v para (16B)
    000A  00 00         0               Minimální extra paměť (v para)
    000C  ff ff         65535           Maximální extra paměť (v para)
    000E  00 00         0x0000          Inicializační relativní SS segment
    0010  00 01         0x0100          Inicializační SP offset
    0012  00 00         0x0000          Checksum (obvykle 0)
    0014  ee ff         0xFFEE          Inicializační IP offset
    0016  13 00         0x0013          Inicializační relativní CS segment
    0018  3e 00         0x003E          Offset relokační tabulky v hlavičce
    001A  00 00         0               Overlay number (obvykle 0)
    
    Rezervovaná/neznámá oblast (0x1C - 0x3D):
    001C  01 00         ..              ??? (reservováno/neznámé)
    001E  fb 71         .q              ??? (reservováno/neznámé)
    0020  6a 72         jr              ??? (reservováno/neznámé)
    0022  00 00         ..              ??? (reservováno/neznámé)
    0024  00 00         ..              ??? (reservováno/neznámé)
    0026  00 00         ..              ??? (reservováno/neznámé)
    0028  00 00         ..              ??? (reservováno/neznámé)
    002A  00 00         ..              ??? (reservováno/neznámé)
    002C  00 00         ..              ??? (reservováno/neznámé)
    002E  00 00         ..              ??? (reservováno/neznámé)
    0030  00 00         ..              ??? (reservováno/neznámé)
    0032  00 00         ..              ??? (reservováno/neznámé)
    0034  00 00         ..              ??? (reservováno/neznámé)
    0036  00 00         ..              ??? (reservováno/neznámé)
    0038  00 00         ..              ??? (reservováno/neznámé)
    003A  00 00         ..              ??? (reservováno/neznámé)
    003C  00 00         ..              ??? (reservováno/neznámé)
    
    Relokační tabulka:
    Addr  Hex bytes       Popis
    --------------------------------------------------
    003E  01 00 13 00     Relokace #0: segment:offset=0x0013:0x0001, absolutní adresa=0x00131, v souboru na=0x00331
    0042  ef ff 13 00     Relokace #1: segment:offset=0x0013:0xFFEF, absolutní adresa=0x1011F, v souboru na=0x1031F
    0046  01 00 13 10     Relokace #2: segment:offset=0x1013:0x0001, absolutní adresa=0x10131, v souboru na=0x10331
  • 5. 6. 2025 14:46

    CHe

    Ak sa dobre pamätám (a zbežne hľadám v zdrojoch), CS to samo inkrementovať nebude, pretečie len IP (na hodnotu podľa dĺźky dekódovanej inštrukcie, ktorá začíanala na konci segmentu, pričom ak tam neležala celá, bude sa to snažiť jej ďalšie bajty nafetchovať zase zo začiatku segmentu).

    Korektná implementácia by bola komplikovaná, IMHO, keďže je tam 6-bajtová prefetch queue, ktorej plnenia by sa to zrejme tiež dotýkalo, nezávisle od inkrementovania samotného CS pri vykonávaní nafetchovaných inštrukcií.

    CS zrejme menia len far JMP/CALL, INT a IRET.

  • 5. 6. 2025 15:29

    Pavel Tišnovský
    Zlatý podporovatel

    IMHO ti to na puvodni 8086/8088 nepreleze do dalsiho segmentu. CS se obecne meni pri far jumpech a far returnech (+ pri preruseni), jinak ne. Ale zkusim to taky*

    DOSBoxu se v tomto neda na 100% verit, on nekontroluje nektery limity atd. Narazil jsem na to pri ladeni prechodu do protected rezimu, kde prosly i veci, ktery by realny HW ukoncil vyjimkou.

  • 5. 6. 2025 17:25

    forth

    To byl muj puvodni predpoklad ze se to bude chovat jako na Z80, a proste i kdyby to rozpulilo instrukci tak to jen preleze na zacatek. I kdyz.. pulit intrukci jsem jeste nezkousel.. :D To chce realny hardware.

    TASM na tebe krici WARNINGy i kdyz se JEN dotknes 65535 adresy! I kdyby tam lezelo RET nebo ukoncujici INT 021h. Je to problematicke jako tisknout posledni znak na posledni radce a nevyvolat srolling.

    Podle me kdyz je tam RET tak procesor pri vykonavani uz ukazuje na dalsi instrukci, takze pokud by to automaticky zvysilo i CS o 4096 tak bys tim RETem ktere pak zmeni jen OFFSET skoncil nekde jinde nez si myslis.

    Takze bud je to nejak osetreny, nebo se to chova jinak. A okrajove pripady se mozna mohou i lisit procesor od procesoru.

    Ale predstava ze kdyz se blizis s IP ke konci te to nuti delat JMP FAR o par bajtu dal je uzmevna a nebo mozna hororova. Zvlast kdyz se da ten 64 KB konec posunout libovolne po 16 bajtovych krocich, takze na te 20 bitove realne adrese predel muze lezet kdekoliv.

    AI me tvrdi ze CS se samo neumelo menit, jen temi skoky, volanim a navratem volani a prerusenim. Takze je to takovy maskovany osmibit. Trosku vylepsena Z80. ZX Spectrum 128, kde nekdo pridal vic pameti. Takovy Pentagon. Jen to misto posunu o 16 bajtu prepina 16 KB banky.

    A proto mozna jen HUGE rezim se chova k pointerum jak ocekavame, protoze je to jen emulovane chovani.

  • 5. 6. 2025 19:52

    CHe

    To už to haníš viac než tomu patrí, IMHO... Externe dobastlený bank switching, vyžadujúci I/O alebo memory zápisy na prepnutie je ešte ďaleko ďaleko neefektívnejšie a obmedzujúcejšie zúfalstvo, než segmentácia. A nie, ani 8-bit z toho segmentácia ničím nerobí, 86 je plnohodnotný 16-bit.

    Čo sa týka kódu naprieč segmentami, je to okrajový prípad. Vyšší jazyk to nevyrobí, jednotlivé procedúry sú normálne rádovo kratšie než 64k a navzájom sa v príslušných modeloch môžu volať far volaniami (pričom vďaka práve tomu prekryvu segmentov po 16B sa veľa nestratí ani na ich rozložení v pamäti tak, aby kód žiadnej neležal naprieč hranicou segmentu).

  • 6. 6. 2025 12:53

    forth

    Mas pravdu s tim, ze normalni kod se vlastne deli na relativne male funkce. Takze pokud vsechno volame FAR, tak problem s prekrocenim segmentu nenastane – jen za cenu toho, ze by nekdy stacilo i NEAR volani.

    A ono to vlastne v assembleru jde. Muzes si vytvorit knihovnu, ktera obsahuje verejne funkce volane FAR a sama pouziva neverejne funkce volane NEAR.

    Staci si jen vytvorit novy segment a kod umistit v ramci neho:

    .code
    RANDOM_CODE_NAME segment
    assume cs:RANDOM_CODE_NAME
    
    ...zde mas sve funkce...
    
    RANDOM_CODE_NAME ends

    Prekladac nastavi pro ten segment takovou hodnotu CS, aby na zacatku bylo IP co nejbliz nule – mozna to dokonce zarovnava na paragrafy? Takze je IP vzdy nula?
    V cele oblasti RANDOM_CODE_NAME je pak CS konstantni. A i kdyz se tam volaji funkce odjinud, tak se to resi tak, ze pro CS se zvoli ta konstantni hodnota.
    Tohle je strasne dulezite, protoze pokud to chces trolit, tak jsi schopen volat verejne funkce v te oblasti s jinym CS – jen pomoci toho, ze zmenis offset – a to muze vest k tomu, ze ti program spadne!

    Dejme tomu, ze volas pres FAR nejakou funkci uprostred te oblasti, ale ne pres CS:16000, ale pres hodnotu CS+1000:0. Tim vytvoris uplne jiny rozsah, kam se lze relativnimi skoky dostat. Ta funkce ma uvnitr sebe relativni skok na neverejnou funkci CALL -1000.
    Puvodne bys teda skoncil na adrese CS:16000-1000. Ted skoncis na adrese (CS+1000):0-1000, coz je jina fyzicka adresa. Relativni odkazy pro stejnou adresu, ale s jinym zvolenym CS, funguji jen do te doby, co skaces na prunik pristupnych oblasti, ktere kazde CS ma.
    Napada me tohle pouzit jen proti nejakemu antiviru nebo analyze kodu – je to spis bolest nez uzitecne.

    Zkousel jsem vymyslet, zda lze napsat funkci, kterou lze volat jak FAR, tak NEAR, a bude korektne fungovat – abys usetril bajty a takty, pokud je ta funkce v dosahu.
    A ano, lze takovou funkci napsat – dokonce ve dvou variantach.

    Prvni varianta: Funkce je primarne FAR a ty osetris pripad, kdy je volana NEAR.
    Na to staci upravit zasobnik tak, aby se funkce mohla korektne ukoncit pres RETF.

    Bohuzel Intel zvolil reseni, kdy prvni polozka pri FAR volani, kterou vytahnes ze zasobniku, je offset a ne segment – takze nestaci pred funkci pridat PUSH CS.

    fce_near:
        pop dx
        push cs
        push dx
    fce_far:
        mov dx, offset msg
        mov ah, 09h
        int 21h
    
        retf

    Kupodivu od 386 by to uz i usetrilo takty. Jinak je to ale horsi nez to volat rovnou FAR.

    Druha varianta: Funkce je primarne NEAR a ty osetrujes pripad, kdy je volana FAR.

    fce_far:
        call fce_near
        retf
    fce_near:
        mov dx, offset msg
        mov ah, 09h
        int 21h
    
        ret

    V podstate mas dve funkce, z nichz jedna je FAR a vola tu druhou NEAR.
    Kupodivu i to muze usetrit bajty i takty – zalezi, v jakem pomeru FAR a NEAR tu funkci volas.

    Od 286 jsem ale nasel instrukci, co umi pushnout konstantu na zasobnik.
    Takze muzes nahradit CALL fce_near za pouhe ulozeni konstanty na zasobnik, kde ta konstanta ukazuje na offset nejake RETF instrukce v ramci tveho segmentu.
    Nekde na zacatku musis napsat direktivu .286, jinak ti to assembler bude emulovat PUSH imm, tedy vygeneruje pomaly kod.

    .code
    CODE1 segment
    assume cs:CODE1
    
    lab_retf:
        retf
    
    fce_far:
        push  0
    fce_near:
        mov dx, offset msg
        mov ah, 09h
        int 21h
    
        ret

    Tohle vypada dost rizikove, ale funguje.
    Otazka je: jak donutit prekladac, aby nahradil 0 za offset, ktery doplni sam?

    Ukazalo se, ze kdyz napises push offset lab_retf, tak to zvoli push imm16. to nechceme protoze je to pomalejsi a my muzeme umistit RETF nekam na zacatek segmentu do offsetu 0 az 127. Pak nam staci rychlejsi a kratsi varianta "push imm8".

    .code
    CODE1 segment
    assume cs:CODE1
    
    lab_retf:
        retf
    
    fce_far:
        db 6Ah, offset lab_retf
    fce_near:
        mov dx, offset msg
        mov ah, 09h
        int 21h
    
        ret

    Nejlepsi reseni, co jsem nasel.
    Pri pomeru cca 50/50 (FAR vs NEAR) je tahle varianta asi nejlepsi.
    Sice na rozdil od prvni vzdy zpomaluje FAR, ale zase NEAR je bez postihu.
    Druha varianta nepostihuje FAR, ale NEAR zrychluje jen mirne – a to v taktech az od 386+.

    Prakticky to ale asi nejde moc pouzit.
    Myslim, ze nemas zpusob, jak si overit, ze dva nezavisle segmenty jsou dostatecne blizko, aby misto FAR slo volat NEAR.
    Takze jedine v ramci jednoho segmentu, kdy je nejaka funkce verejna a zaroven se pouziva vnitrne.

  • 10. 6. 2025 14:52

    Wladows

    Pokud chceš volat FAR funkci ležící ve *stejném* kódovém segmentu, je možné použít NEAR CALL instrukci. Jen je nutné na zásobník předem uložit obsah CS registru pro kompletní návratovou adresu:

    push cs
    call near func
    ...

    func:
    ...
    retf

    Řada překladačů (myslím i Borland C++) to tak dělala.

  • 5. 6. 2025 14:00

    forth

    Chtel jsem pochopit jak je delana relokacni tabulka v DOSovskem EXE. Takze jsem si udelal maly programek, kde jsem se snazil vynutit v small rezimu (kod a data zvlast do 64 kb) dlouhy skok. Skok jen skace za sebe.

    .model small
    .stack 100h
    
    .data
    msg db 'Hello$'
    
    .code
        jmp far ptr start
    start:
        mov ax, @data
        mov ds, ax
        mov dx, offset msg
        mov ah, 09h
        int 21h
        mov ax, 4C00h
        int 21h
    
    end start

    Program se podarilo zkompilovat i spustit a radne ukoncit. List ukazoval tohle

    Turbo Assembler  Version 4.1        06/04/25 20:35:20       Page 1
    TEST_02.ASM
    
    
    
          1 0000                 .model small
          2 0000                 .stack 100h
          3
          4 0000                 .data
          5 0000  48 65 6C 6C 6F 24      msg db 'Hello$'
          6
          7 0006                 .code
          8 0000  EB 03 90 90 90         jmp far ptr start
          9 0005                 start:
         10 0005  B8 0000s           mov ax, @data
         11 0008  8E D8          mov ds, ax
         12 000A  BA 0000r           mov dx, offset msg
         13 000D  B4 09          mov ah, 09h
         14 000F  CD 21          int 21h
         15 0011  B8 4C00            mov ax, 4C00h
         16 0014  CD 21          int 21h
         17
         18                  end start
    Turbo Assembler  Version 4.1        06/04/25 20:35:20       Page 2
    Symbol Table
    
    
    
    
    Symbol Name           Type   Value
    
    ??DATE                Text   "06/04/25"
    ??FILENAME            Text   "TEST_02 "
    ??TIME                Text   "20:35:20"
    ??VERSION             Number 040A
    @32BIT                Text   0
    @CODE                 Text   _TEXT
    @CODESIZE             Text   0
    @CPU                  Text   0101H
    @CURSEG               Text   _TEXT
    @DATA                 Text   DGROUP
    @DATASIZE             Text   0
    @FILENAME             Text   TEST_02
    @INTERFACE            Text   000H
    @MODEL                Text   2
    @STACK                Text   DGROUP
    @WORDSIZE             Text   2
    MSG               Byte   DGROUP:0000
    START                 Near   _TEXT:0005
    
    Groups & Segments         Bit Size Align  Combine Class
    
    DGROUP                Group
      STACK               16  0100 Para   Stack   STACK
      _DATA               16  0006 Word   Public  DATA
    _TEXT                 16  0016 Word   Public  CODE

    Vsechno v poradku nez si uvedomite ze:

    8 0000  EB 03 90 90 90         jmp far ptr start

    Je relativni skok za tri nopy, ktere pouziva jako vypln, aby velikost kodu byla shodna. Rychlost je sice vic jak dvojnasobna, ale je to tak trochu podvod, kdyz to dela neco jineho nez po nem chci bez jakehokoliv upozorneni.
    Vlastne se me ten dlouhy skok nepodarilo udelat ani s

    db 0EAh
    dw offset start
    dw segment start

    protoze to selze kvuli syntaxi.

    Tak jako tak to vypada ze diky te segmentaci ty programy MUSI byt zarovnane po 16 bajtech. Takze relokace se dela jen u segmentu. EXE to nijak nezkrati, protoze ten ma ty zaznamy vedene jako relativni adresu do souboru a ta je 32 bitova, protoze soubor muzes byt vetsi jak 64 kb.
    Navic EXE si rekl, ze kdyz nejmensi sektor na diskete ma 512 bajtu tak tohle bude nejmensi mozna jednotka. Hlavicka nebude mensi. A to zarovnani vseho na 16 bajtu, jako je zasobnik, atd. se s nama asi drzi dodnes.