Impulzus

 
A Budapesti Műszaki és Gazdaságtudományi Egyetem Villamosmérnöki és Informatikai Kar Hallgatói Képviseletének lapja

Y1.999K

Miközben a régi, hasznos programoknak egyre kisebb hányada indul el az egyre gyorsabb gépeken, képes mindenki a 2000. éven verni a nyálát, amikor tudvalevőleg leáll az összes kólaautomata, internetes hűtőszekrény, tamagocsi, paparazzi, digitális partyvonal és tíz percet késnek az internetes pizzarendelések is.

Ha az ember sok programot ír saját használatra, előbb-utóbb tele lesz a vinyója igénytelen kinézetű programokkal, amik kiírják, hogy "E!", aztán várnak valami inputot.

De szükségszerűen lesz olyan programja is, aminek az igényszintjére kínosan ügyel, vagy azért, mert eladásra készült, vagy csak az a Program a szeme fénye, a szíve csücske, vagy egyéb fiktív anatómiai részeg(y)sége.

És ha már büszke rá, akkor be is mutatja a programot másoknak. Mire a Program: "Runtime error 200 at CS:0091".

Felettébb bosszantó. Főleg azért, mert ez minden Turbo Pascal 7.0 alatt fordított programnak közös sorsa, amennyiben egy 233MHz-nél gyorsabb Celeronon, vagy PII-n futtatjuk.

Az ok egyszerű. A CRT unit delay függvényéhez tartozó inicializáló rutin tartalmazza az alábbi kódfoszlányt:

mov	cx,0037hdiv	cxmov	DelayCnt,ax

Ugye. Ez lenne hivatott kiszámolni, hogy mennyi is az 1 ms-os késleltetés. Ha nem csordulna túl. És ha az i80x86 tervezőinek nem lenne rögeszméje, hogy a divide overflow és a division by zero egy és ugyanaz.

Dehát ez van. Mit lehet tenni?

Mondanom sem kell, hogy a hivatalos support most is – mint mindig – a legrosszabb megoldás. Első nekifutásra egy szót sem láttam az Inprise homepage-én. Bár az is lehet, hogy csak én vagyok ügyetlen az internetes keresésben. Számtalan kiváló freeware patch program létezik azonban, amelyek akár lefordított exe-kből is kiírtják ezt a hibát.

Ezek megtalálásához jó kiindulópont mondjuk a http://sun01.brain.uni-freiburg.de/~klaus/pascal/runerr200 cím.

De tekintsünk egy kicsit a probléma mélyébe! Ha valaki nem szeretné, ha a Turbo Pascal mindenhatóságába vetett hitét elveszítené, az fordítsa félre szemérmesen a fejét, mi pedig lássuk a bűnöst!

Ehun e!

A Delay függvény lényege:

(Természetesen kizárólag oktatási céllal került visszafejtésre, sőt, eredeti célom mindössze a debuggolás volt.)

CS:02A8 8BDC	mov	bx,spCS:02AA 368B4F04	mov	cx,ss:[bx+04]	; igény–, dizájn– paraméterkiszedés a verembőlCS:02AE E313	jcxz	02C3CS:02B0 8E064400	mov	es,[0044]	; na, ennek az értéke konstans 40h (civilben Seg0040, még a map file is így hívja)CS:02B4 33FF	xor	di,diCS:02B6 268A1D	mov	bl,es:[di]	; ez pedig a com1 báziscímének az alsó byte-ja!!! :)CS:02B9 A15C00	mov	ax,[0060]	; az ominózus DelayCntCS:02BC 33D2	xor	dx,dx	; amire sajnálták a 4 byte-otCS:02BE E80500	call	02C6	; késleltető rutin...CS:02C1 E2F6	loop	02B9	; cx millisec-igCS:02C3 CA0200	retf	0002CS:02C6 2D0100	sub	ax,0001	; késleltetés szintén kritikán aluli módonCS:02C9 83DA00	sbb	dx,0000	; na, ezt magyarázza meg nekem valaki...CS:02CC 7205	jb	02D3         	; látszik, hogy utólag lett 2 byte a DelayCnt, különben miért csökkentenék a dx-et?CS:02CE 263A1D	cmp	bl,es:[di]	; megvan még a soros port? :)CS:02D1 74F3	je	02C6         	; ha nem, akkor már biztos letelt 1 ms, ezt a logikátCS:02D3 C3	ret		; a fenti ciklust egy újabb proci pillanatok alatt végrehajtja, "branch prediction"-östől, meg minden
Megjegyzések:

1. Az olvasónak talán feltűnt, hogy itt a paraméter címzésére minden híreszteléssel ellentétben nem a BP-t használják. Ennyit a következetességről.

2. Miért pont a com1 báziscímének alsó byte-ja? Gondolom, ipari titok. Mint ahogy az is, hogy miért nem ROM-ból olvasnak, ha már az a cél, hogy az értéke változatlan maradjon? (Javasolnám a köztiszteletben álló 0ffff:0000 címen található EA byte-ot.) Mert én voltam olyan gonosz, és írtam egy aranyos kis programot, ami billentyűleütésre átírja a com1 báziscímét, és rögtön nem kellett várni a delay(10000); -nél, ráadásul minél gyorsabb géped van, annál inkább begyorsul ilyenkor.

3. Miért nullázza ki a főrutin tűntetően a dx-et, amikor a vak is láthatja, hogy a 02C6-nál kezdődő szubrutint dx-ax négybájtos DelayCnt-re írták? Ez is ipari titok. (Egyébként ez az xor dx,dx az, amit patch-eléskor valami értelmesebbre kell lecserélni)

4. Ha kicsit is igényesebbre írták volna meg a késleltetést, (mondjuk interruptosra, bár a hardveres időzítéssel a PC hadilábon áll) nem kéne most ilyenekkel szívni.

És ha ez még mindig nem elég, kukkantsunk bele az inicializáló rutinba:

CS:0071 8E064400	mov	es,[0044]	; es=40hCS:0075 BF6C00	mov	di,006C	; 1/18.2 másodpercmutató (ahogy azt a BIOS gondolja :) )CS:0078 268A1D	mov	bl,es:[di]CS:007B 263A1D	cmp	bl,es:[di]CS:007E 74FB	je	007B	; timer ütem kezdetéhez szinkronizálCS:0080 268A1D	mov	bl,es:[di]	; timer alsó byte-ja dl-beCS:0083 B8E4FF	mov	ax,FFE4CS:0086 99	cwd		; tessék, most négy byte-ot adnak a 02C6 szubrutinnakCS:0087 E83C02	call	02C6	; késleltetés 1 timer ütemigCS:008AF7D0	not	ax	; dx-ax megadja, mennyi ciklusba tellettCS:008C F7D2	not	dxCS:008E B93700	mov	cx,0037	; 37h, az "empirikusan meghatározott konstans" amúgy 37h=1000/18.2, tehát egy timer click ennyi ms)CS:0091 F7F1	div	cx	; na, itt hal meg a programCS:0093 A36000	mov    [0060],ax	; és ez lenne a DelayCnt, ha nem állna le a program az előző utasításnál

Ezekből a példákból remélem kitűnik, hogy semmi különleges rendszerprogramozói tudás nem kellett volna ahhoz, hogy ezt a hibát elkerüljék. Csak ez már nem üzlet.

Legújabb mese az okos programozóról

A 02C6 rutin újrafelhasználása iskolapéldája annak, hogy miként lehet bugokkal telerakni egy programot. Ugyanis a CS:02CE címen található utasításnak csak inicializálásnál van értelme, de az igazi rutinban hibás dummy értéket (nem egy read-only terület címét) töltenek es-be és di-be.

Ennyit a csodás tervezési eljárásokról. Állapotminimalizálás, DFD, mifene. Mind semmit sem ér, ha az ember nem tervezi meg valóban a programot. Mert mondhat bárki bármit, assembly program tervezésekor rajzolhatsz fel állapotgépeket, meg mindent, de csak akkor érsz el vele valamit, ha átlátod mind a problémát, mind a megoldást.

Nem könnyű dolog, olyannyira nem, hogy egyesek "szoftver krízis"-ről kezdtek el óbégatni, és tessék, csináltak nekünk egyet. Nevezetesen azt, hogy a tervezési módszereket egyesek többre becsülik, mint magát a programozót, aki akár hülye is lehet, mert ha jó a módszer, baj nem lehet.

Az "okos" programozó fennhangon kiabálja a betanult frázisait ("code reuse!", "FSM!", "JSP!"), de egy harminc byte-os rutint nem képes átlátni, mert neki azt tanították holmi álpszichológusok, hogy csak hét dolgot lehet egyszerre megjegyezni. (Ami igaz is, csak az nem mindegy, hogy mit tekintesz dolognak, egy profi fogalmai ugyanis nagyságrendekkel bonyolultabbak, ettől profi.)

A "buta" programozó pedig írhatja a patcheket az "okos" programozó működésképtelen, ámde kifogástalanul dokumentált programjaihoz.

Wigyorka

[A hibára patch azóta már a Borland Magyarország homepage-én is elérhető: http://www.borland.hu – a szerk.]


A Turbo Pascalnak ezen kívül még két súlyos hibája van: először is nem ingyen van, másodszor pedig ősidők óta nem fejlesztették. És egy idő után az ember megunja azt, hogy 286-os kódot futtasson a gépén. Erre javasolhatom megoldásként a Free Pascal nevű entitást. Most a 0.99.10-es verziónál tart, tehát még nem teljesen stabil a dolog, viszont ingyenes, szabad a forrása, i386 vagy m68k kódot fordít és rendesen dokumentált. Aki próbált már akár csak picit is hordozható Pascal programot írni, az tudja, hogy ezek a tulajdonságok milyen sokat érnek. http://yuc.sch.bme.hu/~lsp/fpc/