Multitask - Mehrere Maschinenprogramme gleichzeitig ablaufen lassen Welcher C64-Programmierer würde es sich nicht wünschen, mehrere Maschinenprogramme gleichzeitig ablaufen lassen zu können? Mit dem hier vorgestellten Tool »Multitask« wird das zum Kinderspiel. Bis zu 31 Assemblerroutinen laufen mit nur leicht verringerter Geschwindigkeit quasi gleichzeitig ab, auf Wunsch kann dabei sogar noch in Basic weiterprogrammiert werden. von Nikolaus M. Heusler Die Routine wird mit LOAD "MT 49152",8,8 geladen. Danach sollten Sie NEW eingeben, um alle Zeiger richtigzustellen. Andernfalls könnte die Fehlermeldung ?OUT OF MEMORY ERROR erscheinen. Als nächstes lädt man die Routinen, die gleichzeitig ablaufen sollen, in den Speicher. Mit dem Befehl SYS 49152, ad1, ad2, ad3, ad4, ... werden nun beliebig viele solcher Maschinenprogramme (Minimum 1, Maximum 31) fast gleichzeitig gestartet. Sofern sie sich nicht gegenseitig stören (siehe unten), laufen sie quasi parallel ab. Die Startadressen der Routinen werden nach dem Komma übergeben: ad1, ad2, ad3 und so weiter. Geben Sie dabei einfach die Adressen an, die sonst auch hinter SYS stehen würden, um die Routinen einzeln zu aktivieren. So würde etwa der Befehl SYS 49152, 20000, 32000 zwei Maschinenprogramme ab 20000 und 32000 gleichzeitig starten. Wenn in dieser Parameterliste noch das Kaufmannsund (&) steht, kehrt der SYS 49152 nach dem Start der Maschinenprogramme nach Basic zurück. Jetzt kann programmiert werden, während die Assemblerroutinen im Hintergrund laufen (besonders praktisch für Tools und Programmierhilfen oder ähnliches)! Im obigen Fall also etwa: SYS 49152, 20000, 30000, & Die Anwendung des & kann beliebig erfolgen, also auch zum Beispiel SYS 49152, &, 20000, 30000 oder SYS 49152, 20000, &, 30000 Anstelle der Zahlen können natürlich auch Variablen oder Rechenausdrücke für die Startadressen verwendet werden. Allerdings darf keine der Maschinenroutinen, die gestartet werden sollen, an Adresse 0 beginnen. SYS 49152,...,0,... ist also verboten. Die Routine fängt Fehleingaben ab, es erscheint ein ?ILLEGAL QUANTITY ERROR. Hier ein praktisches Beispiel für die Anwendung: 30000 INC 53280 30003 JMP 30000 Startet man dieses Programm mit SYS 30000, flimmert der Bildschirmrahmen in einer Endlosschleife. Soll dieser Effekt auftreten, während Basic läuft, wenden Sie einfach Multitask an: SYS 49152,30000,& Nun könnte gleichzeitig dazu noch ein Sprite horizontal bewegt werden, solange der Feuerknopf eines Joysticks in Port 2 gedrückt wird: 35000 LDA #101 35002 STA 53269 35005 STA 53248 35008 STA 53249 35011 LDA #1 35013 STA 2040 35016 LDA 56320 35019 AND #16 35021 BNE 35016 35023 INC 53248 35026 JMP 35016 Im Bereich von 35000 bis 35015 wird das Sprite sichtbar auf den Monitor gebracht. Ab 35016 beginnt die Endlosschleife, die für die Bewegung bei zuständig ist. SYS 49152,35000,30000,& läßt diese Routine ablaufen - gleichzeitig mit Basic und dem oben gezeigten Flimmern! Bei den Maschinenprogrammen, die von Multitask ausgeführt werden, handelt es sich also meistens um Endlosschleifen. Dennoch ist eine Funktion eingebaut, die auch Ausstiege mit RTS verkraftet. Näheres dazu weiter unten. Einige Anregungen für Routinen, die Sie miteinander und zusammen mit Basic aktivieren könnten: Tastenklick, eingeblendete Echtzeituhr, Escape-Funktion (Löschen des Quote-Modus bei besonderer Tastenkombination), verbesserter Editor, Reset auf Tastendruck, ein Sprite, das »von alleine« bewegt wird, während ein Basic-Spiel läuft, und vieles mehr. Als Beispiel für die Anwendung mag auch das Demo dienen. Es wird mit LOAD "MT.DEMO",8 geladen und mit RUN gestartet. Das Basicprogramm lädt nun noch das steuernde Maschinenprogramm, die Unterroutinen und einige Spritedaten nach. Sodann erscheint die Frage, ob Basic weiter ablaufen soll. Drücken Sie die Taste <1> oder <2>. Es kann nun einige Sekunden dauern, bis wirklich alle Maschinenroutinen ablaufen, dann erscheint ein galoppierendes animiertes buntes Pferd auf dem Schirm. Es wird von einigen Maschinenroutinen ab 10000 gesteuert: 10000 horizontale Bewegung 10050 vertikale Bewegung 10100 Animation 10150 Pferd sichtbar machen 10200 (interne Warteschleife) 10250 Farbänderung Bei allen Routinen (bis aus 10150 und 10200) handelt es sich um Endlosschleifen wie oben erklärt. Jede dieser Routinen kann also mit SYS gestartet werden. Geben Sie SYS 10150 ein, wird nur das Pferd eingeschaltet. Starten Sie jetzt die vertikale Bewegung mit SYS 10050, fährt das Pferd einfach nur nach oben und unten, ohne sonstige Effekte. Das im Demoprogramm erzeugte Sammelsurium wird demnach zum Beispiel mit SYS 49152, 10150,10000,10050,10100,10250,& gestartet. Das Zeichen & am Ende bewirkt den Basic-Rücksprung. Im Prinzip können mit dieser Routine somit beliebige Assemblerroutinen, die an sich Endlosschleifen darstellen, miteinander ablaufen. Sie müssen allerdings aus technischen Gründen einigen Bedingungen entsprechen: Zum einen dürfen keine IRQ-Manipulationen wie SEI oder Verbiegen des IRQ-Vektors vorgenommen werden. Andernfalls kann es passieren, daß etwa der Task, der den Interrupt sperrt, nicht mehr verlassen und als einziger ausgeführt wird. CLI dagegen ist erlaubt. Ebenso ist die Veränderung des Prozessorports (Adresse 1) zu vermeiden. Auch sind natürlich Komplikationen zu erwarten, wenn mehrere Maschinenprogramme die gleichen Speicheradressen verwenden oder gar im selben Bereich arbeiten. Das Utility selbst belegt auch bestimmte Speicherstellen, und zwar den Bereich 49152-49546, $E000-$FFFF unter dem RAM und die Zeropageadressen 2 und 3. Außerdem sollten Operationen mit Peripheriegeräten (Floppy, Drucker, Band) vermieden werden, während das Multitasking aktiv ist. Mit SYS 49155 wird der Multitask-Betrieb wieder abgeschaltet. Unter Umständen ist es günstig, die IRQ-Geschwindigkeit und damit die Tast-Wechselgeschwindigkeit zu verändern. Dazu dient der Befehl SYS 49158,X X ist ein Wert zwischen 1 und 255, der Wert 0 wirkt wie 256. Je größer X gewählt wird, desto langsamer wechselt der C 64 die Tasks. Lassen Sie den Parameter X weg, wird der Standardwert 60 eingesetzt: SYS 49158 Bei diesem Utility ist es interessant, zu erfahren, wie ungefähr es intern funktioniert. Genauere Information kann der interessierte Leser dem kommentierten Quelltext entnehmen. Grundlage für das Multitasking ist der Systeminterrupt (IRQ). Bei jedem IRQ unterbricht der C 64 das momentan laufende Maschinenprogramm, um eine Interruptroutine abzuarbeiten. In diesem Fall handelt es sich um eine neue Routine. Diese holt sich zunächst die Adresse vom Stack, an der das aufrufende Programm unterbrochen wurde, und speichert diese. Einer Tabelle wird die Startadresse des ersten zu startenden Programmes entnommen und auf den Stack geschrieben. Wenn der Computer jetzt die IRQ-Routine verläßt, findet er die neue Adresse auf dem Stack und macht mit dem nächsten Programm weiter - bis zum nächsten IRQ. Auf diese Weise kommen alle gewählten Routinen nacheinander je einmal dran. Danach wird von vorn begonnen. Da die IRQs sehr häufig (normal 60 mal in der Sekunde) auftreten, wird der Eindruck erweckt, die Programme laufen gleichzeitig ab. Neben der Rettung der Startadressen ist es erforderlich, die drei Prozessorvariablen A, X und Y sowie den Stackpointer, den Stack (Adresse 256 bis 511) und das Statusregister zu speichern. Dazu dient ein reservierter Speicherbereich ab $E000 unter dem Kernal-ROM gemäß untenstehender Speicherbelegung. Dadurch bleibt in den Tasks neben der Möglichkeit, ganz normal alle Prozessor-Register zu benutzen, sogar der Stack voll und ganz erhalten. Befehle wie PLA und PHA dürfen wie gewohnt ausgeführt werden (siehe dazu die untenstehende Bemerkung zum Stack). Am Ende der IRQ-Routine wird allerdings noch die normale System-IRQ-Routine ausgeführt, damit Effekte wie das Cursorblinken und der Editor weiterhin erhalten bleiben. Ein Problem stellt sich noch, wenn eines der Maschinenprogramme keine Endlosschleife ist, sondern mit RTS endet. Da es dann keine sinnvolle Rücksprungadresse auf dem Stack finden würde, wäre ein Absturz unvermeidbar. Daher erzeugt das Utility beim ersten Aufruf jeder Routine eine künstliche Rücksprungadresse, die auf eine Endlosschleife zeigt, und schreibt sie auf den Stack. Endet ein Task mit RTS, wird als nächstes diese Endlosschleife ausgeführt. Der Task ist also abgeschlossen, stört aber nicht die Ausführung der übrigen Programme. Ähnlich funktioniert die Auswahlmöglichkeit, ob das Basic weiter ablaufen soll. Wählen Sie diese Option, kehrt der Befehl SYS 49152 nach der Ausführung einfach mit RTS in den Basic-Editor zurück. Dieser ist jetzt einer der auszuführenden Tasks. Soll kein Basic ablaufen, endet SYS 49152 in einer Endlosschleife. Vor dem ersten Aufruf jedes Tasks wird außerdem der Stackpointer auf 247 gesetzt, damit das Maschinenprogramm den vollen Stack zur Verfügung hat. Für den Programmierer ist dies jedoch nicht von Bedeutung. Wir wünschen Ihnen viel Spaß mit diesem neuartigen Multitask-Gefühl. Was Sie alles damit machen können, liegt voll und ganz bei Ihnen und Ihrer Phantasie. Genießen Sie die umfangreichen Möglichkeiten dieser neuen Technik! (Nikolaus M. Heusler) Speicherbelegung (hexadezimal): 0002-0003 temporär: Zeiger auf Startadressen-Tabelle c000-c18a Maschinenprogramm c000-c008 Sprungtabelle c009 Init c01b Parameterschleife c086 Endlosschleife, falls Basic ausgeschaltet c08f Ende Multitasking c095 IRQ-Speed setzen c0a4 Endlosschleife für Task-Ende mit RTS c0a7 neue IRQ-Routine c0b0 nächsten Task suchen c0c2 neue Adresse merken c0cd alte Adresse speichern c0df neue Register merken c0fd alte Register speichern c119 Stacks vertauschen c131 ab hier einige Selbstmodifikationen c139 Zeiger auf nächsten Task c147 Stackpointer vorbereiten c14f Endlosschleife bei RTS vorbereiten c15c neue Adresse setzen c166 neue Register setzen c181 neue Hardware-NMI-Routine c188 Flag: Basic eingeschaltet c189 Anzahl Tasks c18a Flag: Task bereits einmal aufgerufen e000-e03f Startadressen-Tabelle e040-e05f Tabelle: A-Register der Tasks e060-e07f Tabelle: X-Register der Tasks e080-e09f Tabelle: Y-Register der Tasks e0a0-e0bf Tabelle: Status-Register der Tasks e0c0-e0df Tabelle: Stackpointer der Tasks e100-feff Stackinhalte der Tasks