Mehr TURBO-Kompatibilität für MAXON-PASCAL

Eine Unit, in der zusätzliche Befehle für MAXONPASCAL bereitgestellt werden, erhöht die Kompatibilität zu TURBO ganz erheblich. Und auch auf das Utility BINOBJ muß man unter TOS nun nicht mehr verzichten.

Mit der Version 1.5 hat MAXON-Pascal ein beachtliches Maß an TURBO-Kompatibilität erreicht. Dennoch ist man vor einigen Enttäuschungen nicht gefeit, wenn man Quellen vom IBM-PC ohne Änderungen auf dem Atari übersetzen will: so moniert vielleicht der Compiler daß er die Befehle Sound() und NoSound nicht kennt, und Systemaufrufe über Intr() sind ihm natürlich auch fremd. Bleiben einem solche Fehlermeldungen erspart, gibt es dafür u.U. beim Programmlauf einige Überraschungen: daß anstatt der Blockgrafiksymbole hebräische Buchstaben erscheinen, hatte man ja noch erwartet, schwerwiegender ist dagegen, daß auf Funktionstasten nicht reagiert wird und beispielsweise der Programmabbruch über Alt-X auch nicht funktioniert. Alle diese Macken lassen sich relativ einfach mittels einer geeigneten Unit beheben, wie im folgenden gezeigt werden soll. Dabei wurden die Prozeduren weitestgehend in Assembler geschrieben, um einen kompakten und vor allem schnellen Code zu erhalten. Zusätzlich zur Kompatibilitäts-Unit wurde das unter MS-DOS bekannte und geschätzte Utility BINOBJ nachempfunden.

Tasten und Töne

In TURBO-Pascal liefert ReadKey beim Drücken von Sondertasten erst einmal chr(0) und beim nächsten Aufruf den Scancode der Taste zurück, in MAXON-Pascal beschränkt sich die Funktion in diesem Falle auf ersteres. Da die Tastatur-Scancodes des Atari weitgehend mit denen des PC übereinstimmen, ist es kein Problem, ein ReadKey zu schreiben, das sich 100%ig turbolike verhält. Mit diesen Eigenschaften ist die neue Funktion sicher nicht nur für die Umsetzung von TURBO-Quellen interessant, da nun Funktionstasten abgefragt werden können, ohne auf Systemaufrufe zurückgreifen zu müssen.

Nächstes Manko sind die fehlenden Prozeduren Sound() und NoSound, die ebenfalls problemlos auf dem Atari implementiert werden können. Die bescheidenen Möglichkeiten des PC zur Tonerzeugung emuliert der Soundchip spielend, wenn man ihm die Parameter ein wenig aufbereitet.

DOS-Interrupts

Sicher ist es nicht sinnvoll, alle DOS-lnterrupts, die möglicherweise einmal in einem Programm benutzt werden könnten, zu emulieren, auf einige wird jedoch besonders häufig zurückgegriffen, so daß eine Nachbildung nützlich erscheint. Je nach Bedarf läßt sich die Unit an dieser Stelle problemlos um weitere Funktionen erweitern.

Der wohl am häufigsten in TURBO-Pascal benutzte Interrupt ist Nr. 5 - die Bildschirm-Hardcopy. Diese Funktion wird auf dem Atari am sichersten realisiert, indem man die Systemvariable _prt_cnt ($4ee, word) auf 0 setzt. Eine andere Möglichkeit wäre der Aufruf der Funktion scrdmp in der Unit BIOS, die jedoch mit einigen nachladbaren Hardcopy-Routinen nicht ganz zurechtzukommen scheint (oder umgekehrt). Damit bietet sich die hier gezeigte Lösung allgemein als Alternative zu dieser Funktion an.

Ebenfalls recht häufig genutzt wird die DOS-Funktion zur Cursor-Konfiguration, meistens um ihn aus- oder einzuschalten. Da sich die Konfigurationsmöglichkeiten auf PC und Atari beträchtlich unterscheiden, wurde nur das Aus-und Einschalten emuliert. Schließlich wurde noch die Abfrage des Umschaltstatus’ der Tastatur mit aufgenommen, allerdings sind hier einige Unterschiede in der Bedeutung der einzelnen Bits zu beachten:

Bit Atari IBM-PC
0 rechte Shift-Taste rechte Shift-Taste
1 linke Shift-Taste linke Shift-Taste
2 Control-Taste Control-Taste
3 Alternate-Taste Alternate-Taste
4 Caps-Lock-Taste Scroll-Lock aktiv
5 rechte Maus-Taste Num-Lock aktiv
6 linke Maus-Taste Caps-Lock aktiv
7 reserviert Insert aktiv

Blockgrafik inklusive

Um die Kompatibilität auch auf solche mehr ästhetischen Aspekte wie die Verfügbarkeit der Blockgrafik auszudehnen, wurde nach einer Möglichkeit gesucht, unabhängig vom eingebauten System-Font zur Programmlaufzeit den vollen IBM-Zeichensatz zur Verfügung zu haben. Das Problem dabei war, daß die Zeichensatzdaten möglichst mit in den Programmcode gelinkt werden sollten, also als Objekt-File vorliegen mußten. Den entsprechenden Zeiger dann auf diese Daten und bei Programmende wieder auf den System-Font zu setzen war demgegenüber eine leichte Übung.

Die Zeichensatzdaten (ohne Fontheader) lagen als Binär-File vor, das für den Linker in ein Objekt-File umgewandelt werden mußte. In TURBO-Pascal gibt es für solche Fälle das Utility BINOBJ. Mit Kenntnis des DR-Objektformats für den Atari ist es nicht allzu schwer, ein analoges Programm zu schreiben. Allerdings ist bei MAXON-Pascal eine Besonderheit zu beachten: da es kein Data-Segment gibt, müssen die Daten im Textsegment abgelegt werden. Da bei MAXON-Pascal ein Codesegment maximal 32K groß sein darf, wird die Konvertierung größerer Binär-Files von vornherein abgewiesen.

Nach der Konvertierung wird man feststellen, daß das Objekt-File nahezu doppelt so groß ist wie das ursprüngliche Binär-File. Flier handelt es sich nicht um einen Fehler in BINO, sondern die Ursache liegt im Aufbau des DR-Objektformats, genauer der sogenannten Fixuptable, die für die Relozierbarkeit der Programme notwendig ist. Obwohl in unserem Falle diese Tabelle aus lauter Nullen besteht, besteht der Linker auf ihrem Vorhandensein in voller Länge (= Länge der Binärdaten).

Die Quelltexte von TPASCAL und BINO können gleichermaßen mit den Versionen 1.1 und 1.5 von MAXON-Pascal, BINO zusätzlich auch mit TURBO-Pascal compiliert werden.

Literatur:

[1] Christoph Conrad, Resource-Einhindung in C, ST-Computer 1/1991


program bino; { Binär-Files in DR-Objekt-Format wandeln } { Daten werden für MAXON-PASCAL-Linker im Text- } { segment abgelegt. j { Aufruf: } { BXNO <src[.BIN]> [<dest[.0]> [<name>]] [/H] } { wird nur «•source. [BIN] > angegeben, sind Wild- } { cards möglich } { Option </H> für Halten des Bildschirms bis } { Tastendruck } { W. Schneider, Berlin 12/1991 } { (c) 1992 MAXON Computer } {$R-,I-,D- , F+} uses crt,dos; var mkres :longint; bnnm,obnm :pathstr; tmpdr :dirstr; tmpnm :namestr; tmpex :extstr; lbnm :string; nms,flcnt :integer; hldscr :boolean; bnrec :searchrec; {*** Erzeugen des Objekt-Files ***} function makeobj (binfl,objfl :pathstr; labname :string):longint; var infl,outfl :file; binsz :longint; binbf :pointer; blkres,i :integer; outbt :shortint; {$IFNDEF ATARI) {* TURBO-PASCAL kennt kein Hi-/LowWord() *} function hiword(lv :longint):word; begin hiword:=lv shr 16; end; function lowordtlv :longint):word; begin loword:=lv and $ffff; end; {$ENDIF) procedure wrlong(outvl :longint); begin outbt:=hi(hiword(outvl)); blockwrite(outfl,outbt,1,blkres); outbt:=lo(hiword(outvl)); blockwrite(outfl,outbt,1,blkres); outbt:=hi(loword(outvl)); blockwrite(outfl,outbt,1,blkres); outbt:=lo(loword(outvl)); blockwrite(outf1,outbt,1,blkres); end; procedure wrword(outvl :longint); begin outbt:=hi(loword(outvl)); blockwrite(outfl,outbt,1,blkres); outbt:=lo(loword(outvl)); blockwrite(outfl,outbt,1,blkres); end; begin {$IFDEF ATARI) reset(infl,binfl); {$ELSE) assign(infl,binfl); reset(infl,1); {$ENDIF) if ioresult<>0 then begin makeobj :=-1; exit; end; binsz:=filesize(infl); if binsz>$7fff then begin close(infl); makeobj:=-4; exit; end; if maxavail<binsz then begin makeobj:=-3; exit; end; getmem(binbf,binsz); blockread(infl,binbf^,binsz,blkres); close(infl); {$IFDEF ATARI) erase(objfl); rewrite(outfl,objfl); {$ELSE} assign(outfl,objfl); rewrite(outfl,1); {$ENDTF} if ioresult<>0 then begin makeobj:=-2; exit; end; wrword($601a); { Magic $601A } wrlong{binsz); { TEXT-Size } wrlong(0); { DATA-Size } wrlong(0); { BSS-Size ) wrlong(14); { Symtablesize } for i:=1 to 5 do wrword(0); { reserved } blockwrite(outfl,binbf^,binsz,blkres);{ TEXT } for i:=1 to 8 do { Symboltable: } begin { ..Name } if i<=length(labname) then outbt:=ord(upcase(labname[i])) else outbt:=0; blockwrite(outfl,outbt,1,blkres); end; wrword($a200); { ..Typ } wrlong(0); { ..Offset } { Fixuptable (lauter Nullen) schreiben } {$IFDEF ATARI)} asm move.l binsz,d0 move.l binbf,a0 @ll: clr.b (a0)+ dbra d0,@ll end; blockwrite(outfl,binbf^,binsz,blkres); {$ELSE} outbt:=0; for i:=1 to binsz do blockwrite(outfl,outbt,1,blkres); {$ENDIF} freemem(binbf,binsz); close (outfl); if ioresult<> then makeobj:=-5 else makeobj:=binsz; end;{makeobj) {*** Extension ggf. an Namen anhängen ***} procedure extend(var expth:pathstr; extl;extstr); var exnm :namestr; exdr :dirstr; exex :extstr; begin fsplit(expth,exdr,exnm,exex); if (exex='') or (exex='.') then expth:=exdr+exnm+extl; end; {*** String in Großbuchst. wandeln ***} function upstr(lwstr :string):string; var i :integer; s :string; begin s:=''; for i:=1 to length(lwstr) do s:=s+upcase(lwstr[i]); upstr:=s; end; {*** ein File behandeln ***} procedure processfile; begin extend(bnnm,'.BIN'); extend(obnm,'.O'); writeln('converting : ',bnnm); writeln('to : ',obnm); writeln('public name: ',lbnm); mkres :=makeobj(bnnm,obnm,lbnm); if mkres>=0 then begin writeln(mkres,' bytes converted'); inc(flcnt); end else case mkres of -1: writeln('Can''t open infile !'); -2: writeln('Can''t create outfile !'); -3: writeln('Not enough memory !'); -4: writeln('Infile too large !'); -5: writeln('Can''t close outfile !'); end; writeln; end; {**** HauptProgramm ****} begin nms:=parameount; flcnt:=0; hldscr:=upstr(paramstr(nms))='/H'; if hldscr then begin dec(nms); clrscr; end; writeln; write('BIN to O Convexter for MAXON-PASCAL '); writeln('Version 1.0 W. Schneider, Berlin 12/1991'); case nms of 0: begin write('Usage: BINO[.TTP] <src[.BIN]>[<de3t[.0]>'); writeln(' [<public name>]] [/H]'); writeln('Press any key'); repeat until readkey<>#0; writeln; halt; end; 1: begin bnnm:=upstr(paramstr(1)); fsplit(bnnm,tmpdr,tmpnm,tmpex); findfirst(bnnm,0,bnrec); while (doserror=0) do begin with bnrec do begin bnnm:=tmpdr+name; fsplit(bnnm,tmpdr,tmpnm,tmpex); obnm:=tmpdr+tmpnm+'.O'; lbnm:=tmpnm; processfile; end; findnext(bnrec); if keypressed then if readkey=#27 then doserror:=-1; end; writeln(flcnt,' files converted'); end; 2: begin bnnm:=upstr(paramstr(1)); obnm:=upstr(paramstr(2)); fsplit(obnm,tmpdr,tmpnm,tmpex); lbnm:= tmpnm; processfile; end; 3: begin bnnm:=upstr(paramstr(1)); obnm:=upstr(paramstr(2)); lbnm:=upstr(paramstr(3)); processfile; end; end; if hldscr then begin writeln('Press any key'); repeat until readkey<>#0; end;

unit tpascal; { Proceduren und Funtionen, die in MAXON-PASCAL } { nicht existieren oder andere Ergebnisse lie- } { fern, werden bereitgestellt bzw. redefiniert. } { W. Schneider, Berlin 12/1991 } { (c) 1992 MAXON Computer } Interface {$R-,D-,F+} Type Registers=Record Case Integer of 0: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags :Word); 1: (AL,AH,BL,BH,CL,CH,DL,DH :Byte); End; Function ReadKey :Char; Procedure Sound (frq :Word); Procedure NoSound; Procedure Intr(IntNo:Integer; Var Regs:Registers); Implementation var scancd :word; oldfont,oldexit :pointer; (*** Redefinition von ReadKey ***} function readkey:char; assembler; asm tst.w scancd beg @rk1 move.w scancd,@result clr.w scancd bra @rkend @rk1: move.w #2,-(sp) move.w #2,-(sp) trap #13 addq.l #4,sp move.w d0,@result clr.w scancd tst.b d0 bne @rkend swap d0 move.w d0,scancd @rkend: end; { einfache Soundbefehle, nutzer Kanal A des } { Soundchips } {*** Sound abschalten ***} procedure nosound; assembler; asm move.w #135,-(sp) move.w #$ff,-(sp) move.w #28,-(sp) trap #14 addq.l #6,sp end; {*** Ton erzeugen, max. Lautstärke ***} procedure sound(frq :word); var tc :integer; begin if (frq>30) and (frq<20000) then begin tc:=round(125000/frq); asm move.w tc,d5 clr.w d0 move.b d5,d0 move.w #128,-(sp) move.w d0,-(sp) move.w #28,-(sp) trap #14 addq.l #6,sp lsr.w #8,d5 clr.w d0 move.b d5,d0 move.w #129,-(sp) move.w d0,-(sp) move.w #28,-(sp) trap #14 addq.l #6,sp move.w #136,-(sp) move.w #$0f,-(sp) move.w #28,-(sp) trap #14 addq.l #6,sp move.w #135,-(sp) move.w #$fe,-(sp) move.w #28,-(sp) trap #14 addq.l #6,sp end; end; end; { Einige oft genutzte DOS-Interrupts emulieren } procedure intr(intno :integer;var regs:registers); var temp :integer; begin if intno=5 then asm { Bildschirm-Hardcopy auslösen } pea @sdstrt move.w #38,-(sp) trap #14 addq.l #2,sp bra @sdend @sdstrt: clr.w $4ee rts @sdend: end else with regs do begin if (intno=$10) and (ah=1) then { Cursor konf. } begin if ch= $20 then asm { Cursor aus ) move.w #0,-(sp) move.w #0,-(sp) move.w #21,-(sp) trap #14 addq.l #6,sp end; if ch<>$20 then asm { Cursor ein } move.w #0,-(sp) move.w #1,—(sp) move.w #21,-(sp) trap #14 addq.l #6,sp end; end; if (intno=$16) and (ah=2) then begin { Umschaltst. Tastatur ermitteln } asm move.w #-1,-(sp) move.w #11,-(sp) trap #13 addq.l #4, sp move.w d0,temp end; al :=temp; end; end; end; { IBM-Zeichensatz zur Verfügung stellen } {*** Zeichensatzdaten ***} procedure ibmfont; external; {$L a:\ibmfont.o) {*** Zeichensatz für TOS installieren ***} function instfont(newfont :pointer):pointer; assembler; asm dc.w $a000 move.l -$16(a0),@result move.l newfont,-$16(a0) end; { nach Programmende alten Zeichensatz inst. } procedure tpexit; begin oldfont:=instfont(oldfont); exitproc:=oldexit; end; { diverse Initialisierungen } begin oldexit:=exitprcc; exitproc:=@tpexit; oldfont:=instfont(@ibmfont); scancd:=0; end.

Wolfgang Schneider
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]