Es gibt Programme, die das TOS-Datum oder die TOS-Versionsnummer abfragen. Da möchte der Programmierer z.B. Funktionen nutzen, die erst von neueren Versionen des Betriebssystems zur Verfügung gestellt werden. Soll das Programm auch auf älteren, vielleicht noch weit verbreiteten OS-Versionen lauffähig sein, muß entsprechend verzweigt werden, um die nicht vorhandenen Features zu umgehen oder vielleicht sogar nachzubilden.
Jedenfalls sollte das Programm nicht etwa nur böse bombend abstürzen: Guter Stil erfordert zumindest eine Meldung der Art Prg. XYZ läuft erst ab OS-Version ABC!. Diese Art der TOS-Versionsabfrage ist völlig korrekt und entspricht guter Programmierpraxis.
Es gibt aber noch eine ganz andere Art der Versionsabfrage: Da hat ein pfiffiger Programmierer eine OS-interne Variable entdeckt, über die sich Manipulationen vornehmen lassen, die seinem Programm ungeahnte Möglichkeiten eröffnen. Der einzige Nachteil ist, daß sich die Lage dieser Variablen von OS-Version zu OS-Version verändert. Was tun? Der 'brave’ Programmierer stöhnt: „schade!“ und läßt die Finger davon. Vielleicht beginnt er auch eine Korrespondenz mit den Systemprogrammierern seines Rechners und versucht, sie davon zu überzeugen, wie gut es wäre, die bewußte Variable zu dokumentieren und damit zur Benutzung freizugeben.
Doch magisch lockt die Macht des Bösen, und das Fleisch ist schwach. So wird denn schließlich eine Tabelle installiert, in der die Lage der besagten Variablen für alle relevanten, bis dato bekannten OS-Versionen festgelegt ist. Die Höflichkeitsmeldung - sofern implementiert müßte hier lauten: Prg. XYZ läuft nur auf OS-Version(en) ABC! Bitte Update ordern! [1]
Nach dieser Einführung hoffentlich neugierig gemacht und entsprechend motiviert, lassen Sie uns zur Lösung eines bekannten praktischen Problems nun gemeinsam in die (Un-)Tiefen des TOS, seiner Dokumentation und speziell seiner internen Speicherverwaltung durch das GEMDOS hinabsteigen.
Eine der am heißesten begehrten internen TOS-Variablen ist der sogenannte MPB (Memory Parameter Block): Er stellt nämlich den Schlüssel für die Speicherverwaltung des Betriebssystems (hier GEMDOS) dar und besteht aus drei Zeigern, angeordnet in folgender Struktur:
MPB:
dc.l mpmfl ;Zeiger auf Beginn der 'mfl' (memory free list)
dc.l mp_mal ;Zeiger auf Beginn der 'mal' (memory allocated list)
dc. mp_rover ;nicht weiter definierter Zeiger (roving ptr)
Der roving ptr, ein intern verwendeter Zeiger auf den zuletzt bearbeiteten (oder den demnächst zu bearbeitenden?) Block, ist allerdings in seiner Funktion nicht weiter offiziell beschrieben. Bei meiner Konfiguration (TOS 1.4) zeigt er immer auf die mfl; wenn es keine mfl gibt (mp_mfl = 0), ist er auch Null. Man sollte sich allerdings hüten, aus solchen Beobachtungen irgendwelche Annahmen für die Zukunft abzuleiten, und siehe da, im TT-TOS 3.01 wird er anscheinend schon nicht mehr benutzt...
In den beiden Listen, der mfl und der mal, befinden sich die sog. MDs (Memory Descriptor), sie haben folgende Struktur:
MD:
dc.l m_link ;Zeiger auf nächsten MD (0 = Ende der Liste)
dc.l m_start ;Zeiger auf Beginn des Blocks
dc.l m_length ;Länge des Blocks
dc.l m_own ;Zeiger auf Besitzer des Blocks (die Basepage)
Beide Listen bestehen also aus diesen MDs, die mittels der m_link-Zeiger einfach miteinander verkettet sind, jeder allozierte (mit bestimmten Ausnahmen, s.u.) und jeder freie Block des Anwenderspeichers ist hier eingetragen. Der m_own-Zeiger ist allerdings bei den in der mfl befindlichen MDs bedeutungslos, wenn nicht schon Null. Diese Strukturen sind - äußerst sparsam - offiziell dokumentiert in [2], eine ausführliche Diskussion (inoffiziell) gibt ’s in den grundlegenden Beiträgen von Alex Esser [3]. Dabei sollte man aber bedenken, daß sich die nicht offiziell dokumentierten internen Strukturen des Betriebssystems des öfteren geändert haben und auch weiterhin ändern werden, ja, daß sie gerade deshalb nicht dokumentiert werden, um die Möglichkeit ihrer Änderung - Optimierung oder Erweiterung - offenzuhalten.
Wenn man also über die Kenntnis des leider nur strukturmäßig wohldokumentierten Ankers, eben des MPB, Zugriff auf diese ebenfalls in ihrer Existenz und Struktur dokumentierten einfach verketteten Listen hätte, könnte man die Speicherverwaltung des GEMDOS allerliebst manipulieren. Dazu eröffnen sich folgende Perspektiven:
Atari dokumentiert endlich die Adresse des MPB als Systemvariable und gibt damit den Zugriff auf die schon längst publizierten MDs frei. Dafür sollte man Allan Pratt und seinem Team von Systemprogrammierem den ‘Goldenen Jack am Band’ verleihen! Gewisse ‘trickreiche’ Programme könnten dann mit Tabellen für Abfragen erster Art versehen werden und endlich in den Bereich der so lang ersehnten programmiertechnischen Legalität überwechseln.
Es gibt da die allgemein bekannte, dokumentierte [2] und vom Anwender tunlichst nicht aufzurufendc BIOS-Funktion Nr.0 getmpb, die vom GEMDOS während des Systemstarts dazu benutzt wird, den MPB auszufüllen und dabei gleichzeitig einen MD, nämlich den MD, das ist die Systemvariable themd $48E, zu initialisieren [2, 4].
Da wir wissen, daß das GEMDOS nur einmal die Funktion BIOS (0) getmpb aufruft, müssen wir uns zu einem Zeitpunkt in der Systeminitialisierung, zu dem der BIOS-Trap schon eingerichtet ist, aber vor der Initialisierung des GEMDOS, in den Trap hängen und warten, bis getmpb aufgerufen wird. Dabei wird p_mpb als Zeiger auf den MPB auf dem Stack übergeben, wir brauchen ihn uns nur zu merken und können ihn in einer dokumentierten und deshalb für andere Programme zugänglichen Form ablegen, z.B. als cookie im cookie jar, um gleich einmal diese mit TOS 1.6 eingeführte, aber auch bei früheren TOS-Versionen nachrüstbare Informationsstruktur zu nützen.
Leider stellt sich bei Untersuchung der im schon zitierten HHG [2] sehr ausführlich dokumentierten Startup-Sequenz heraus, das nur eine ROM-Port-Cartridge dort hineinkommen kann, und zwar in unserem Falle am besten eine Anwendung vom Typ L direkt vor der Initialisierung des GEMDOS. Nun haben aber Cartridges, auch ROM-Module genannt, einen Nachteil: Sie sind ziemlich hart und lassen sich weder auf Diskette ziehen noch über ein elektronisches Medium schicken, was ihrer Verbreitung natürlich im Wege steht - zumal dies ja eine der wenigen echten ROM-Modul-Anwendungen wäre. Na, zumindest den Code kann man ja veröffentlichen. Leider ist diese harte Tour bis dato der einzige Weg, dem MPB auf völlig legale - und damit 100%ig und für alle Zeiten (...solange das TOS noch von Belang ist und Atari sich an seine eigene Dokumentation hält...) sichere - Weise beizukommen.
Der Anker der MD-Listen ist also der MPB. Nur, was macht man, wenn man seine Adresse nicht kennt? Das einzige, worauf man seine Hand legen kann, ist themd ($48E), eine ‘garantierte’ Systemvariable zwar, aber leider nicht sehr von Nutzen. Das war und ist der bisherige Stand der (recht kärglichen) Dokumentationen und die generelle Meinung der Fachautoren. Das soll sich nun ändern, denn genau hier setze ich den Hebel an!
themd ist gleich nach der GEMDOS-Initialisierung ein (der einzige) Eintrag der mfl und weist damit den gesamten Anwenderspeicher - auch TPA (Transient Program Area; genannt - von membot bis memtop als frei aus, so ist’s dokumentiert in [2]. Die mal ist zu diesem Zeitpunkt leer (mp_mal=0), mp_mfl (und bis TOS 1.6 auch mp_rover) zeigt auf themd. Dies ändert sich, sobald die erste Speicherreservierung (z.B. zum Starten eines Prozesses) vorgenommen wird. Nun ist themd der letzte MD der mal: m link=0, m_own = Urprozeß (seit TOS 1.4, früher Null), m_start = membot, m_length enthält die Größe des ersten reservierten Blocks. Die Möglichkeit zu einer solchen ‘öffentlichen’ Reservierung - mit Malloc() - besteht bei ausführbaren Boot-Sektoren (Floppy- und DMA-Boot) und Päckchen, in dieser Reihenfolge. Danach ist dann wieder das ROM dran, und hier geschehen auf jeden Fall die ersten Reservierungen für Environment-String und Basepage, die für den sog. AUTOEXEC-Prozeß eingerichtet werden (nicht dokumentiert), der dann, falls vorhanden, die AUTO-Ordner-Programme und anschließend das AES (oder das COMMAND.PRG) startet (das ist wieder dokumentiert!).
Äußerst kritisch ist hier, daß noch kein Speicher reserviert worden sein darf, im Falle des Päckchens könnte z.B. ein Harddisk-Treiber aus dem Boot-Sektor von Laufwerk C: dazwischen gekommen sein, so daß eine gewisse Sicherheit für diese Methode nur dann besteht, wenn das Programm aus dem Boot-Sektor von Laufwerk A: startet und damit garantiert als allererstes ausgeführt wird. Die Unberührtheit des Speichers läßt sich recht einfach nachprüfen. Um dem Problem mit der Benutzung des mp_rover aus dem Wege zu gehen, empfiehlt sich hier die Suche von unten nach oben. Die verbleibende Unsicherheit resultiert aus der nicht ganz dokumentenechten Annahme, daß es im durchsuchten Bereich nur einen solchen Zeiger gibt, und daß das dann der gesuchte mp_mfl ist (ich wüßte allerdings nicht, was das GEMDOS mit mehr als einem solchen Zeiger auf die mfl anfangen sollte!).
Hier gibt es zwei kritische Punkte, den einen allerdings erst seit TOS 1.4. Die von residenten Programmen belegten MDs werden nämlich wie schon zuvor üblich aus der mal ausgehängt und sind damit dem GEMDOS unbekannt. [6] Leider werden die von ihnen belegten ‘Slots’ aber nicht freigegeben, d.h. der Inhalt gelöscht oder zumindest ihr m_own ungültig gemacht. Bei z.B. 10 residenten AUTO-Ordner-Programmen sind dann 20 MD-Slots völlig unnötigerweise belegt (je 10 für die Environment-Strings, 10 für die residenten Programmteile inklusive Basepage), ein weiterer Bug im neuen GEMDOS-Poolmanager (POOLFIX3 hilft da leider auch nicht weiter)! Unser Suchprogramm könnte somit auf die Adressen zwar formal gültiger, aber für uns wertloser MDs stoßen, die als ‘Leichen’ im GEMDOS-Pool liegen. Ein Test auf die Gültigkeit von m_own (muß auf gültige Basepage zeigen [7]) nützt in diesem Falle auch nichts. Der Referenzierungsalgorithmus, der die verkettete Liste von hinten aufrollen soll, muß also durch einen entsprechenden Dereferenzierungsalgorithmus ergänzt werden, damit sich die Routine sozusagen wieder rückwärts aus der Sackgasse - Stufe für Stufe - heraushangeln kann, um dann jeweils neue Versuche nach vom zu starten. Damit das Programm bei etwaigem Versagen nicht hängenbleibt, sollte die maximal mögliche Anzahl der ‘Backtracking’-Versuche auf einen vernünftigen Wert begrenzt werden. Das alles hört sich zwar furchtbar kompliziert an, der Code ist jedoch recht kurz und klar!
Der zweite kritische Punkt ist die Abbruchbedingung, nämlich m_own=0. So etwas darf es allerdings in der mal nicht geben: einen herrenlosen Block. Dann hätten wir also hoffentlich den mp mal selbst erwischt. Das wäre dann aber kein Element eines MD mehr, und die Tatsache, daß an diesem Offset eine Null steht, haben wir nur dem glücklichen Umstand zu verdanken, daß der MPB (zumindest zur Zeit des AUTO-Ordners und meist auch des Desktops) allein auf weiter Flur in einem sonst leeren Datenbereich steht. Dies ist natürlich logisch überhaupt nicht zwingend. Und das muß auch nicht so bleiben, selbst wenn es schon seit Uralt-Rauchpilz-TOS-Zeiten so war und auch im STE-TOS 1.6 sowie im TT-TOS 3.01 noch immer so ist. Die Methode funktioniert prächtig, wie der geneigte Leser sicher schon erraten hat, und wird es wohl auch weiterhin, doch kann man dies weder 100%ig noch für alle Zeiten garantieren.
Die erste Methode bietet mehr Sicherheit, eignet sich aber nicht für Programme, die, wie das nun vorgestellte MAL_FIND oder GET_MPB, lediglich eine darstellende Funktion haben. Um nicht umständlich mit Boot-Sektor-Installationen herumbasteln zu müssen, könnte man auch ein Päckchen installieren (ein Bonus für Programme, die sowieso auf einem resetfesten Päckchen laufen sollen!), evtl. gleich einen Warmstart veranstalten und aus dem Päckchen heraus den MPB ermitteln. Da man hier immer damit rechnen muß, daß ein selbstbootender Harddisk-Treiber von Laufwerk C: dazwischenfunkt, empfiehlt sich eine Kombination der beiden Methoden, wie es in dem als Link-Modul ausgelegten _GET_MPB implementiert wurde.
Gesucht wird jeweils von membot nach unten bis $2000 (willkürlich, auch bei Uralt- TOS-Versionen findet sich nichts darunter). Hierbei wird vorausgesetzt, daß die Liste komplett im ursprünglichen OS-Pool liegt und dieser nicht etwa schon erweitert wurde (z.B. durch FOLDRXXX.PRG). Ferner ist denkbar, daß Pool-Manipulationen residenter Programme, z.B. Monitore, den Suchalgorithmus blockieren könnten, die Reihenfolge der Programme wäre hier zu beachten: Je früher der Start, desto höher die Erfolgswahrscheinlichkeit! Die Suchrichtung von oben nach unten ist zwar um einiges langsamer, hat sich aber als stabiler erwiesen. Das Problem der ‘Sackgassen durch Geister-MDs’ wird zwar in beiden Suchrichtungen von der Routine gleich gut bewältigt, auch wenn man ihnen bei der Suche von unten nach oben natürlich öfter begegnet, zuweilen tauchen jedoch kurzfristig (!) ‘verstümmelte’ MDs auf, die mit ausgenulltem m_own vorzeitig die Abbruchbedingung erfüllen. Diese Gefahr hat sich bei der Suche von oben nach unten nicht ergeben.
Wie man sieht, bewegen wir uns hier auf ziemlich dünnem Eis. Sinnvolle Plausibilitätstests, in der richtigen Kombination, sind demnach das A und O solcher Routinen, da ließe sich evtl. noch einiges verfeinern. Das war auch mit der schwierigste Teil beim Entwurf der Routine, denn hier spiegeln sich ja alle Annahmen, die man so macht. Der Test auf 24-Bit-Adressen mußte dem TT zuliebe z.B. wieder gestrichen werden, ebenso die Annahmen über das Verhalten des mp_rover. Andere Tests erwiesen sich als nicht stabil: der ‘Ad-hockery’ sind bei solchen Problemen ja Tür und Tor geöffnet, und man muß schon sehr aufpassen, was für Annahmen man macht und was sie implizieren. Man darf sich auch nicht von SpeicherabbiIdern, sog. Dumps, zu generalisierenden Annahmen verleiten lassen. Wenn ich vorhin sagte, daß der Bereich um den MPB so schön leer sei, so gilt das durchaus nicht immer. Beim Nachladen anderer Programme werden u.U. direkt anschließend Tabellen mit Deskriptoren angelegt. Das bedeutet, daß die Abbruchbedingung der Routine z.B. unter auf dem AES laufenden Debuggern oder bestimmten Shells (u.a. Gulam, Gemini 1.2) nicht erfüllt werden kann. So etwas ist aber nun gerade nicht die Umgebung, unter der die Routine nachher laufen soll, und ich habe mich daher gehütet, hier ad hoc Abhilfe zu schaffen!
Weitere Einzelheiten zu erklären, erspare ich mir, und lasse dafür lieber den Code sprechen: Er ist ja kurz genug und, wie ich hoffe, ausreichend kommentiert. Also, immer die Zeiger suchen oder den Zeigern folgen! Glücklicherweise läßt sich dieses ganze Zeigergewurstel in Assembler nicht nur einigermaßen elegant programmieren, sondern der Code ist auch entsprechend schnell in der Ausführung, so daß selbst bei RAM-TOS, wo membot ja ziemlich hoch liegt und sich der Algorithmus erst durch das ganze TOS.IMG wühlen muß, passable Ausführungszeiten erreicht werden.
MAL_FIND gibt die ganze mal entsprechend dem Algorithmus von hinten nach vom aus, wobei ungültige MDs mit <invalid! gekennzeichnet werden. Weitere solche Markierungen hintereinander bedeuten, daß entsprechend viele Zeilen darüber als ungültig zu betrachten sind, wie schon oben erklärt. Dann wird der MPB ausgegeben und anschließend die mfl[8] von vorn nach hinten, den m_link-Zeigern folgend. Falls der Suchalgorithmus bei Ausgabe der mal steckenbleiben sollte, gibt es eine entsprechende Fehlermeldung.
GET_MPB gibt bei Erfolg nur den MPB aus, sonst eine Fehlermeldung. Der Code entspricht MAL_FIND bis auf die MD-Listenausgabe.
GET_MPB ist ein Modul, das zur Einbindung in Hochsprachen gedacht ist. In D0 wird bei Erfolg die Adresse des MPB zurückgeliefert, sonst eine Null. Man bedenke allerdings, daß so ein Modul wirklich nur zur Vewendung in ganz speziellen (meist resetfesten) Utilities geeignet ist und nicht etwa für Standard TOS- oder gar AES-Applikationen. Dort gibt es schließlich genug bekannte und gut dokumentierte Methoden, sich Speicher zu besorgen.
CART_MPB und BOOT_MPB sind die Implementationen der Routine für Cartridge bzw. Boot-Sektor. Sie installieren ein Cookie namens MPB* im Cookie Jar, auf das andere Programme dann zu greifen können. Falls das Cookie Jar erst eingerichtet werden muß (<TOS 1.6), wird auch ein Resethandler installiert, der die Systemvariable _p_cookies $5A0 bei einem allfälligen Warmstart wieder löscht, wie von Atari in [0] vorgeschrieben. Speicher für das Cookie Jar besorgen sich beide Programme in einer Art, die ich ‘stille Reservierung’ nennen möchte - also ohne Benutzung von Malloc(). Das ist, wie man sieht, sehr einfach - wir sitzen ja schließlich an der Quelle! Im Falle der Cartridge ist es noch einfacher und zudem die einzig mögliche Methode, da ja noch keine GEMDOS-Aufrufe gemacht werden dürfen.
Spott erntet, wer sich darüber beklagt, daß sein wunderbares XYZ-Programm nicht auf der neuen OS-Version läuft. Neulich hatte ein (registrierter!) User endlich sein Update eines bekannten biegsamen Programms für TOS 1.4 erhalten und strahlte übers ganze Gesicht. Der STE stand auf dem Tisch. Ich sagte nur: ‘Nimm doch mal diesen hier... 4Unbekannte TOS-Version ’, das war’s! Betretenes Schweigen...
Bei aller Begeisterung für den wunderbar ‘tight & clean’ geschriebenen Code des Luftschlosses [10] aus Scheibenkleister II habe ich es wegen der dort enthaltenen Versionsabfragen zweiter Art nicht gustiert. RRN2500F wurde nach kurzem Test wieder gelöscht: Aus den Augen, aus dem Sinn! Doch nun her damit und neu ausgepackt! Das Stückchen Code schnell ersetzt, assembliert und gleich mal auf dem STE gestartet: läuft! Desgleichen auf diversen modernen RAM-TOS-Versionen, Blitter TOS, KAOS, RAM/ROM-TOS 1.0, Very OLD RAM-TOS 1.0 vom 20.11.85. Läuft nicht auf Rauchpilz-TOS, scheitert dort aber an act_pd (nicht in der Liste). Eine Abfrage erster Art wäre hier natürlich möglich, wird jedoch nicht eingearbeitet, da unwichtig!. Wer noch auf so einer alten Gurke hackt, muß wohl irgendwie von der Zivilisation abgeschnitten sein, vielleicht auf Expedition am Amazonas...
P.S. Nach nunmehr über einem Jahr stabilen Dauerbetriebs des mit der hier vorgestellten MPB Suchroutine ausgerüsteten ‘Luftschlosses’ auf meinem und einigen anderen Systemen unter wechselnden Konfigurationen, konnte es dank der Nachrüstung des stufen weisen Dereferenzierungsalgorithmus’ nun auch erfolgreich auf dem TT030 (32 MHz, TOS 3.01) getestet werden, so daß ich, von der relativen (Beachtung der Caveats) Robustheit des Algorithmus’ überzeugt, diesen einer größeren Öffentlichkeit nicht länger vorenthalten möchte. Eigentlich schön, daß Atari zur Zeit so aktiv ist: Die nächste Probe aufs Exempel wäre, ob’s auch auf TOS 2.05 des neuen Mega STE läuft.
Anmerkungen und Referenzen:
Leider sind diese Methoden selbst bei Leuten, die ansonsten etwas anderes ‘predigen’, noch immer nicht ganz aus der Mode gekommen. Hat’s doch subjektiv auch manches für sich: Das anhängige Problem ist gelöst, und wenn’s dann spätestens bei Erscheinen der nächsten OS-Version nicht mehr funktioniert, muß der Programmierer eben das Programm anpassen (Auftrag) und der Kunde halt zahlen (fürs Update). So steigert man das Bruttosozialprodukt!
Landon Dyer: A Hitchhiker’s Guide to the BIOS (HHG), 1985, Atari Corp
Unter ‘themd’ heißt es: „Filled in by the BIOS on a ‘getmpb’ call; indicates to GEMDOS the limits of the TPA. ...(you can’t use the m_link field in the first MD). Someday (with a better GEMDOS) these limitations may be lifted.“ ...und stellt somit die Freigabe des MPB in Aussicht.
Alex Esser: TOS intern, ST-Computer 1987, Sonderheft Nr.2, S.35 ff.:
Umfassende Analyse der GEMDOS-Speicherverwaltung, mit Programmen zur Erforschung der interessierenden Strukturen, basierend auf TOS 1.0. Siehe ebenfalls den 1. Teil des zweiteiligen Beitrags ‘Systemvariablen des TOS’, ST-Computer 11/88, sowie zu TOS 1.4 ‘Somewhere over the Rainbow’, 2 und 3, ST-Computer 5-6/90, vom gleichen Autor. In letzterem Beitrag übrigens auch die Beschreibung des oben genannten Pool-Bugs.
Allan Pratt. UUCP Message-ID: < 2737@atari.UUCP >, 12 Nov 90 20:03:26 GMT „...Getmpb is a BIOS call which is used by GEMDOS to find out the lay of the land at the beginning of the world: it is the BIOS’s way of telling GEMDOS what memory it found and where. It should not be used by user programs.
Zu diesem Zeitpunkt könnten wir übrigens auch, ohne den MPB zu ermitteln und damit dann die ‘mfl’ wie gehabt zu bearbeiten, völlig simpel gleich ‘themd’ manipulieren. Das ist es nämlich, was in Wirklichkeit geschieht, wenn man zu eben der Zeit über den MPB auf die ‘mfl’ zugreift: Sie besteht dann ja nur aus ‘themd’!
Allan Pratt, UUCP Message-ID: < 2756@atari.UUCP >, 30 Nov 90 02:03:15 GMT „...You can’t free memory which has stayed resident as a result of a TSR. It’s not even accounted for in the system as „allocated“ memory - it’s been UNHOOKED from the memory management lists. It’s GONE.“
B. Rosenlecher, ‘XBRA? XNAM?? BASEFIND!!!’, ST Computer 9/90, S.147 ff.
Bei meiner Standardkonfiguration (TOS 1.4 mit geladenem POOLFIX3 sowie weiteren fünf residenten und einem nicht residenten AUTO-Ordner-Programm und sechs Accessories) habe ich immer nur einen MD in der ‘ mfl ’ gesehen, auf den regelmäßig beide Zeiger mp_mfl und mp rover zeigten, bei anderen Konfigurationen auch schon mal 2 oder 3, und manchal sogar, daß der mp_rover nicht auf das gleiche Ziel zeigte wie der mp _mfl. Nur einen MD in der ‘mfl’ zu haben, bedeutet natürlich, daß der als frei ausgewiesene Speicher nur aus einem großen Block besteht und nicht etwa zersplittert ist, eine wirklich erfreuliche Tatsache also!
Atari Corp., STE TOS Release Notes, Jan 12, 1990, Sunnyvale, CA 94086
Claus Brod, Anton Stepper: Scheibenkleister II, MAXON Computer GmbH, Eschborn 1989. Das ‘Luftschloß’ ist eine über den Dateinamen konfigurierbare, superschnelle, resetfeste, autobootfähige, winzigkleine (mit eigener Optimierung unter 2 kB) RAM-Disk, die nach Einbau der hier vorgeschlagenen ‘ _geMnpb’-Routine auf allen relevanten TOS-Versionen läuft (hoffentlich zur Freude der Autoren wie auch der Anwender!).
* -------------------------------------------------------
* mal_find.s lists MD's of mal MPB & MD's of mfl br 2/90
* 1st revision: multiple tries in backtracing thru dead MD links
* br 1/91
* 2nd revision: reverse scan direction br 2/91
* -------------------------------------------------------
action: move.l 4(sp),a0 ;basepage addr
lea mystk,a1 ;end of code
move.l a1,sp ;new sp
suba.l a0,a1 ;prog length
move.l a1,-(sp) ;newsize
move.l a0,-(sp) ;block
clr -(sp) ;filler
move.w #$4A,-(sp) ;Mshrink
trap #1 ;GEMDOS
lea $C(sp),sp
start: dc.w $A000 ;line_a init
cmpi #79,-$2C(a0) ;v_cel_mx: minimum #columns = 80
blt sorry ;schade!
cmpi #24,$2A(a0) ;v_cel_my: minimum #lines = 25
blt sorry ;traurig!
lea title1(pc),a0 ;Titelzeile
bsr conws ;ausgeben
lea subtit(pc),a0 ;Untertitel
bsr conws ;ausgeben
pea main(pc)
move #$26,-(sp) ;Supexec
trap #14 ;XBIOS
addq #6,sp
term: bsr cnecin ;warte auf Taste
clr -(sp) ;Pterm0
trap #1 ;GEMDOS
* --------------------------------------------------
main: lea $48E,a3 ;themd — letzter MD der mal
lea $2000,a5 ;Startadresse
movea.l $432,a4 ;Endadresse = membot
bsr show_md ;zum Display (a3 - > MD)
loop_0: movea.l a5,a0 ;Startadresse laden
moveq #0,d6 ;Versuchszähler
loop_1: addq #2,a0 ;nur gerade Adressen
cmpa.l a0,a4 ;Endadresse erreicht ?
bls stuck ;war nichts
cmpa.l (a0),a3 ;Zeiger da?
bne loop_1 ;weiter testen
* Plausibilitäts-Tests für MD’s, falls (a0) = mp_mal - kritisch!
btst #0,15(a0) ;m_own gerade? (!)
bne loop_1 ;weiter testen
btst #0,7(a0) ;m_start gerade? (!)
bne loop1 ;weiter testen
btst #0,11(a0) ;m_length gerade? (!)
bne loop_1 ;weiter testen
move.l a0,a3 ;neuer MD?
bsr show_md ;als Eintrag der 'mal' ausgeben
weiter: tst.l 12(a3) ;m_own =0? (!!!)
bne loop_0 ;nächsten Zeiger suchen
thats_it: lea marker(pc),a0 ;als ungültig
bsr conws ;markieren
lea title3(pc),a0 ;neue Überschrift
bsr conws ;ausgeben
subq #4,a3 ;Adresse mpb
move.l a3, d3 ;umrechnen und
bsr prt_hex ;ausgeben
moveq #5,d4
sp_loop: bsr.s space ;Zwischenraum
dbf d4,sp_loop
move.l (a3),d3 ;mp_mf1
bsr prt_hex ;ausgeben
bsr.s space ;Zwischenraum
move.l 4(a3),d3 ;mp_mal
bsr prt_hex ;ausgeben
bsr.s space ;Zwischenraum
move.l 8(a3),d3 ;mp_rover
bsr prt_hex ;ausgeben
lea crlf2(pc),a0 ;2 * CR LF
bsr.s conws ;ausgeben
lea title2(pc),a0 ;letzte Titelzeile
bsr.s conws ;ausgeben
lea subtit(pc),a0 ;Untertitel
bsr.s conws ;ausgeben
loop_2: tst.l (a3) ;und jetzt
beq.s return ;schön
move.l (a3),a3 ;einfach
bsr.s show_md ;die 'mfl' ausgeben
bra loop_2
return: rts
stuck: lea marker(pc),a0 ;als ungültig
bsr.s conws ;markieren
addq #1,d6 ;# Versuche hochzählen
move.l a3,a0 ;ab hier weiter suchen
move.l (a3),a3 ;wieder alten Zeiger nehmen
cmp #10,d6 ;max. Anzahl der Versuche = 11
bls loop_1 ;nochmal versuchen!
lea sticky(pc),a0 ;leider
bsr.s conws ;steckengeblieben!
rts
* --------------------------------------------------
show_md: lea crlf(pc),a0 ;CR LF
bsr.s conws ;ausgeben
move.l a3,d3 ;md_addr
bsr.s prt_hex ;ausgeben
bsr.s space ;Zwischenraum ausgeben
moveq #3,d5 ;4 Werte
next_1: move.l (a3)+,d3 ;Wert holen
bsr.s prt_hex ;ausgeben
bsr.s space ;Zwischenraum
dbf d5,next_1 ;um durchzuschaun
lea -16(a3),a3 ;a3 restaurieren
rts
* --------------------------------------------------
space: lea space_1(pc),a0 ;Zwischenraum
conws: pea (a0) ;Stringadresse
move #9,-(sp) ;Cconws
trap #1 ;GEMDOS
addq #6,sp ;SP restaurieren
rts
* --------------------------------------------------
cnecin: move #7,-(sp) ;Cnecin
trap #1 ;GEMDOS
addq #2,sp
rts
* --------------------------------------------------
cconout: move d0,-(sp) ;char
move #2,-(sp) ;Cconout
trap #1 ;GEMDOS
addq #4,sp
rts
* --------------------------------------------------
* Langwort in d3 in Hex (als Text) auf Konsole ausgeben
prt_hex: moveq #7,d7 ;8 mal
nibble: rol.l #4,d3 ;jeweils ein Nibble
move d3,d0 ;ans Ende rollen
andi #$000f,d0 ;isolieren
lea hextab(pc),a0 ;Hextabelle holen
move.b 0(a0,d0.w),d0 ;und Zeichen "
bsr cconout ;ausgeben
dbf d7,nibble ;weiter
rts
* --------------------------------------------------
sorry: lea lorez(pc),a0 ;'sorry, min screen size 80 * 25!'
bsr conws ;ausgeben
bra term ;das war's
* --------------------------------------------------
hextab: dc.b '0123456789ABCDEF'
title1: dc.b 13,'mal: memory allocated list (c) br 90,91',13,10,0
subtit: dc.b 'md_addr m_link m_start mlength m_own',13,10
dc.b '--------------------------------------------',13,0
title2: dc.b 13,'mfl: memory free list (c) br 90 91',13,10,0
title3: dc.b 13,10,10,'mpb: memory parameter block (c) br 90,91',13,10
dc.b 'mpb_addr mp_mfl mp_mal mp_rover',13,10
dc.b '------------------------------------------',13,10,0
sticky: dc.b 13,10,'* * * * * * sorry, got stuck! * * * * * *',13,10,0
space_1: dc.b ' ',0
crlf2: dc.b 10
crlf: dc.b 13,10,0
marker: dc.b '<invalid! ',0
lorez: dc.b 13,10,'sorry, min screen size 80 * 25!',13,10,0
* --------------------------------------------------
even
bss
ds.l 100
mystk:
* --------------------------------------------------
* get_mpb.s (the real one!) br 2/90
* 1st revision: multiple tries in backtracking
* thru dead MD links br 1/91
* 2nd revision: reverse scan direction br 2/91
* --------------------------------------------------
action: move.l 4(sp),a0 ;basepage addr
lea stack+$400(pc),a1 ;end of code + $100 longs
move.l a1,d0
andi.b #$FC,d0 ;long align
move.l d0,sp ;new sp
suba.l a0,a1 ;prog length
pea (a1) ;newsize
pea (a0) ;block
clr -(sp) ;filler
move #$4A,-(sp) ;Mshrink
trap #1 ;GEMDOS
lea $C(sp),sp
lea title(pc),a0 ;Titelzeile
bsr conws ;ausgeben
pea main(pc) ;get_mpb
move #$26,-(sp) ;Supexec
trap #14 ;XBIOS
addq #6,sp
tst.l d0 ;na?
beq.s error ;schade!
move.l d0,a3 ;Adresse mpb
move.l a3,d3
bsr prt_hex ;ausgeben
moveq #2,d5 ;3 mal
loop_2: bsr.s space ;Zwischenraum
move.l (a3)+,d3 ;mp_mfl, mp_mal, mp_rover
bsr prt_hex ;ausgeben
dbf d5,loop_2
lea bye(pc),a0 ;'Taste drücken!'
bsr.s conws ;ausgeben
term: move #7,-(sp) ;Cnecin
trap #1 ;GEMDOS
clr (sp) ;Pterm0
trap #1 ;GEMDOS
error: lea err_l(pc),a0 ;Fehlermeldung
bsr.s conws ;ausgeben
bra term
* --------------------------------------------------
main: lea $48E,a3 ;themd = letzter MD der mal
lea $2000,a4 ;Startadresse
movea.l $432,a5 ;Endadresse = membot
loop_0: movea.l a4,a0 ;Startadresse
moveq #0,d6 ;Versuchszähler
loop_1: addq #2,a0 ;nur gerade Adressen
cmpa.l a0,a5 ;Endadresse erreicht?
bls.s stuck ;war nichts
cmpa.l (a0),a3 ;Zeiger?
bne loop_1 ;weiter testen
btst #0,15(a0) ;m_own gerade?
bne loop_1 ;weiter testen
btst #0,7(a0) ;m_start gerade?
bne loop_1 ;weiter testen
btst #0,11(a0) ;m_length gerade?
bne loop_1 ;weiter testen
move.l a0,a3 ;neuer MD?
tst.l 12(a3) ;m_own = 0 Besitzer? (!!!)
bne loop_0 ;nächster Zeiger
subq #4,a3 ;Adresse des MPB
move.l a3,d0 ;Rückgabewert
rts
stuck: addq #1,d6 ;# Versuche hochzählen
move.l a3,a0 ;ab hier weitersuchen
move.l (a3),a3 ;wieder alten Zeiger nehmen
cmp #10,d6 ;max. Anzahl der Versuche = 11
bls loop_1 ;nochmal versuchen!
moveq #0,d0 ;Fehler
rts
* --------------------------------------------------
space: lea space_1(pc),a0 ;Zwischenraum
conws: pea (a0) ;Stringadresse
move #9,-(sp) ;Cconws
trap #1 ;GEMDOS
addq #6,sp ;SP restaurieren
rts
* --------------------------------------------------
cconout: move d0,-(sp) ;char
move #2,-(sp) ;Cconout
trap #1 ;GEMDOS
addq #4,sp
rts
* --------------------------------------------------
* Langwort in d3 in Hex (als Text) auf Konsole ausgeben
prt_hex: moveq #7,d7 ;8 mal jeweils
nibble: rol.l #4,d3 ;ein Nibble
move d3,d0 ;ans Ende rollen
andi #$000f,d0 ;isolieren
lea hextab(pc),a0 ;Hextabelle holen
move.b 0(a0,d0.w),d0 ;und Zeichen
bsr cconout ;ausgeben
dbf d7,nibble ;weiter
rts
* --------------------------------------------------
hextab: dc.b '0123456789ABCDEF'
title: dc.b 13,'get mpb (the real one!) (c) br 90,91',13,10,10
dc.b 'mpb_addr mp_mfl mp_mal mp_rover',13,10
dc.b '---------------------------------------',13,10,0
space_1: dc.b ' ',0
bye: dc.b 13,10,10,"* * * press any key! * * *",13,10,0
err_1: dc.b "* * * sorry, couldn't get mpb! * * *",13,10,0
even
stack:
* --------------------------------------------------
bss
ds.l 100
* --------------------------------------------------
* _get_mpb.s link module dev1d from get_mpb (the
* real one!) br 2/90
* --------------------------------------------------
* 1st revision: multiple tries in backtracking
* thru dead MD links br 1/91
* 2nd revision: split mfl/mal search, reverse
* first scan direction br 2/91
* --------------------------------------------------
* long _get_mpb(void); IN: nothing,
* OUT: address of MPB or NULL in D0.L
* --------------------------------------------------
themd = $48E
membot = $432
memtop = $436
MAX = 10 ;max # Versuche = 11
* --------------------------------------------------
globl _get_mpb ;C & assembly language entry
globl GETMPB ;FORTRAN & Pascal entry
GETMPB:
* --------------------------------------------------
_get_mpb: movem.l d1-d2/a0-a2,-(sp) ;Register retten
pea get_mpb(pc) ;Routine
move #$26,-(sp) ;Supexec
trap #14 ;XBIOS
addq #6,sp
movem.l (sp)+,d1-d2/a0-a2 ;Register zurück
rts
* --------------------------------------------------
get_mpb: lea themd,a1 ;Ausgangspunkt
movea.l membot,a2 ;Suchende
move.l memtop,d0
sub.l a2,d0 ;freien Speicher berechnen
cmp.l 8(a1),d0 ;m_length = memtop - membot?
bne.s loop_0 ;fehlt schon etwas
lea $4000,a0 ;Suchbeginn
t_loop: addq #2,a0 ;nur gerade Adressen
cmpa.l a0,a2 ;Endadresse erreicht?
bls.s error ;fertig
cmpa.l (a0),a1 ;Zeiger auf themd?
bne t_loop ;weiter suchen
bra.s fini ;das war's
loop_0: move.l a2,a0 ;Suchbeginn = membot
moveq #0,d2 ;Versuchszähler
loop_1: subq #2,a0 ;nur gerade Adressen
cmpa.l #$2000,a0 ;Endadresse erreicht?
bls.s stuck ;war nichts
cmpa.l (a0),a1 ;Zeiger da?
bne loop_1 ;weiter testen
* Plausibilitäts-Tests für MD's, falls mp mal gefunden wurde, kritisch!
btst #0,15(a0) ;m_own gerade?
bne loop_1 ;weiter testen
btst #0,7(a0) ;m_start gerade?
bne loop_1 ;weiter testen
btst #0,11(a0) ;m_length gerade?
bne loop_1 ;weiter testen
move.l a0,a1 ;evtl. neuer MD
tst.l 12(a0) ;Besitzer = 0?
bne loop_0
subq #4,a0 ;Adresse des MPB
fini: move.l a0,d0 ;Rückgabewert
rts
stuck: addq #1,d2 ;# Versuche hochzählen
move.l a1,a0 ;war kein gültiger m_link
move.l (a1),a1 ;alten Zeiger nehmen
cmp #MAX,d6 ;max. Anzahl der Versuche
bne loop_1 ;nochmal versuchen!
error: moveq #0,d0 ;Fehler
rts
* ----------------------------------------------
* cart_mpb.s install 'MPB*' cookie from type #1
* cartridge br 1/91
* ----------------------------------------------
p_cookie = $5A0
longfram = $59E
resvalid = $426
resvecto = $42A
RESMAGIC = $31415926
membot = $432
trap13 = $B4
COOKIE = 'MPB*'
* --------------------------------------------------
ca_magic: dc.l $ABCDEF42
ca_next: dc.l 0
ca_init: dc.l $02FA0026
ca_run: dc.l $00FA0026
ca_time: dc.w 0
ca_date: dc.w 0
ca_size: dc.l ende-start
ca_name: dc.b 'CART_MPB.003',0,0
* --------------------------------------------------
start: move.l membot,a0 ;erstmal Platz besorgen
move.l trap13,d0
move.l d0,(a0)+ ;alten BIOS-Vektor retten
move.l a0,membot
lea get_mpb(pc),a0 ;neuen BIOS-Vektor
move.l a0,trap13 ;installieren
rts
* --------------------------------------------------
get_mpb: move.l sp,a0 ;OS ist immer im Super
tst longfram ;Prozessor?
beq.s weiter
addq #2,a0
weiter: tst 6(a0) ;Getmpb?
beq.s do_it
move.l membot,a0 ;dort ist
subq #4,a0 ;alter Vektor
jmp (a0)
* --------------------------------------------------
* Here's where the action is: IN: p_mpb @ 8(a0),
* OUT: entry in cookie jar
* --------------------------------------------------
do_it: move.l 8(a0),a0 ;p_mpb
movem.l d3-d7/a3-a7,-(sp) ; retten
move.l membot,a6 ;altes membot retten
move.l a0,a5 ;MPB merken
move.l #COOKIE,d3 ;'MPB*'
movea.l p_cookie,a0 ;*p_cookie
move.l a0,d0 ;NULL = TOS <1.6?
beq.s make_jar ;cookie jar nicht vorhanden
moveq #0,d1 ;Zähler
jarscan:addq #1,d1 ;Eintrag zählen
cmp.l (a0),d3 ;cookie schon da?
move.l (a0)+,d0 ;cookie
beq.s put_cook ;eintragen
addq #4,a0 ;nächstes cookie
bra jarscan ;prüfen
put_cook:cmp.l (a0),d1 ;noch Platz?
beq.s mak_spc ;nein
move.l (a0)+,d0 ;#d.Einträge
clr.l (a0)+ ;Endeintrag
move.l d0,(a0) ;verschieben
subq #8,a0 ;zurückpositionieren
move.l a5,(a0) ;Adresse des MPB eintragen
move.l d3,-(a0) ;cookie eintragen
bra.s return ;unlink from trap etc.
mak_spc:movea.l p_cookie,a1 ;*p_cookie
move.l a6,a0 ;Adresse = membot
move.l d1,d2 ;Zähler
add.l d2,d2 ;für je 2 longs
subq.l #3,d2 ;präparieren
j_loop: move.l (a1)+,(a0)+ ;altes cookie jar
dbf d2,j_loop ;umkopieren
move.l d1,d0 ;minus Endeintrag
addq.l #7,d0 ;und um 8 erweitern
bra.s _injar
make_jar:move.l a6,a0 ;Adresse = membot
moveq #8,d0 ;# Einträge
_injar: move.l d3,(a0)+ ;cookie
move.l a5,(a0)+ ;Adresse des MPB
clr.l (a0)+ ;Endeintrag
move.l d0,(a0)+ ;Anzahl eintragen
move.l a6,p_cookie ;cookie jar eintragen
lea 48(a0),a0 ;+6*8 position.
_unjar: move.l resvecto,(a0)+ ;Reihenfolge
move.l resvalid,(a0)+ ;und Lage wie unten!
movea.l a0,a2 ;Position merken
lea reshand(pc),a1 ;Resethandler
moveq #copy_cnt,d0
c_loop: move.l (a1)+,(a0)+ ;kopieren
dbf d0,c_loop
move.l a0,membot ;neues membot
move.l a2,resvecto
move.l #RESMAGIC,resvalid
return: lea -4(a6),a0 ;alten trap13 Vektor
move.l (a0),trapl3 ;restaurieren
movem.l (sp)+,d3-d7/a3-a7 ;restaur.
jmp (a0)
* --------------------------------------------------
vecsave: dc.l 0 ;Reihenfolge
valsave: dc.l 0 ;und Lage beachten!
reshand: clr.l p_cookie;für < TOS 1.6
move.l valsave(pc),resvalid
move.l vecsave(pc),resvecto
jmp (a6)
cpy_cnt = (* - reshand)/4
* --------------------------------------------------
ende:
* --------------------------------------------------
* boot_mpb.s install 'pMPB' cookie from
* bootsector of drive A: br 1/91
* --------------------------------------------------
* Bedingungen: Laufwerk A:, Speicher in
* Originalkonfiguration, sonst raus.
* Annahme: Es gebe zur Zeit der Ausführung des
* Bootsektors A: einen Zeiger
* auf 'themd' zwischen SRCHBG und membot
* (Suchrichtung!), den 'MPB.mp_mfl'.
* --------------------------------------------------
p_cookie = $5A0
resvalid = $426
resvecto = $42A
RESMAGIC = $31415926
themd = $48E
membot = $432
memtop = $436
bootdev = $446
COOKIE = 'MPB*'
SRCHBG = $4000
* --------------------------------------------------
bra.s start
dcb.w 16,'**'
msg_1: dc.b ' MPB Ox',0
msg_2: dc.b ' cookie in jar br91’,13,10,0
dcb.w 16,'**'
* --------------------------------------------------
start: move #$19,-(sp) ;Dgetdrv
trap #1 ;GEMDOS
addq #2,sp
tst d0 ;Laufwerk A:?
bne.s getout ;nein
get_mpb: movea.l membot,a6 ;merken
move.l memtop,d0 ;freien Speicher
sub.l a6,d0 ;berechnen
lea themd,a3 ;themd sei einziger MD der mfl
cmp.l 8(a3),d0 ;m_length = memtop - membot ?
bne.s getout ;Speicher schon manipuliert, von wem???
lea SRCHBG,a0 ;willkürliche Startadresse für Suche
movea.l a6,a1 ;Endadresse = membot
t_loop: addq #2,a0 ;nur gerade Adressen
cmpa.l a0,a1 ;Endadresse erreicht?
bls.s getout ;fertig
cmpa.l (a0),a3 ;Zeiger da?
bne t_loop ;weiter suchen
movea.l a0,a5 ;Adresse des MPB merken
move.l #COOKIE,d7 ;'MPB*'
movea.l p_cookie,a0 ;*p_cookie
move.l a0,d0 ;NULL = TOS <1.6?
beq.s make_jar ;cookie jar nicht vorhanden
moveq #0,d1 ;Zähler
jarscan: addq #1,d1 ;Eintrag zählen
cmp.l (a0),d7 ;cookie schon da?
beq tell_it ;das war's schon
move.l (a0)+,d0 ;cookie
beq.s put_cook ;hier eintragen
addq #4,a0 ;nächstes cookie
bra jarscan ;prüfen
put_cook:cmp.l (a0),d1 ;noch Platz?
beq.s mak_spc ;nein
move.l (a0)+,d0 ;Zahl der Einträge
clr.l (a0)+ ;Endeintrag
move.l d0,(a0) ;verschieben
subq #8,a0 ;zurückpositionieren
move.l a5,(a0) ;Adresse des MPB
move.l d7,-(a0) ;cookie eintragen
bsr.s tell_it ;Meldung ausgeben
getout: rts ;fertig
mak_spc: movea.l p_cookie,a1 ;*p_cookie
move.l a6,a0 ;Adresse = membot
move.l d1,d2 ;Zähler
add.l d2,d2 ;für je 2 longs
subq.l #3,d2 ;präparieren
j_loop: move.l (a1)+,(a0)+ ;altes cookie jar
dbf d2,j_loop ;umkopieren
move.l d1,d0 ;minus Endeintrag
addq.l #7,d0 ;und um 8 erweitern
bra.s _injar
make_jar:move.l a6,a0 ;Adresse - membot
moveq #8,d0 ;# Einträge
_injar: move.l d7,(a0)+ ;cookie
move.l a5,(a0)+ ;Adresse des MPB
clr.l (a0)+ ;Endeintrag
move.l d0,(a0)+ ;Anzahl eintragen
move.l a6,p_cookie ;cookie jar eintragen
lea 48(a0),a0 ;+6*8 positionieren
_unjar: move.l resvecto,(a0)+ ;Reihenfolge
move.l resvalid,(a0)+ ;und Lage wie unten!
movea.l a0,a2 ;Position merken
lea reshand(pc),a1 ;Resethandler
moveq #copy_cnt,d0
c_loop: move.l (a1)+,(a0)+ ;kopieren
dbf d0,c_loop
move.l a0,membot ;neues membot
addq #4,a3 ;m_start
move.l a0,(a3)+ ;eintragen
suba.l a6,a0 ;Platz
move.l a0,d0 ;besorgen
sub.l d0,(a3) ;neues m_length
move.l a2,resvecto
move.l #RESMAGIC,resvalid
tell_it: lea msg_1(pc),a0 ;string
bsr.s message
move.l a5,d3 ;MPB
bsr.s prt_hex
lea msg_2(pc),a0 ;string
message: pea (a0) ;string
move #9,-(sp) ;Cconws
trap #1 ;GEMDOS
addq #6,sp
rts
* --------------------------------------------------
cconout: move d0,-(sp) ;char
move #2,-(sp) ;Cconout
trap #1 ;GEMDOS
addq #4,sp
rts
* --------------------------------------------------
* Wort in d3 in Hex (als Text) auf Konsole ausgeben
prt_hex: moveq #3,d7 ;4 mal
nibble: rol #4,d3 ;jeweils ein Nibble
move d3,d0 ;ans Ende rollen
andi #$000f,d0 ;isolieren
lea hextab(pc),a0 ;Hextabelle holen
move.b 0(a0,d0.w),d0 ;und Zeichen
bsr cconout ;ausgeben
dbf d7,nibble ;weiter
rts
* --------------------------------------------------
vecsave: dc.l 0 ;Reihenfolge
valsave: dc.l 0 ;und Lage beachten!
reshand: clr.l p_cookie ;für < TOS 1.6
clr bootdev ;von Floppy booten bei < TOS 1.6
move.l valsave(pc),resvalid
move.l vecsave(pc),resvecto
jmp (a6)
copy_cnt = (* - reshand)/4 ;1 added in loop
* --------------------------------------------------
hextab: dc.b '0123456789ABCDEF'