Sind Sie stolzer Besitzer sowohl eines Commodore 64 als auch eines Commodore Amiga? Programmieren Sie noch immer professionelle Software in Assembler auf dem guten alten C64? Dann werden Sie wahrscheinlich oft, wenn Sie sich mit der, im Vergleich zum Amiga wohl als vorsintflutlich zu bezeichnenden Programmierumgebung herumschlagen mußten, neidisch zu Ihrem Amiga geblickt haben. Von diesem mit tollen Editoren, grafischer Benutzeroberfläche, Windowing, Multitasking und hoher Geschwindigkeit ver- wöhnt, bleibt Ihnen trotzdem nichts anderes übrig, als mit dem kleinen Speicher und der langsamen Floppy zusätzlich zum unkomfortablen Basic- editor, der meist benutzt werden muß, auszukommen. Welche Probleme sich hier ergeben, kann sich wahrscheinlich jeder lebhaft vorstellen. Es dürfte bereits klar sein, auf was ich hinaus will: Es müßte einfach eine Möglichkeit geben, dem Amiga die Maschinensprache des C64 beizubringen um dann auf diesem mit all seinen Möglichkeiten Programme für den C64 erstellen zu können - auf komfortablere und einfachere Weise, als es mit dem C64 selbst je möglich wäre. Schließlich müssten sich diese Programme dann noch zum C64 übertragen lassen. Am Ende dieses Gedankenganges (und noch vieler weiterer kleiner Einfälle) steht das, was Sie nun vor sich liegen haben: Das 6510-Developers-Packet. Obwohl die Idee, auf größeren Rechnern Programme für kleinere Rechner zu entwickeln, nicht neu ist, wurde sie auf dem Amiga für den doch immer noch recht beliebten und für sein Alter von nunmehr 10 Jahren noch erstaunlich leistungsfähigen C64 in dieser Weise (so weit ich weiß) noch nie reali- siert. Das 6510-Developers-Packet beinhaltet deswegen alle Komponenten, die nötig sind, um einerseits dem Amiga das nötige Wissen zu geben, mit 6510-Assembler etwas anfangen zu können und andererseits die Übertragung zum C64 zu realisieren. Der im Paket enthaltene 6510-Cross-Makroassembler "CrossAss V1.0" ist dafür zuständig, den Amiga mit 6510-Assembler, für den er normalerweise kein Verständnis hat, vertraut zu machen. "CrossAss V1.0" kann 6510-Assemblerprogramme, die mit einem beliebigen Textedi- tor Ihrer Wahl erstellt wurden, "interpretieren" und die darin enthaltenen 6510-Mnemonics in Opcodes umwandeln (=assemblieren). So entsteht noch auf dem Amiga ein auf dem C64 lauffähiges Maschinensprachprogramm, welches nur noch übertragen werden muß. Da das Programm im Speicher des Amiga praktisch bereits genauso vorliegt, wie es der C64 später brauchen wird, beschränkt sich der Teil der Software, die für den C64 bestimmt ist, auf Routinen, die den Empfang der Daten realisieren. Prinzipiell gibt es zur Übertragung der Daten vom Amiga zum C64 zwei einfache Möglichkeiten, nämlich die serielle Übertragung per RS232-Schnittstelle oder die parallele Übertragung vom Parallelport des Amiga zum Userport des C64. Die erste Möglichkeit hat zwar den Vorteil, daß Übertragungsroutinen bereits in den Betriebssystemen sowohl des Amiga als auch des C64 enthalten sind, jedoch aber auch einige entscheidende Nachteile. Zunächst besitzt der C64 zwar RS232-Routinen, aber keine solche Schnittstelle. Diese müßte über relativ komplizierte Hardwareerweiterungen nachgerüstet werden. Außerdem ist die serielle Schnittstelle aufgrund der Notwendigkeit des Taktens der Übertragung mit auf beiden Computern übereinstimmenden Übertragungsraten (BAUD/BPS-Raten) eine heikle, umständliche und nicht gerade einfache Sache. Die zweite Möglichkeit hat nur den Nachteil fehlender Betriebssystemunterstützung durch den C64. Die Vorteile sind jedoch immens: Durch Übertragung von jeweils 8 Bit und die selbstständige Geschwindigkeitsanpassung (Handshaking) tritt keines der Probleme der seriellen Übertragung auf und die Geschwindigkeit erreicht immer das Maximum. Da der C64 nur empfängt, aber selbst nicht sendet, liegt nichts näher, als den C64 wie einen Drucker am parallelen Port des Amiga anzusprechen, was durch das Betriebssystem des Amiga ja sowieso unterstützt wird. Das Kabel an sich ist entsprechend sehr einfach aufgebaut. Trotzdem sollte der Nachbau sorgfältig erfolgen, da falsch angelötete Leitungen die Zerstörung eines oder beider Computer zur Folge haben können. Markieren Sie sich auch die Oberseite des nicht verdrehsicheren C64-Userport-Steckers! Außerdem darf das Kabel nie eingesteckt werden, wenn die Rechner gerade in Betrieb sind. Sind beide Rechner erfolgreich mit dem Kabel verbunden, so kann nach vollendeter Assemblierung die Übertragung beginnen, wobei hier zwei verschiedene Empfangsroutinen auf dem C64 dafür sorgen, daß die übertragenen Daten entweder direkt auf Disk ("Transfer") oder im RAM des C64 ("TransRAM") landen. Dort können sie dann weiterverwendet werden. Für die Funktionsweise des Assemblers wurde die auf dem C64 übliche 2-Pass-Assemblierung verwendet. Das bedeutet, daß der Assembler den Quelltext zweimal durchläuft, wobei er in Pass 1 nur die Labeldefinitionen übernimmt. In Pass 2 erfolgt dann die eigentliche Assemblierung des Quelltextes mit Zugriff auf alle Labels. Auf daraus folgende Konsequenzen wird noch öfter in dieser Anleitung eingegangen werden. Wie bereits erwähnt, erfolgt die Eingabe der 6510-Assembler-Programme mit einem Texteditor Ihrer Wahl. Das kann entweder Ihr Lieblingstexteditor oder Ihre Lieblingstextverarbeitung (im ASCII-Modus) sein, oder auch die Editoren "Ed" der Workbench- bzw. "MicroEmacs" der Extras-Diskette. Damit CrossAss V1.0 Ihren Quelltext aber auch wirklich verstehen kann, müssen Sie sich an einige Regeln halten. Wie Sie sehen werden, ist CrossAss hier aber äußerst großzügig. Der grundsätzliche Aufbau einer Zeile sieht in etwa so aus: LABEL ANWEISUNG OPERAND,OPERAND,OPERAND... ;KOMMENTAR "In etwa" nur deshalb, weil eine Quelltextzeile nicht immer exakt obige Gestalt annimmt. Ganz im Gegenteil kann auch der Fall auftreten, daß keine der obigen Komponenten tatsächlich vorhanden sind. Wichtig ist zunächst nur, daß sowohl Label als auch Anweisung (wird alles noch erklärt) und der erste Operand durch mindestens ein Leerzeichen getrennt werden müssen. Sollten mehrere Operanden folgen, so werden diese durch Kommata getrennt angegeben. Ein eventuell angefügter Kommentar wird durch ein Semikolon ";" eingeleitet und endet am Zeilenende. Ist das erste Nichtleerzeichen der Zeile sofort ein Semikolon, so wird entsprechend die ganze Zeile als Kommentarszeile überlesen. Ist die Zeile völlig leer (eben der obige Fall, daß keine der Komponenten der Zeile vorhanden sind), so wird entsprechend auch nichts assembliert. Sie können also zur Strukturierung Ihres Pro- gramms einfach Leerzeilen einfügen, was zur Übersichtlichkeit beiträgt. Da ich vorraussetze, daß Sie bereits auf dem C64 mit anderen Assemblern gearbeitet haben, wird Ihnen dieser Zeilenaufbau nicht fremd sein und entsprechend dürfte die Verwendung eines Labels am Zeilenanfang zur Zuweisung des aktuellen Programmcounters ebenfalls bekannt sein, da gerade diese Möglichkeit ja die Flexibilität eines Assemblers ausmacht. Es ist aber natürlich keine Pflicht, ein Label am Anfang der Zeile zu definieren. Unter Anweisung versteht sich entweder ein Mnemonic, ein Pseudo-Opcode oder ein Makroname. All diese Begriffe sind Ihnen sicherlich nicht unbekannt, so daß die Begriffserläuterungen unten genügen sollte. Damit der Rahmen dieses Artikels nicht gesprengt wird, kann hier natürlich nur auf CrossAss V1.0-spezifische Dinge genauer eingegangen werden. Beachten Sie beim Erstellen des Quelltextes, daß grundsätzlich nur eine Anweisung pro Zeile stehen darf. Die Definition eines Labels am Zeilenanfang und die Verwendung eines Kommentars ist, wie bereits gesagt, optional. Mit Kommentaren sollten Sie aber aus Selbstzweck nicht sparen, sofern Sie sich später noch in Ihrem Programm auskennen wollen. Die Anzahl der auf die Anweisung folgenden Operanden hängt von der Anweisung selbst ab. Während auf Mnemonics grundsätzlich nur ein Operand folgt, der unter anderem die Adressierungsart bestimmt, können auf Makros, je nach Definition, und auf Pseudo-Opcodes, je nach "Natur", auch zwei oder mehrere Operanden folgen. Es wird zwischen drei Arten von Operanden unterschieden: - Terme - Mnemonic-Operanden (enthalten Terme) - Texte CrossAss V1.0 bietet die Möglichkeit, überall dort, wo Werte assembliert werden sollen, mathematische Terme zu verarbeiten. Dabei hält sich CrossAss an die mathematischen Grundregeln (Punkt vor Strich etc.). Terme können durch die Verwendung von "(", "[", ")" und "]" geklammert werden. Die in einem Term verwendeten Zahlen können als - Ziffernfolgen - Labels - Makroparameter - Zeichen - "*" auftreten. Ziffernfolgen können dabei in vier verschiedenen Zahlensystemen verwendet werden, worüber das Vorzeichen der Zahl entscheidet. Dezimal- zahlen (Basis 10) haben kein Vorzeichen, Hexadezimalzahlen (Basis 16) ein "$" (Zusatzziffern "a"-"f" bzw. "A"-"F"), Oktalzahlen (Basis 8) ein "&" und Binärzahlen (Basis 2) ein "%". Bei der Verwendung von Labels werden diese durch ihre Werte ersetzt. Makroübergabeparameter sind nur innerhalb von Makroquelltexten verwendbar. Sie werden durch das Vorzeichen "?" eingeleitet, auf das eine Ziffer zwischen 0 und 9 (einschließlich) folgt, die die Nummer des Parameters bestimmt (0 = Parameter 1 ... 9 = Parameter 10). Die Verwendung von Zeichen wird durch das Vorzeichen "'" (ALT+ä) eingeleitet auf welches das zu verwendende Zeichen folgen muß. Dieses Zeichen wird durch seinen ASCII-Code ersetzt, welcher mit Hilfe der veränderbaren (wird später erklärt) Konvertierungstabelle angepasst wird. Die Zahl "*" schließlich ist ein Sonderfall. Sie wird bei der Assem- blierung durch den aktuellen Programmzähler ersetzt. Vor alle Zahlen, aber auch vor geklammerte Terme können zusätzlich noch die Vorzeichen "<" und ">" gestellt werden, die CrossAss V1.0 veranlassen, nur das Low- ("<") bzw. das Highbyte (">") des Wertes zu verwenden. Neben den Grundrechenarten "+", "-", "*" (Multiplikation) und "/" (Division) kennt CrossAss V1.0 noch folgende weitere Rechenarten: "|" ODER-Verknüpfung linker Wert mit rechtem Wert "&" UND-Verknüpfung linker Wert mit rechtem Wert (nicht zu verwechseln mit dem Vorzeichen oktaler Zahlen) "^" EXCLUSIV-ODER Verknüpfung linker Wert mit rechtem Wert "<" Verschiebung von linkem Wert um rechten Wert Bits nach LINKS (nicht zu verwechseln mit Vorzeichen "<") ">" Verschiebung von linkem Wert um rechten Wert Bits nach RECHTS (nicht zu verwechseln mit Vorzeichen ">") Mnemonic-Operanden können logischerweise nur auf Mnemonics folgen und enthalten sowohl die Adressierungsart des Mnemonics selbst, als auch einen zur Adressierungsart gehörenden Wert, wobei dieser je nach Adressierungsart eine Breite von 8 oder 16 Bit hat. Natürlich kann auch dieser Wert durch einen Term angegeben werden. Die Angabe der Adressierungsart erfolgt durch die Standardschreibweisen, die allgemein bekannt sein dürften: immediate (8 Bit): #Term absolut (16 Bit): Term absolut x-indiziert (16 Bit): Term,x absolut y-indiziert (16 Bit): Term,y Zeropage (8 Bit): Term Zeropage x-indiziert (8 Bit): Term,x Zeropage y-indiziert (8 Bit): Term,y indiziert indirekt (8 Bit): (Term,x) indirekt indiziert (8 Bit): (Term),y relativ (16 Bit): Term indirekt (16 Bit): (Term) Akkumulator/implement (8 Bit): kein Operand ! Zu beachten gibt es hier folgendes: Bei den indizierten Adressierungen ist es egal, ob die Angabe des Indexregister in Groß- oder Kleinschrift erfolgt. Bei relativer Adressierung wird der 16-Bit-Wert in einen relativen, vorzeichenbehafteten 8-Bit-Wert umgewandelt, so daß bei Branchsprüngen eine maximale Reichweite von 126 Byte in Richtung Programm- anfang bzw. 129 Byte in Richtung Programmende erreicht wird. Bei indirekten Adressierungen dürfen nur runde Klammern als Adressierungs- merkmal benutzt werden, eckige Klammern sind der Klammerung von Termen vorbehalten. Vorsicht ist bei der Unterscheidung zwischen den Zeropage- und absoluten Adressierungsarten geboten. Hier wird nur durch die Bitbreite des Wertes entschieden um welche Adressierungsart es sich handelt. Eine Problem kann sich hierbei nämlich daraus ergeben, daß CrossAss ein 2-Pass-Assembler ist. Wie bereits erwähnt, beschränkt sich die Arbeit von CrossAss in Pass 1 ausschließlich auf das Definieren der Labels. Da für die Definition von Labels am Zeilenanfang aber der Programmcounter bekannt sein muß, müssen bereits hier alle Adressierungs- arten richtig erkannt werden. Wird nun eine Adressierung in Abhängigkeit eines Labels gebracht, das darüber entscheidet, ob es sich um eine Zeropage- oder um eine absolute Adressierungsart handelt und ist dieses Label noch nicht bekannt, weil es z.B. später im Quelltext definiert wird, so nimmt der Assembler an, es handele sich um die absolute Adressierungsart. Wird dieses Label später jedoch so definiert, daß es sich in Wahrheit um eine Zeropageadressierung gehandelt hätte, so wird diese in Pass 2 zwar richtig assembliert, aber es ergibt sich eine Differenz zwischen den Programmcountern in Pass 1 und Pass 2 was mit höchster Wahrscheinlichkeit falsche Sprungadressen im Objektcode zur Folge hat, ohne daß der Assembler hiervor warnen könnte. Sollte also der Fall gegeben sein, daß eine Zeropageadressierung in Abhängigkeit eines Labels assembliert werden soll, so muß in diesem Fall unbedingt das Label bereits vorher im Quelltext definiert worden sein! Möchte man jedoch genau den entgegengesetzten Fall erreichen, nämlich unbedingt eine absolute Adres- sierungsart erzwingen (manchmal notwendig bei selbstmodifizierenden Programmen), so muß vor den gesamten Operanden das Vorzeichen "!" gesetzt werden. Die letzte Art eines Operanden tritt ausschließlich bei Pseudo-Opcodes auf. Es handelt sich um ganze Texte, die z.B. Dateinamen enthalten können, aber auch dazu bestimmt sein können, in den Objektcode eingefügt zu werden. In letzterem Fall werden sie wieder mit Hilfe der Konvertierungs- tabelle angepasst. Ein Text beginnt und endet mit einem Anführungszeichen ("), wobei keines der beiden vergessen werden darf. Texte können ent- sprechend alle Zeichen, also auch Kommata, enthalten. Die maximale Anzahl einzelner Operanden beträgt übrigens 32, während die maximale Länge eines jeden Operanden 80 Zeichen nicht überschreiten darf (wichtig bei Texten). Zu Mnemonics und deren Adressierungsarten braucht im Rahmen dieser Anleitung wohl nichts mehr gesagt werden, da diese entweder bekannt, oder in jeder Fachliteratur zum 6510 nachzulesen sind. Die Pseudo-Opcodes von CrossAss müssen jedoch einzeln erläutert werden, da sie sich in vielen Punkten von denen anderer Assembler unterscheiden und außerdem stark unterschiedliche Funktionen haben. Zunächst sollte die Schreibweise der Pseudo-Opcodes vereinbart werden, denn auch hier sind sie eine Ausnahme. Alle Pseudo-Opcodes beginnen mit dem Punkt ".". Darauf folgt der eigentliche Name des Pseudo-Opcode. Da der Assembler Pseudo-Opcodes entsprechend sehr leicht erkennt, braucht der Name des Pseudo-Opcodes nicht vollständig angegeben werden. Es genügen soviele Zeichen, wie zur unver- wechselbaren Unterscheidung notwendig sind (Also z.B. ".in" für ".include" aber nicht nur ".i", da dies auch für ".if" stehen könnte). Zwischen Groß- und Kleinschreibung wird nicht unterschieden. .BYTE Der ".byte"-Pseudo-Opcode dient zum Einfügen von Bytefolgen in den Quelltext (z.B. Tabellen). Die Bytes werden als Operanden angegeben. Entsprechend der Maximalzahl können also max. 32 Bytes pro Pseudo-Opcode angegeben werden. .WORD Der ".word"-Pseudo-Opcode fügt 16-Bit-Worte im 6510-typischen Format Low-Byte/High-Byte in den Quelltext ein. Anstatt ".word 1200" könnte man also auch ".byte <1200,>1200" schreiben. .BLOCK Der ".block"-Pseudo-Opcode kann ein angegebenes Byte entsprechend der angegebenen Anzahl oft in den Quelltext einfügen. Parameter 1 enthält die Anzahl (max. 65535), Parameter 2 das Byte selbst. Dieser Befehl sollte nicht zu großzügig angewendet werden, da er den Objektcode meist nur unnötig verlängert. .ASCII Der ".ascii"-Pseudo-Opcode dient dazu, einen Text in den Objektcode einzufügen. Der Text wird als Operand angegeben (Anführungszeichen nicht vergessen) und von CrossAss mit Hilfe der Konvertierungstabelle angepasst. .BASE Der ".base"-Pseudo-Opcode definiert die Startadresse des Objektcodes und dessen Dateinamen. Die Adresse wird als erster, der Dateiname inkl. Pfad als zweiter Parameter angegeben. Durch mehrmaliges Verwenden des ".base"-Pseudo-Opcodes bietet CrossAss V1.0 als Besonderheit die Möglichkeit des Objektcodesplitting. Das heißt, der Objektcode kann in mehrere Blöcke (Objektcodedateien) an verschiedenen Adressen aufgesplittet werden, wobei trotzdem blockübergreifend gearbeitet werden kann. Die einzelnen Objektcodedateien müssen einzeln zum C64 übertragen werden. Jeder Quelltext muß mindestens einen ".base"-Pseudo-Opcode enthalten, der vor der ersten den Objektcode beeinflussenden Anweisung stehen muß. .EQUAL Mit dem ".equal"-Pseudo-Opcodes können Labels explizit definiert werden, das heißt, der dem Label zuzuweisende Wert kann vom Programmierer selbst angegeben werden. Der Labelname muß aber trotzdem als erstes in der Zeile stehen, gefolgt vom Pseudo-Opcode. Dessen Operand schließlich ist der zuzuweisende Wert. Wird ein Label in Abhängigkeit eines anderen Labels definiert, so muß letzteres bereits vorher definiert worden sein, da die Definition des abhängigen Labels ebenfalls in Pass 1 vorgenommen wird. Ein Label kann grundsätzlich nur einmal definiert und nicht mehr geändert werden (Ausnahme sind lokale Labels in Makroquelltexten) ! Übrigens: Die Zeile "marke lda $2000" kann entsprechend durch die beiden aufeinanderfolgenden Zeilen "marke .equal *" und "lda $2000" ersetzt werden. .MACRO - .ENDMACRO Die beiden Pseudo-Opcodes ".macro" und ".endmacro" dienen zur Definition eines Makros. Sie umschließen den Makroquelltext. Nach dem Pseudo-Opcode ".macro" wird als erster Parameter der Name des Makros OHNE Anführungszei- chen angegeben (max. 32 Zeichen). Als zweiter Parameter wird optional die Anzahl der Übergabeparameter hinzugefügt, welche Werte von 1-10 (ein- schließlich) annehmen kann. Wird 0 oder gar kein zweiter Parameter angege- ben, so werden keine Übergabeparameter verwendet. Innerhalb des Makros kann wie oben beschrieben auf die Übergabeparameter zugegriffen werden. Alle innerhalb des Makroquelltextes definierten Labels gelten als lokale Labels und sind nur dort erreichbar. Anders als in Hochsprachen sind Überlappungen von globalen Labels durch lokale Labels gleichen Namens nicht erlaubt.Innerhalb eines Makroquelltextes darf aus programmtechnischen Grün- den weder ein weiteres Makro definiert noch eine Datei per ".include" ein- gebunden werden, was aber logisch ist, da im ersten Fall Makros bei mehrma- ligem Aufruf mehrmals definiert werden würden und letzterer Fall nur zu un- nötig langen Objektcodedateien beitragen würde. Makroaufrufe können jedoch sehr wohl geschachtelt werden (max. 32 mal), wobei jedoch jedes Makro be- reits vor seinem Aufruf bekannt sein muß (sowohl in Pass 1 als auch in Pass 2). .IF - .ELSE - .ENDIF Wie aus Hochsprachen bekannt, dienen if-else-endif-Konstruktionen zur bedingten Bearbeitung, in unserem Fall zur bedingten Assemblierung. Ist die angegebene Bedingung erfüllt wird der Teil zwischen ".if" und ".else" assembliert, der Teil nach ".else" überlesen. Ist die Bedingung nicht erfüllt, wird der Teil zwischen ".if" und ".else" überlesen und zwischen ".else" und ".endif" assembliert. Der ".else"-Teil kann dabei entfallen, so daß nur eine ".if"-".endif"-Konstruktion entsteht, bei der bei nicht erfüllter Bedingung nichts assembliert wird. ".if"-".else"-".endif"-Kon- struktionen können beliebig ineinander oft (max. 65535 mal) verschachtelt werden Die Bedingung selbst wird dem ".if"-Pseudo-Opcode als Operand übergeben. Dabei wird in zwei Verwendungsweisen unterschieden: - Logische Verknüpfung zweier Operanden - Prüfung auf Berechenbarkeit eines Operanden In ersterem Fall werden die Ergebnisse zweier Terme logisch miteinander verknüpft und entsprechend dem Ergebnis assembliert. Als erster Operand des ".if"-Pseudo-Opcodes muß zunächst der Vergleichsoperator angegeben werden, mit dem die beiden Vergleichsoperanden verknüpft werden sollen. CrossAss kennt vier Möglichkeiten, wobei KEINE Kombinationen möglich sind: = prüft auf Gleichheit der beiden Operanden ! prüft auf Ungleichheit der beiden Operanden < prüft, ob erster Operand kleiner als zweiter Operand > prüft, ob erster Operand größer als zweiter Operand Als zweiter und dritter Operand des Pseudo-Opcodes werden dann die Vergleichsoperanden angegeben. Diese etwas ungewöhnliche Schreibweise soll an zwei Beispielen verdeutlicht werden: .if =,Label1,Label2 wahr, wenn "Label1" gleich "Label2" .if <,$c000+23*3,*+10 wahr, wenn $c000+23*3 kleiner 10 + Programmcounter Bei der zweiten Verwendungsmöglichkeit des ".if"-Pseudo-Opcodes wird ge- prüft, ob ein Term berechnet werden kann, oder ob dabei ein Fehler auftritt (natürlich mit Unterdrückung einer Fehlermeldung die zum Abbruch der Assemblierung führen würde). Dies ist hauptsächlich dann nützlich, wenn eine Includedatei nur dann eingebunden werden soll, wenn ein Label, das diese Includedatei kennzeichnet, noch nicht definiert ist. Der Sinn solcher Maßnahmen wird später noch ausführlich erklärt. Die Zahl der Vergleichsoperatoren beschränkt sich in diesem Fall auf zwei, nämlich: = wahr, wenn bei der Berechnung kein Fehler auftritt ! wahr, wenn bei der Berechnung ein Fehler auftritt Natürlich kann es auch hier einen ".else"-".endif"-Zweig geben. Auch hierzu noch zwei Beispiele .if !,Marke wahr, wenn "Marke" nicht definiert .if =,1/0 falsch, da 1/0 nicht berechnet werden kann .INCLUDE Der ".include"-Pseudo-Opcode dient vordergründig dazu, eine Datei in den laufenden Quelltext ab der Position des Pseudo-Opcodes einzubinden um nach Beendigung der Assemblierung dieser Datei mit der Zeile nach dem Pseudo-Opcode fortzufahren. Dabei kann innerhalb einer solchen Includedatei eine weitere Datei eingebunden werden, da maximal 32 Verschachtelungen möglich sind. Der Verwendungszweck des Pseudo-Opcodes liegt aber weniger in der Auslagerung von Quelltextteilen auf Disk oder Festplatte aufgrund von Speichermangel, da dieser auf dem Amiga seltener auftritt und außerdem von CrossAss sowieso nur immer die aktuell zu assemblierende Zeile im Speicher gehalten wird. Es ist aber doch wohl so, daß bestimmte Labels, Makros oder gar Quelltextteile immer wieder verwendet werden (Hardwareregister, Betriebssystemaufrufe etc.). Was liegt also näher, als deren Definitionen, anstatt sie jedesmal neu an den Quelltext anzufügen, einfach in eine Datei auszulagern, die man dann nur noch bei Bedarf hinzu "includen" muß. So kann man sich eigene Bibliotheken erstellen, die bei Gebrauch immer zur Verfügung stehen. Später wird noch einmal genauer auf solche Bibliotheken eingegangen, da mit ihnen äußerst effizient gearbeitet werden kann, wenn man sich an einige wenige Regeln hält. Außerdem sind bereits einige vorgefertige Bibliotheken Bestandteil des 6510-Developers-Packet. Der ".include"-Pseudo-Opcode erhält daher als einzigen Parameter den Dateinamen plus Pfad der einzubindenden Datei. Sol- lte sich der ".include"-Pseudo-Opcode in einem nichtassemblierten Bereich befinden ("falscher" Zweig einer ".if"-".else"-".endif"-Konstruktion), so wird auch nichts eingebunden. .APPEND Der ".append"-Pseudo-Opcode steht wegen der geringen Verwendungs- wahrscheinlichkeit am Schluß. Er dient zum Anhängen einer weiteren Quelltextdatei an das Ende der aktuell bearbeiteten. Der einzige Fall (den ich mir vorstellen kann), bei dem dies nötig wird, ist, daß bei einem Diskettensystem ein Quelltext nicht mehr ganz auf Disk Platz findet (also größer als 880 KByte ist!) und deshalb auf einer anderen Disk und entsprechend in einer anderen Datei fortgeführt werden muß. Im Gegensatz zu ".include" wird dabei immer (auch wenn ".append" in einem nicht zu assemblierenden Bereich steht) mit der als Parameter des ".append"- Pseudo-Opcodes angegebenen Datei fortgefahren und nicht mehr zurückgekehrt, da CrossAss erwartet, daß nach ".append" der alte Quelltext zu Ende ist. Zum Schluß der Erklärung des Quelltextaufbaus sollen nun noch einige Worte zum Thema "Includebibliotheken" fallen. Bei richtigem Einsatz können diese Bibliotheken zu einem mächtigen Werkzeug werden, das man als Programmierer bald nicht mehr missen möchte. Zunächst sollte jedoch ein einheitlicher Aufbau der Includebibliotheken vereinbart werden, sofern man keine bösen Überraschungen erleben will. Außerdem können, wenn diese wenigen Regeln eingehalten werden, Includebibliotheken leicht weitergegeben werden. Zu Beginn einer Bibliothek sollte ein Kommentarskopf stehen, der kurz den Inhalt der Bibliothek umreißt. Die Bibliothek sollte natürlich auch nur das enthalten, was der Kopf (und ihr Dateiname!) verspricht. Ein zu Beginn der Bibliothek definiertes Dummy-Label soll als Prüflabel dienen. Bei größeren Bibliotheken, die andere Bibliotheken, von denen sie abhängig sind, einbinden, kann es leicht geschehen, daß durch Mehrfacheinbindungen, Labels oder Makros doppelt zu definieren versucht werden. Wird vor der Einbindung geprüft, ob das der Bibliothek gehörende Dummy-Label bereits existiert, so kann dies verhindert werden. Das Label sollte daher einen markanten, aussagekräftigen Namen besitzen. Sein Wert ist egal, da nur die Tatsache des "definiert Seins" entscheidet. Bei Bibliotheken, die Makros enthalten, sollte besonders gut kommentiert werden, was welches Makro bewirkt. Insbesondere gilt dies auch für die Übergabeparameter (wenn vorhanden). Vor einer Fehlerquelle soll jedoch gewarnt werden: Das Prinzip des 2-Pass-Assemblers verbietet, daß eine bedingt eingebundene Datei auch in Pass 2 eingebunden und damit assembliert wird. Bei reinen Label- oder Makrobibliotheken ist dies nicht weiter schlimm, wohl aber, wenn die Bibliothek echten Quelltext enthält. Dieser wird bei Pass 2 einfach vergessen ! Quelltext (direkt zu assemblierenden Quelltext, nicht Makroquelltext) enthaltende Bibliotheken dürfen deshalb NIE bedingt einge- bunden werden. Sie sollten von anderen Bibliotheken getrennt (am besten in verschiedenen Verzeichnissen) aufbewahrt werden. Das 6510-Developers-Packet umfaßt bereits fünf fertige Includebiblio- theken: Basic-ROM.inc Kernel-ROM.inc SID.inc CIA.inc VIC.inc Basic-Start.inc "Basic-ROM.inc" und "Kernel-ROM.inc" enthalten die Einsprungsadressen der Betriebssystemroutinen des C64 mit deren Abkürzungen. VIC.inc und SID.inc enthalten die Adressen der Register des VIC bzw. SID, während CIA.inc nur die Offsets der CIA-Register und die Basisadressen der beiden CIAs enthält, welche entsprechend addiert werden müssen. Alle Bibliotheken halten sich an die obigen Regeln, die als Prüflabel gedachten Dummies sind die jeweils als erstes definierten Labels. Die Includedatei "Basic-Start.inc" enthält gegenüber den anderen Dateien quelltextmodifizierende Anweisungen und darf deshalb, wie oben erklärt, NIE bedingt eingebunden werden! Die Datei bietet komfortable Möglichkeiten, einen Basic-Kopf an ein Assemblerprogramm zu binden (so ein Programm kann dann ganz normal mit LOAD "....",8 geladen und mit RUN gestartet werden). Da die Includedatei komplizierter zu handhaben ist, befindet sich in ihrem Kommentarskopf eine ausführliche Verwendungsbeschreibung. Wie die Datei gehandhabt wird, kann außerdem am Beispiel des Source-Codes von "Transfer" ersehen werden. Die Namensendung ".inc" ist bei Includedateien übrigens nicht zwingend. Wie Dateien heißen, ist CrossAss im allgemeinen egal. Trotzdem sollte hier eine gewisse Ordnung eingehalten werden. Ein reibungsloser Assembliervorgang wird Ihnen dies lohnen, wenn solche Regeln allgemein eingehalten werden. Lassen Sie deshalb alle Quelltextdateien auf ".src", alle Objektcodedateien auf ".obj", alle Includedateien auf ".inc" und schließlich alle Konvertierungsdateien (wird später erklärt) auf ".con" enden und legen Sie sie separat in verschiedene Verzeichnisse. Der Amiga bietet die Möglichkeit der Zuweisung logischer Laufwerke. Das bedeutet, daß einem bestimmten Pfad ein Name zugewiesen wird, über den dieser Pfad dann, wie ein neues Laufwerk, angesprochen werden. Unter Amiga-DOS übernimmt dies der "Assign"-Befehl. Gerade diese Möglichkeit kann bei der Arbeit mit CrossAss vieles erleichtern. Wird auf jedem System, auf dem CrossAss verwendet wird, den einzelnen Verzeichnissen, die Quelltexte, Objektcodes etc. enthalten, ein logisches Laufwerk mit jeweils einheitlichem Namen zugewiesen, so kann jeder Benutzer ohne Probleme auf diese Laufwerke zugreifen und wird immer im richtigen Pfad landen. Folgende logische Laufwerke sollten daher auf jedem System die angegebenen Pfade repräsentieren: SOURCE: Pfad des Verzeichnisses, das Quelltexte enthält OBJECT: Pfad des Verzeichnisses, in das Objektcodes gelangen INCLD: Pfad des Verzeichnisses, in dem sich alle Include- Dateien befinden CONVERT: Pfad des Verzeichnisses, in dem sich alle Konver- tierungstabellen befinden Gerade die Arbeit mit Bibliotheken wird erst hierdurch richtig möglich, da nun ganz global auf eine Bibliothek mit "INCLD:Bibliothekname.inc" zugegriffen werden kann, ohne sich Sorgen zu machen, wo sich die Dateien eigentlich tatsächlich befinden. Die für die Laufwerke notwendigen "assign"-Kommandos sollten Sie sich entweder in Ihre User-Startup setzen, oder in ein IconX-Script, das Sie vor dem Start von CrossAss aufrufen. Nachdem nun alles zum Aufbau des Quelltextes gesagt ist, kommen wir zur Bedienung des Programms selbst. CrossAss integriert sich in die Benutzeroberfläche des Amiga, so daß die Bedienung des Programms für keinen Amiga-Anwender zum Problem werden sollte. Obwohl CrossAss auch unter Kickstart 1.3 arbeitet, sollte die Verwendung unter Amiga-OS2.0 vorgezogen werden, da CrossAss dann den Filerequester der "asl.library" benutzen kann. CrossAss besitzt selbst keinen Filerequester. Unter Kickstart 1.3 erfolgt die Dateinameneingabe über Tastatur. Nach dem Start von CrossAss erscheinen zunächst zwei sich überlagernde Windows, wobei das obere Window hauptsächlich nur eine Copyright-Erklärung enthält und deshalb durch Klicken seines Close-Gadgets sofort geschlossen werden kann. Das Copyright-Window gibt aber auch einige Informationen über von CrossAss belegte Speicherbereiche, auf die jetzt genauer eingegangen werden soll. CrossAss belegt für seine Arbeit drei Speicherbereiche, deren Größe von Ihnen bestimmt werden kann. Die Speicherbereiche tragen die Namen Labeltabelle, Makrotabelle und Makrospeicher. Die Labeltabelle bzw. Makrotabelle enthalten, wie ihre Namen ja aussagen, alle wichtigen Daten der Labels bzw. Makros. Ihre Größe entscheidet darüber, wieviele Labels bzw. Makros überhaupt verwendet werden können. Der Unterschied zwischen Makrotabelle und Makrospeicher ist der, daß in der Makrotabelle alle Daten über die Makros, im Makrospeicher jedoch die Makroquelltexte selbst gespeichert werden. Makrotabelle und Makrospeicher müssen also immer in einem vernünftigen Verhältnis zueinander stehen, denn was nützt eine große Makrotabelle, wenn die Quelltexte der Makros nicht im Makrospeicher Platz finden? Nach Schließen des Copyright-Windows wird das gesamte Statuswindow von CrossAss sichtbar. In diesem Fenster werden laufende Ausgaben während der Assemblierung gemacht und alle Fehlermeldungen angezeigt. Hier können Sie Länge, Anfangs- und Endadresse des Objektcodes ablesen, die Dateinamen der aktuellen Quelltext- bzw. Objektcodedateien, die Anzahl der definierten Labels und Makros, die aktuell assemblierte Zeile im Quelltext (neben "Zeile:"), die Anzahl der bisher insgesamt assemblierten Zeilen (neben "Zeilen insg."), sowie last but not least ob ein Fehler aufgetreten ist bzw. was CrossAss gerade tut. Das Window besitzt zwei Gadgets, darunter ein Close-Gadget, mit dem das Programm beendet werden kann. Das zweite Gadget trägt die Aufschrift "Start" und dient zum Starten der Assemblierung. Wird es betätigt, erscheint je nach Ihrer Amiga-OS-Version der asl-Filerequester oder ein Stringgadget zur Dateiauswahl. Wird der Auswahlvorgang durch das am jeweiligen Eingabefenster befindlichen Close-Gadget abgebrochen, erfolgt natürlich keine Assemblierung. Ansonsten ändert sich die Aufschrift des "Start"-Gadgets in "Stop" und die Assemblierung beginnt. Während der Assemblierung wird zyklisch der Fensterinhalt des Statuswindow erneuert, laut (änderbarer) Voreinstellung nach jeweils 100 assemblierten Zeilen. Wird die Assemblierung nicht durch das "Stop"-Gadget abgebrochen, endet sie entweder, wenn CrossAss einen Fehler entdeckt oder das Ende des Quelltextes erreicht und die Assemblierung somit erfolgreich war. Während der Assemblierung kann die Anzeige "Quelldatei" in "akt. Makro" wechseln, nämlich dann, wenn CrossAss einen Makroquelltext assembliert. Da CrossAss sich nicht die Datei und Position des Makroquelltextes merkt, sondern diesen im Makrospeicher hält, erfolgt die Anzeige auf diese Weise. Die Anzeige "Zeile" beginnt dabei bei 0, so daß bei einem Fehler die angegebene Zeile, die fehlerhafte Zeile im Makroquelltext darstellt. CrossAss besitzt eine eigene Menüleiste mit einer ganzen Reihe weiterer Funktionen, die nun im einzelnen erklärt werden sollen. Menü "Datei", Menüpunkt "Übertragen" Wird der Menüpunkt aufgerufen, erscheint wieder der der OS-Version entsprechende Filerequester zur Auswahl einer Objektcodedatei. Wurde zuvor bereits assembliert, erscheint der Namen der zuletzt erzeugten Datei automatisch, so daß, wenn kein anderer gewählt werden soll, direkt fortgefahren werden kann. Nach der Auswahl beginnt die Übertragung. Amiga und C64 müssen dabei mit dem Parallelkabel verbunden sein und eine der beiden Empfangsprogramme auf dem C64 gestartet worden sein. Die Übertragung läßt sich nicht abbrechen, da die Kontrolle während der ganzen Übertragung an das Amiga-Betriebssystem übergeben wird. Sollte die Verbindung nicht stimmen, hängt der Computer und gibt CrossAss die Kontrolle nicht mehr zurück. Prüfen Sie deshalb vorher die Verbindung. Trotzdem: Verbinden Sie die Computer NIEMALS in eingeschaltetem Zustand!! Menü "Datei", Menüpunkt "Information" Sollten Sie einmal das starke Bedürfnis haben, den Namen des Authors oder die Copyrightmeldung zu lesen, dann sollten Sie diesen Menüpunkt aufrufen. Es erscheint das Copyright-Window, wie nach dem Start von CrossAss. Menü "Datei", Menüpunkt "Ende" Dieser Menüpunkt beendet CrossAss. Menü "Tabellen", Menüpunkt "Labels" Über das "Tabellen"-Menü macht CrossAss dem Benutzer seine internen Tabellen zugänglich. Über den "Labels"-Menüpunkt, kann CrossAss' Label- tabelle sichtbar gemacht werden. Sie erscheint (wie alle Tabellen des "Tabellen"-Menüs") in einem eigenen Window. Die Labels werden in einem Kasten dargestellt, der max. 15 Einträge enthalten kann. Durch Betätigen der Pfeilgadgets oder durch Benutzen des Rollbalkens kann der Inhalt des Kastens gerollt werden. Die Labels erscheinen mit ihren Werten in dezimaler und hexadezimaler Form, wie im Tabellenkopf über dem Kasten angegeben. Durch das Close-Gadget kann das Tabellen-Window geschlossen werden. Sollten bei der Anzeige plötzlich mehrere Labels gleichen Namens mit verschiedenen Werten angezeigt werden, so handelt es sich hierbei um lokale Labels, die in Makros definiert wurden. Menü "Tabellen", Menüpunkt "Makros" Entsprechend dem "Labels"-Menüpunkt erscheint hier der Inhalt der Makrotabelle. Die Makros werden mit Namen, Länge im Makrospeicher und ihrer Übergabeparameteranzahl angegeben. Menü "Tabellen", Menüpunkt "Blöcke" Die Möglichkeit des Objektcodesplittings durch CrossAss in verschiedene Objektcodedateien bietet zahlreiche Vorteile. Allerdings wird im Status- window nur der aktuelle Objektcodedateinamen angezeigt. Über den Menüpunkt "Blöcke" können alle Objektcodedateinamen inkl. Start-, Endadresse und Länge sichtbar gemacht werden. Allerdings merkt sich CrossAss nur 32 Objektcodedateinamen, was wohl völlig ausreicht. Der ".base"-Pseudo-Opcode kann aber dennoch mehr als 32 mal im Quelltext verwendet werden. Menü "Tabellen", Menüpunkt "Mnemonics" Für seine internen Arbeiten benötigt CrossAss eine Tabelle, die zusätz- lich zu den Namen der einzelnen Mnemonics auch ihre Adressierungsarten angibt. Über den "Mnemonics"-Menüpunkt wird dem Programmierer diese Tabelle, sozusagen als besonderer Service, zugänglich gemacht. Besonders bei sehr optimierter Programmierung, kann man leicht in die Versuchung geraten, z.B. ein ASL-Mnemonic mit Zeropage-Y-indizierter Adressierung zu verwenden, weil dies eben so gut passen würde, obwohl es der 6510 nicht ermöglicht. Die Tabellenkopf enthält Abkürzungen für die einzelnen Adres- sierungsarten. Je nachdem, ob ein Mnemonic eine Adressierungsart kennt, steht in dieser Spalte ein Stern ("*"). Die einzelnen Abkürzungen sind: IM : immediate AB : absolut AX : absolut x-indiziert AY : absolut y-indiziert ZP : Zeropage ZX : Zeropage x-indiziert ZY : Zeropage y-indiziert ID : indiziert indirekt DI : indirekt indiziert RE : relativ IN : indirekt (nur JMP-Mnemonic) AI : Akkumulator/implement (wird vom Assembler nicht unterschieden) Menü "Einstellungen", Menüpunkt "Ändern" Das "Einstellungen"-Menü enthält alle Funktionen, die mit der Vorein- stellungsdatei "s:CA.prefs" zu tun haben. Diese Datei wird beim Start von CrossAss geladen und enthält alle vom Benutzer gewünschten Eingaben. Ist diese Datei noch nicht vorhanden, wie z.B. beim allerersten Start von CrossAss, so wird sie, mit den Grundeinstellungen gefüllt, erzeugt. Nach Aufruf des Menüpunkts "Ändern" erscheint das Einstellungswindow von CrossAss mit insgesamt vier String- und zwei Boolean-Gadgets, mit denen die Einstellungen manipuliert werden können. Eines der beiden Boolean-Gadgets trägt die Aufschrift "OK". Bei Betätigung schließt das Einstellungsfenster und die neuen Einstellungen werden aktiv. Wird das Fenster durch das ebenfalls vorhandene Close-Gadget geschlossen, werden die aktuellen Veränderungen vergessen und die vor Aufruf des "Ändern"-Menüpunkts aktiven Einstellungen zurückgeholt. Die ersten drei String-Gadgets dienen zur Größenbestimmung von Label-, Makrotabelle und Makrospeicher. Für die Größe der Label- und Makrotabelle muß die Anzahl der Labels bzw. Makros angegeben werden, die in die Tabellen passen sollen. Rechts daneben wird dann angezeigt, wieviel Speicher in Byte diese Anzahl benötigt. Erscheint als Anzeige 0, so übersteigt die Größe der Tabelle den freien Speicher. Die Größe des Makrospeichers muß direkt in Bytes angegeben. Wie bereits erwähnt, sollte man ein vernünftiges Verhältnis zwischen Makrospeicher und Makrotabelle einhalten, so daß der Quelltext der in der Makrotabelle speicherbaren Makros auch wirklich im Makrospeicher Platz findet. Tatsächlich belegt werden die drei von CrossAss benötigten Speicherbereiche übrigens erst bei Beginn der Assemblierung. Das vierte String-Gadget mit der Überschrift "Refresh-Rate" gibt an, wie oft CrossAss während der laufenden Assemblierung den Inhalt des Status- windows erneuern soll. Nach dem eingetragenen Wert von Zeilen wird der Windowtext "refreshed". Sehr kleine Werte ermöglichen ein gutes Verfolgen des Assembliervorganges, bremsen diesen jedoch stark ab, während große Werte Zeit sparen helfen. Das zweite Boolean-Gadget trägt die Aufschrift "Sort". Es bestimmt, ob die Labels nach Beenden oder Abbruch der Assemblierung alphabetisch sortiert werden sollen. Das Gadget ist ein Toggle-Gadget und kann somit als Schalter benutzt werden, um die Sortierung an- oder auszuschalten. Als Sortieralgorithmus wird "Straight Insertion" benutzt (nicht der schnellste, aber doch mehr als doppelt so schnell wie Bubble-Sort). Durch Abschalten der Sortierung kann bei langen Labeltabellen Zeit gespart werden. Die Labels werden dann in der Reihenfolge der Definition angezeigt. Menü "Einstellungen", Menüpunkt "Speichern" Erweisen sich modifizierte Einstellungen als besonders gut, so können Sie sie mit dem Menüpunkt "Speichern" zusammen mit einer evtl. vorher geladenen Konvertierungstabelle (siehe unten) in die Datei "s:CA.prefs" speichern. Die Einstellungen inkl. Konvertierungstabelle stehen dann nach jedem Start von CrossAss sofort zur Verfügung. Menü "Einstellungen", Menüpunkt "Laden" Erweisen sich modifizierte Einstellungen hingegen als unbrauchbar, nachdem Sie bereits das Einstellungswindow verlassen haben, so können Sie mit dem Menüpunkt "Laden" die zuletzt gespeicherten Einstellungen wieder laden. Testen Sie deshalb Ihre Einstellungen, bevor Sie sie speichern. Menü "Einstellungen", Menüpunkt "KonvertTab laden" Mehrmals wurde bereits von der Konvertierungstabelle gesprochen, die CrossAss benutzt, um Texte und Zeichen, bevor sie in den Quelltext eingefügt werden, anzupassen. Dies ist notwendig, da der Amiga und der C64 verschiedene ASCII-Tabellen verwenden, begründet durch ihre verschiedenen Zeichensätze. Die Konvertierungstabelle kann beliebig geändert werden. Da sie jedoch 256 Einträge besitzt, die man dem Benutzer wohl nicht zutrauen kann, jedes mal eintippen zu müssen, wurde die Möglichkeit geschaffen, die Konvertierungsdatei mit einer Beschreibungsdatei zu beschreiben. Eine solche Beschreibungsdatei wird mit Ihrem Texteditor erstellt. Jede Zeile enthält dann zunächst einen Amiga-ASCII-Code und durch mindestens ein Leerzeichen getrennt den entsprechenden C64-Code. Zum Erstellen der eigentlichen Konvertierungstabelle benutzt CrossAss ein Verfahren, das am besten am einem Beispiel verdeutlicht werden kann. Angenommen die Beschreibungsdatei sieht wie folgt aus: 0 10 2 40 5 0 CrossAss liest zunächst die erste Zeile und nummeriert ab dem angegebenen Amiga-Code die Konvertierungstabelle beginnend mit dem C64-Code fortlaufend durch. Diese würde nach der ersten Zeile wie folgt aussehen: Amiga-Code C64-Code 0 10 1 11 2 12 3 13 4 14 5 15 6 16 7 17 usw. Danach wird die zweite Zeile interpretiert und wieder ab dem Amiga-Code durchnummeriert: Amiga-Code C64-Code 0 10 1 11 (bis hier zugewiesen durch Zeile 1) 2 40 (ab hier zugewiesen durch Zeile 2) 3 41 4 42 5 43 6 44 7 45 usw. Mit der dritten Zeile wird genauso verfahren: Amiga-Code C64-Code 0 10 1 11 (bis hier zugewiesen durch Zeile 1) 2 40 3 41 4 42 (bis hier zugewiesen durch Zeile 2) 5 0 (ab hier zugewiesen durch Zeile 3) 6 1 7 2 Mit diesem Verfahren kann auf einfache Weise mit wenigen Angaben eine Konvertierungstabelle erstellt werden. Die Amiga-Codes müssen jedoch immer klein beginnen und dann ansteigen, da sonst vorhergehende Zuweisungen wieder ganz überschrieben werden. Die Zahlen können in allen Zahlen- systemen (8 Bit) angegeben werden, allerdings nicht als Terme. Die Angabe direkt als Zeichen scheidet aus, da diese durch eine noch nicht vollständige Konvertierungstabelle (eben die gerade im Aufbau befindliche) konvertiert würden, was falsche Werte ergäbe. Konvertierungstabellen dürfen und sollten auch Kommentare enthalten. Eine Kommentarszeile muß mit einem Semikolon (";") in der ersten (1.) Spalte beginnen. Auch Leerzeilen benötigen ein Semikolen! Das 6510-Developers-Packet enthält bereits zwei Konvertierungstabellen, die wie vereinbart immer über das logische Laufwerk "CONVERT:" erreichbar sein sollten. "C64.con" wandelt Amiga-ASCII-Codes in C64-ASCII-Codes, während "SCode.con" in C64-Bildschirm-Codes wandelt. Nach Aufruf des Menüpunktes erscheint der dem verwendeten OS ent- sprechende Filerequester zur Auswahl der Beschreibungsdatei. Danach startet die Generierung der Tabelle. Kann CrossAss die Beschreibungsdatei nicht richtig interpretieren, erscheint eine Fehlermeldung. Menü "Drucken", Menüpunkt "Quelldatei" Das letzte der vier Pull-Down-Menüs von CrossAss enthält Funktionen, die zum Ausdruck von Quelltext, Label-, Makro- und Blocktabelle dienen. Der Ausdruck erfolgt dabei im Normalfall nicht direkt auf den Drucker, sondern in eine Textdatei. Dies ist logisch, da ja das Parallelkabel vom C64 zum Amiga den Parallelport belegt, an dem normalerweise ein Drucker angeschlossen ist. Sollte dies nicht der Fall sein (oder sollte Ihr Drucker am seriellen Port angeschlossen sein), kann im jeweiligen Filerequester natürlich auch "prt:" als Dateinamen eingegeben werden. Wird bei den Druckfunktionen eine bereits vorhandene Datei ausgewählt, so werden die auszudruckenden Daten an diese angehängt. Die entstandene Textdatei kann mit einem Texteditor bearbeitet werden. Der Menüpunkt "Quelldatei" dient zum Online-Drucken des Quelltextes mit den erzeugten Opcodes während der Assemblierung. Dies ist nur so möglich, da CrossAss ja jeweils nur die aktuell assemblierte Zeile im Speicher hält und sich den Quelltext nicht merkt. Nach Aufruf des Menüpunktes und der Dateinameneingabe erscheint deshalb nur die Meldung, daß der Quelltextdruck nun eingeschaltet ist. Der Druck erfolgt dann bei jedem Assembliervorgang, solange bis die Funktion durch nochmaliges Auswählen des Menüpunktes wieder abgeschaltet wird. Bei Makrodefinitionen wird nur deren Kopf- und Fußzeile ausgedruckt. Der Quelltext mit angepassten Opcodes wird bei jedem Aufruf des Makros ausgedruckt. Menü "Drucken", Menüpunkt "Labels" Der Menüpunkt "Labels" dient zum Drucken der Labeltabelle in eine Textdatei. Da die Labeltabelle erst nach vollendeter Assemblierung fertiggestellt ist, kann sie erst nach deren Beendigung gedruckt werden. Menü "Drucken", Menüpunkt "Makros" Die Funktionsweise des "Makros"-Menüpunktes entspricht der des "Labels"- Menüpunktes, mit dem Unterschied, daß die Makrotabelle gedruckt wird. Menü "Drucken", Menüpunkt "Blöcke" Die letzte der Druckfunktionen druckt schließlich die vom Objektcode- splitting per ".base" und dem gleichnamigen Menüpunkt des "Tabellen"-Menüs bekannte Blocktabelle aus. Die Bedienung ist dieselbe wie bei den obigen Menüpunkten "Makros" bzw. "Labels". Nachdem alle Funktionen von CrossAss hinreichend ausführlich erklärt worden sind, dürfte einer reibungslosen Arbeit nichts mehr entgegen stehen. Im Anschluß sollten noch einige Hinweise zu den beiden Empfangsprogrammen für den C64 gemacht werden. Die erste Empfangsroutine "Transfer" dient zum direkten Speichern der übertragenen Daten auf Diskette. Sie eignet sich deshalb besonders gut für große Programmprojekte (für die das 6510-Developers-Packet ja geschaffen wurde). Nach dem Start des Programmes mit LOAD "TRANSFER",8 und erscheint eine kleine Titelmeldung und eine Aufforderung zur Eingabe des Dateinamens unter dem die Daten gespeichert werden sollen. Ist eine Datei mit diesem Namen bereits vorhanden wird sie zuvor (und ohne Warnung !) per SCRATCH-Befehl gelöscht, bevor die Daten gespeichert werden. Transfer startet dann die Übertragungsroutine. Daß Transfer von Disk geladen werden muß, sollte auch bei Floppies ohne Speeder nicht stören, da Transfer nur 2 Blöcke auf Disk belegt. Obwohl bei der parallelen Übertragung jeweils 8 Bit übertragen werden erscheint diese bei Transfer relativ langsam. Das liegt jedoch nicht an der Übertragungsroutine, sondern an den langsamen Diskettenroutinen des C64-Betriebssystem. Ein Floppyspeeder schafft hier Abhilfe. Die Übertragung braucht nicht auf beiden Rechnern gleichzeitig gestartet zu werden (was auch nur schwer möglich wäre); die Rechner warten selbstständig aufeinander. Die zweite Empfangsroutine "TransRAM" überträgt die empfangenen Daten nur ins RAM des C64. Sie eignet sich zum schnellen Austesten kleinerer Routinen. Eine Dateinameneingabe ist überflüssig. Die Routine wird mit LOAD "TRANSRAM",8,1 geladen. SYS 53048 startet die Empfangsroutine, die das obere Ende des 4 KByte-Bereich ab 49152 belegt. TransRAM meldet "waiting for data" und sobald die ersten Daten vom Amiga ankommen "receiving from xxxxx". xxxxx ist dabei die von CrossAss mit übertragene Startadresse . Die Übertragung geht dabei durch Fehlen der Diskettenzugriffe wesentlich schneller. Ist die Übertragung beendet, erscheint zusätzlich die Endadresse. Tritt ein Fehler auf, meldet TransRAM "failed". Für solch einen Fehler kann es zwei Gründe geben: 1. Die Startadresse wurde unvollständig übertragen (die Übertragung brach also folglich bereits nach einem Byte ab) 2. Das Highbyte der Adresse, an die die Daten übertragen werden sollen, überstieg den Wert 255 (Speicherende). Beide Empfangsroutinen sind übrigens bereits mit CrossAss entwickelte Programme, die mit einer alten, großteils in Basic geschriebenen Empfangs- routine übertragen wurden. Die Sourcecodes "Transfer.src" bzw. "TransRAM.src" können wie vereinbart über das logische Laufwerk SOURCE: erreicht werden. Zum Schluß noch zur Abrundung der Anleitung noch einige Tips, die sich als hilfreich erwiesen haben: Die Pseudo-Opcode-Konstruktion ".if"-".else"-".endif" erlaubt, wie bereits gesagt keine Kombination von Vergleichsoperatoren. Die beiden häufig benötigten Vergleichsoperatoren größer/gleich ">=" und kleiner/ gleich "<=" lassen sich jedoch einfach erzeugen, indem der Block zwischen ".if" und ".else" leer gelassen wird. Beispiele: .if <,lab,10 ;"Gegenteil" von >= .else lda $2000 ;nur wenn dieses NICHT erfüllt ist, .endif ;wird assembliert. bzw. .if >,lab,10 .else lda $2000 .endif Die Adressierungsart der Zeile jmp (2312+43)-(3+2) würden Sie sicherlich richtig mit "JMP-absolut" beantworten, auch wenn der Term der Adresse geklammert ist. CrossAss ist da anderer Meinung. Die Routinen zur Identifizierung der Adressierungsart beachten aus Geschwindigkeitsgründen nur die äußersten Klammern und erkennen diese als Kennzeichen der Adressierungsart "JMP-indirekt". Da diese Klammern dann zusätzlich noch entfernt werden, gerät alles durcheinander. Um diesem Fehler zu entgehen, gibt es drei Möglichkeiten: 1. Sie vermeiden grundsätzlich Klammern am Anfang und Ende eines JMP-Operanden, so daß CrossAss garnicht auf die Idee kommt, eine indirekte Adressierung zu interpretieren. 2. Sie fügen am Anfang oder am Ende eine Dummy-Rechenoperation hinzu, die nur dazu dient, die Verwechslung auszuschließen: JMP 0+(2312+43)-(3+2) JMP (2312+43)-(3+2)*1 3. Sie verwenden anstatt der runden Klammern eckige Klammern, die nicht an Adressierungsmerkmal verwendet werden dürfen. Eine Zeile JMP [2312+43]-[3+2] wird demnach korrekt assembliert. Ähnliche Probleme gibt es bei der indizierten Adressierung. Hier können verschiedene Fehler auftreten, wie z.B. das CrossAss meldet, das "fehlerhafte" Mnemonic kenne keine indiziert indirekte Adressierung. Die obigen Tricks helfen aber immer. Sollte obige Anweisung wirklich ein indirektes JMP-Mnemonic sein, so müssen übrigens nochmals Klammern gesetzt werden, damit die Termberechnung nach entferntem Adressierungsmerkmal nicht durcheinander gerät: JMP ((2312+42)-(3+2)) Fehler, die scheinbar in Makroquelltexten auftreten, müssen nicht immer am Makroquelltext liegen. Oftmals ist ein falscher Übergabeparameter schuld. Übergabeparameter können intensive Auswirkungen haben, sie bestim- men teilweise sogar die Adressierungsart von Mnemonics. Als Beispiel sei die einfache Zeile "lda ?0,y" dienen. Wird ein 8-Bit-Übergabeparameter übergeben, bricht der Assembler ab, da das LDA-Mnemonic keine Zeropage-y-indizierte Adressierung kennt. Abhilfe: "lda !?0,y". Mit dem 6510-Developers-Packet Programme für andere Rechner mit dem 6510 zu entwickeln dürfte keine große Schwierigkeit sein. Besonders einfach ist dies natürlich für den großen Bruder des C64, den C128 zu realisieren, da dieser ja über den C64-Modus verfügt. Programme für ihn werden mit CrossAss assembliert, im C64-Modus übertragen und auf Disk gespeichert und im 128-Modus dann geladen und gestartet. Für andere Rechner (Atari 800XL/130XE, Apple II, alte CBM-Rechner etc.) müsste sowohl eine Hard- waregrundlage (Übertragungskabel) als auch eine Empfangsroutine geschrieben werden, um mit dem 6510-Developers-Packet arbeiten zu können. Vielleicht findet auch hier jemand Möglichkeiten. Zum Schluß bleibt mir nur noch zu hoffen, daß das 6510-Developers-Packet für viele Programmierer ein nützliches Werkzeug wird, das ihre Arbeit erleichtert. Einige Begriffserläuterungen: ASSEMBLIERUNG / ASSEMBLER: Genaueres unter -> Quelltext. LABEL (= Symbol): Alphanumerische Zeichenkette, die im -> Quelltext einen ihr zugewiesenen Wert vertritt. Der Labelname muß mit einem Buchstaben beginnen und darf bis zu 32 Zeichen lang sein, wobei dann auch Ziffern und das Zeichen "_" im Namen enthalten sein dürfen. Die Wertzuweisung erfolgt entweder explizit per -> Pseudo-Opcode ".equal" oder durch Angabe am Zeilenanfang. In letzterem Fall wird dem Label der aktuelle Wert des -> Programmcounters zugewiesen. Die Groß- und Kleinschreibung wird bei Labels beachtet, so daß sowohl ein Label "Marke" als auch ein Label "marke" oder "MARKE" existieren kann, die alle unterschiedliche Werte repräsentieren. MAKRO: Alphanumerische Zeichenkette, die im Quelltext einen ihm zugewiesenen Makroquelltext vertritt. Das Makro wird mit den Pseudo-Opcodes ".macro" und ".endmacro" definiert. Zur Namensgebung gelten dieselben Regeln wie bei Labeldefinitionen. Das Makro kann je nach Definition bis zu 10 Über- gabeparameter erhalten, die nur innerhalb des Makroquelltextes bekannt sind. Innerhalb eines Makros definierte -> Labels gelten als lokale Labels und sind nur innerhalb des Makroquelltextes bekannt. MNEMONIC: Mnemonic ist die Bezeichnung für alle standarisierten Befehlskürzel zur Vertretung von Maschinensprachbefehlen (-> Opcodes). Da die Programmierung rein durch Zahlen oder gar Bitfolgen wohl alles andere als einfach wäre, bekommen die einzelnen Befehle Namen, eben die Mnemonics. CrossAss hält sich genau an den Standard des 6510, der von anderen Assemblern bekannt ist. Jedes Mnemonic ist genau drei Zeichen lang. Mnemonics sind nicht "case sensitiv"; es ist also egal, ob Mnemonics groß, klein oder gemischt geschrieben werden. OBJEKTCODE (= Objektprogramm): Die entgültige Bitfolge (oder besser Bytefolge), die das tatsächliche, vom Prozessor verständliche Programm darstellt. Um diese erzeugen zu können benötigt man eben den Assembler. OPCODE (= Operationscode): Ein Mikroprozessor wie der 6510 des C64 versteht eigentlich nur Bitfolgen. Acht solcher Bits zusammen kennzeichnen einen bestimmte Funktion des Prozessors, die dieser ausführt, wenn er bei der Abarbeitung auf diese Bitfolge stößt. Ein Opcode ist also nichts anderes als eine 8-Bit-Zahl, die einen solchen Befehl darstellt. PSEUDO-OPCODE (= Pseudoinstruktion, Direktive): Pseudo-Opcodes sind Anweisungen, die im Gegensatz zu -> Mnemonics den -> Assembler selbst etwas tun lassen und damit die -> Assemblierung an sich beeinflussen. Die Aufgaben der einzelnen Pseudo-Opcodes variieren sehr stark und sind deshalb in der Anleitung einzeln beschrieben. QUELLTEXT (= Quellcode, Quellprogramm, Sourcecode): Ein 6510-Assembler-Programm als ASCII-Textdatei. Um dieses Programm ausführen zu können, muß es erst in die für den Prozessor verständliche Bitfolge (-> Opcodes) umgewandelt werden. Diesen Vorgang nennt man "Assemblierung", das Programm, das diese Arbeit erledigt ist der "Assembler".