QUICK Ecke 21 von Harald Schönfeld Inzwischen haben wir ja schon gesehen, wie man die meisten BAISC-Befehle in QUICK-Befehle übersetzt. Aber ein paar sind doch noch nicht erwähnt worden. ON X GOSUB Z1,Z2,..,ZN. Mit Hilfe dieser beiden Befehle ist es im BASIC leicht möglich verschiedene Unterprogramme aufzufrufen, je nach dem Wert der Varaiblen X. X muß dabei den Wert 1 bis N haben. Dieser unscheinbare Befehl ersetzt somit sozusagen N IF-Statements. In QUICK gibt es soetwas leider nicht. Im QUICKmagazin 7 wurde jedoch bereits ein etwas fortgeschrittenerer Weg gezeigt, wie man in QUICK-Programme doch etwas ähnliches einbauen kann. Da dieses Thema für die Umsetzung von BAISC-Programmen nach QUICK recht wichtig ist, möchte ich hier nochmal darauf eingehen. ON X GOSUB in QUICK Will man in QUICK je nach dem Wert einer Variablen X eine unterschiedliche Funktion aufrufen, so sieht der dazu nötige Programmteil normalerweise etwa so aus: IF X=0 .P0 ELSE IF X=1 .P1 ELSE ... IF X=N .PN ... ENDIF Das ist natürlich nicht gerade übersichtlich und elegant. Vor allem aber ist dieser Block relativ speicherfressend und es besteht die Gefahr, daß der interne Stack des Compilers bei zu vielen IF-Statements überläuft. Ganz zu schweigen davon, daß man mehr als 10 IFs wohl kaum korrekt eingerückt im Editor eingeben kann... Wesentlich schöner wäre es, wenn man in einer Schleife die N Werte von X überprüfen könnte und dann das nötige Unterprogramm aufrufen könnte. Nun, mit einem Trick ist das möglich. INLINE-Assembler in QUICK Keine Sorge, ich werde jetzt nicht anfangen in einem Text, der sich an "NOCH"-BASIC-Programmierer richtet lange Assemblerprogramme zu bringen. Wir brauchen für unsere Anwendung nur 3 Byte Maschinencode, die gerade einen einzigen Befehl enthalten. Ein "GOSUB"-Befehl in QUICK ist ja sozusagen der Aufruf eines Unterprogramms der Art: .P1 Was bedeutet dieser Befehl im fertig compilierten QUICK-Programm? Der Compiler setzt hier einfach den Maschinencode 32 für einen JSR-Befehl, gefolgt von der 2 Byte langen Sprungadresse ein. Die Sprungadresse ist natürlich gerade die Anfangsadresse des Unterprogramms P1. In Assembler sieht das also etwa so aus: JSR P1 wobei P1 die Anfangsadresse des Unterprogramms P1 ist. Wir könnten also auch in QUICK das Unterprogramm P1 mit dem folgenden INLINE-Assemblerblock aufrufen: INLINE (+ 32,P1L,P1H )+ 32 ist der Maschinencode für JSR, P1L das Lowbyte der Anfangsadresse des Unterprogramms und P1H das Highbyte. Nun bleibt noch die Frage, woher P1L und P1H zu bekommen sind. Dabei hilft uns der PADR()-Befehl, der die Adresse eines Unterprogramms ermittelt. PADR(P1) schreibt die Adresse von P1 in die Speicherzelle 208/209. Legen wir mit WORD (+ ADR=208 )+ eine Variable an diese Stelle im Speicher, so ethält ADR nach dem PADR-Aufruf also die Adresse von P1. Nun müssen wir dafür sorgen, daß diese Adresse im INLINE-Befehl zum tragen kommt. Dazu müssen wir während der Laufzeit das Programm selbst ändern. D.h. wir poken den Wert von ADR an die Stelle, wo im INLINE-Befehl die Sprungadresse steht! Dazu brauchen wir natürlich selbst wieder die Adresse des INLINE-Befehls, weshalb er in einem Unterprogramm abgelegt wird. Addiert man 1 zu der Anfangsadresse dieses Unterprogramms, so ist man gerade an der Stelle, an der die Adresse steht. Dorthin poked man den Wert von ADR. Wird nun dieser INLINE-JSR aufgerufen, so wird das Unteprogramm P1 ausgeführt. Das ganze klingt wohl ein wenig kompliziert, aber wenn man sich das zugehörige Programm ansieht, wird man feststellen, daß es gar nicht so schlimm ist. Nun bliebt noch die Frage: Wozu das Ganze? Nun, jetzt es es natürlich möglich, sich die Adressen aller Unterprogramme, die man bei einem ON X GOSUB aufrufen möchte in einem Feld zu merken. Dann kann man in einer Schleife X abfragen und je nach dessen Wert eine Adresse aus dieser Tabelle auswählen. Diese poked man dann in den INLINE-Block, und kann so ein bestimmtes Unterprogramm gezielt aufrufen. Die folgenden Programmsegmente zeigen noch einmal die einzelnen Schritte: QUICK-Sourcetext ... ARRAY (+ ADRS(20) Platz für 10 WORD-Adressen )+ MAIN .INITADR ... GOSUB(X) X darf 0 bis 9 sein ENDMAIN PROC GOSUB führt das GOSUB aus IN BYTE (+ X )+ LOCAL WORD (+ JSRADR=208,XAD )+ BEGIN PADR(JSR) schreibt Adresse des Unterprogramms JSR in JSRADR ADD(JSRADR,1,JSRADR) addiert 1 dazu MULT(X,2,X) Index verdoppeln da Adresse aus einem WORD-Feld zu lesen ist XAD=ADRS(X) Sprungadresse lesen DPOKE(JSRADR,XAD) Sprungadresse an die richtige Stelle im INLINE-Block kopieren (2 Byte) .JSR Unterprogramm aufrufen ENDPROC PROC JSR INLINE-Block der JSR ersetzt BEGIN INLINE (+ 32,0,0 0,0 wird durch den obigen )+ DPOKE-Befehl überschrieben von der korrekten Sprungadresse PROC INITARD kopiert die Anfangs- adressen der Unterprogramme P1 is P10 in das Adressen- array LOCAL WORD (+ ADR=208 )+ BEGIN PADR(P1) ADRS(0)=ADR PADR(P2) ADRS(2)=ADR PADR(P3) ADRS(4)=ADR PADR(P4) ADRS(6)=ADR PADR(P5) ADRS(8)=ADR ... ADRS(18)=ADR ENDPROC Es bleibt nun jedem selbst überlassen, ob er lieber IF-Kaskaden benutzt um ON-GOSUB in QUICK zu ersetzen, oder diesen hier beschriebenen Weg, der sich sicher erst rentiert wenn man mehr als 5 Programme aufrufen will, oder wenn man mehrere verschiedene ON-GOSUBs in einem Programm vorliegen hat. Übrigens läßt sich ON-GOTO auf diese Art praktisch nicht in QUICK einbinden.