Der Virtual Assembler 16 - (c) Manuel Nickschas 1. Was ist der VAss16? 2. Unterschiede zum VAss8 3. Generelle Bedienungshinweise eines Assemblers 3.1 Labelbedienung 3.2 Weitere Besonderheiten eines Assemblers 4. Der Umstieg vom anderen Assemblern auf den VirtualAss, Kompatibilität 5. Die Funktionen des VAss16 5.1 Die Pseudo-Opcodes des VAss 16 5.2 Mit Pfeil Links zu erreichende Funktionen 5.3 Weitere Funktionen des VAss16 6. Das Optionsmenu des VAss16 6.1 Möglichkeiten nach dem Assemblieren 6.2 Memory-Optionen -7. Die Funktionen des Virtual Mon 8. Das Wichtigste auf einen Blick, Tipps 8.1 Support 8.2 Bekannte Probleme 9. Credits, Danksagungen 1. Was ist der VAss16? Der Virtual Assembler 16 ist ein Hilfsmittel zur Erstellung von Assembler-Programmen. Er wurde speziell zur Entwicklung von SuperCPU-Programmen geschrieben. Es ist aber ebenfalls möglich, ihn für herkömmlichen 8-bit-code zu verwenden. Im dem 64k Standard-RAM des c64 belgt er gerade mal vier Bytes (ab $d3f0), alles andere liegt im SuperRAM, welches vorausgesetzt wird. Wo der Vass16 im SuperRAM liegt, kann frei gewählt werden. So ist es möglich, den gesamten zur Verfügung stehenden Speicher zu programmieren. Neben dem Assembler selbst beinhaltet der VAss16 auch einen Monitor. Der Virtual Mon kann den gesamten c64-Speicher samt SuperRAM darstellen und bearbeiten. Er besitzt alle wichtigen Grundfunktionen eines Monitors. Wer allerdings noch mehr komfort sucht, sei auf den DreaMon (GO64! 7/2000) verwiesen. 2. Unterschiede zum Virtual Assembler 8 Selbst wenn man den Virtual Assembler 16 für herkömmlichen 8-bit-code verwendet, bietet er etliche Vorteile. Der Virtual Assembler 8 war eigentlich wie ein TurboAss mit REU-Unterstützung. Nur unterstützte der VAss8 neben der REU auch das SuperRAM und erlaubte längeren Sourcecode und mehr Labels. Leider war der VAss8 nie völlig ausgereift und wie bei anderen C64-Assemblern musste man immer aufpassen, dass man nicht aus versehen den Assembler oder Sourcecode überschreibt oder einige Zeropage-adressen nicht richtig resettet, bevor man zu dem Assembler zurückkehrte. Der Virtual Ass 16 geht da einen ganz anderen Weg. Er existiert kaum in den eingebauten 64k, deswegen ist es nur schwer möglich, ihn überhaupt zu überschreiben. Über Reset-Routinen braucht man sich auch keine Gedanken mehr machen, der Virtual Ass 16 wird immer einwandfrei funktionieren, da er alles, was er braucht, selbst resettet. Die Darstellung des VAss16 kann verschieden gewählt werden. Bis zu 62 Zeichen pro Zeile können angezeigt werden. Auch im 40-Zeichen-Modus können sie durch scrollen genutzt werden. Ausserdem besitzt der VAss16 einen integrierten Monitor. Der Virtual Mon kann den gesamten (SuperRAM-)Speicher darstellen. Der VirtualAss 16 hat einige neue Funktionen und beherrscht natürlich auch SuperCPU-opcodes perfekt. 3. Generelle Bedienungshinweise eines Assemblers Dieses Kapitel ist speziell denjenigen gewidmet, die bisher mit einem Monitor gearbeitet haben. Vielen fällt es leichter, die Bedienung eines Monitors zu erlernen als mit einem Assembler umzugehen. Ein Umstieg ist nicht leicht, aber er lohnt sich in jedem Fall. Wer sich erstmal mit Assemblern angefreundet hat, holt die verlorene Zeit schnell wieder ein. 3.1 Labelbedienung In einem Monitor sucht man sich einen Speicherbereich aus, und schreibt seinen code an diese Stelle. Wenn man dann später merkt, dass der code mittendrin geändert werden soll oder an ganz anderer Stelle besser gelegen wäre - dann beginnt die Knochenarbeit, seinen eigenen code zu "relocaten". Erstmal muss der code Befehl um Befehl umgelegt werden und dann stimmen immer noch die ganzen Sprungbefehle nicht. Alles muss per Hand angeglichen werden. Ein Assembler nimmt dem Programmierer diese arbeit vollkommen ab. Auch einem Assembler muss mitgeteilt werden, wo der code später liegen soll. (einfach *=$adresse vor den code schreiben) Danach kann in gewohnter weise programmiert werden. Aber wie soll man auf eine bestimmte Speicherstelle zuspringen oder selbstmodifizierenden code verwenden? Dafür gibt es sogenannte Labels, die anstelle von konkreten Speicherstellen verwendet werden. Sie können fast beliebig benannt werden. Ein Monitor-code dieser Art... $C000 lda #$01 $C002 jmp $C000 sieht jetzt so aus... *=$c000 label lda #$01 jmp label (Ein label wird immer durch das definiert, was dahinter steht. Dabei macht es nichts aus, wie viel Platz dazwischen frei bleibt. Es geht also auch: *=$c000 label lda #$01 jmp label Das label zeigt immernoch auf das lda #$01) Der Vorteil wird schnell erkennbar, wenn der code verschoben werden soll: $A000 lda #$01 $A002 jmp $C000 Der jmp muss per hand umgeändert werden. *=$a000 label lda #$01 jmp label Der jmp zeigt automatisch auf eine andere adresse, obwohl der Benutzer nur eine Zeile geändert hat. Es können immer noch konkrete Adressen verwendet werden: lda #$01 sta $d000 Aber empfehlenswert ist es, auch diese als Labels zu deklarieren: sprite0 = $d000 lda #$01 sta sprite0 Diese Methode ist vor allem für Variablen wichtig. Es kann immer mal sein, dass man feststellt, dass gerade die Speicherstelle, die man als Variable benutzt, anderswo gebraucht wird. Dann braucht man nur das Label auf eine andere adresse zu deklarieren - und im gesamten code wird jetzt die neue adresse verwendet. Befehle wie BEQ/BNE/BMI usw sind relative Sprungbefehle, d.h. sie gehen immer von ihrer eigenen relativen Lage aus. Sie springen zum Beispiel von sich aus gesehen 4 Bytes nach hinten. Das hat zur Folge, das sie, wenn man sie im Monitor verschiebt, immernoch richtig gesetzt sind - sie müssen nicht, wei ein jmp geändert werden. ... $c008 sta $3000,x $c00c inx $c00d bne $c008 Wenn man aber einen Befehl einfügt, muss man den bne doch ändern: ... $c008 sta $3000,x $c00c sta $3100,x $c00f inx $c010 bne $c00c Der bne springt immernoch genau 4 Bytes nach hinten - aber jetzt stimmt das nicht mehr. Er muss geändert werden, damit auch das sta $3000,x innerhalb der Schleife liegt. Beim Assembler ist das anders: loop sta $3000,x inx bne loop Der bne zeigt auf sta $3000,x. loop sta $3000,x sta $3100,x inx bne loop Und immernoch zeigt der bne auf sta $3000,x! In besonderen Fällen ist es aber sinnvol, relative Sprünge einzusetzen. Ein Assembler beherscht diese natürlich auch, sogar bei jmps. jmp * Das "*" steht für die aktuelle Speicherstelle, an der der Befehl liegt. Ein jmp * zeigt also auf sich selbst - eine Endlosschleife. jmp *+5 lda #$01 sta $3000 Jetzt zeigt der jmp -von sich aus gesehen- 5 Bytes nach vorne. Er zeigt auf das sta $3000. Diese funktionen können überall eingesetzt werden. Also nicht nur für jmp oder beq, sondern auch für daten, labels oder was einem sonst noch einfällt. Ein sta (*-18+$0a)/40 rechnet von der aktuellen Position 18 ab, zählt $0a dazu und teilt das ganze durch 40. Wären die Klammern nicht vorhanden, würde VAss erst $0a durch 40 teilen und dann die anderen Rechenoperationen durchführen. Dieses Beispiel zeigt noch etwas: Der VAss hat keine Probleme mit verschiedenen Zahlenformaten. Es können also Dezimalzahlen, Binärcode und Hexadezimalzahlen wild durcheinander verwendet werden. Eine einfache Zahl ist dezimal. Hat sie ein "$" vor sich, ist sie hexadezimal. Und wenn ein "%" davor steht, ist sie binär. Der VA16 akzeptiert 8,16 und 24-bit Zahlen. Es ist sogar noch mehr möglich. Auch label können zum rechnen verwendet werden. label = $3000 andereslabel = $40 sta label+andereslabel Es wird der Akku nach $3040 geschrieben. Label sind nicht zwingend synonym für eine Speicherstelle. Was sie nachher sind, entscheidet sich eigentlich erst in der Anwendung der label... label = $01 lda label ;liest den Wert ein, der an $01 im Speicher steht. Es ist also ein lda $01. lda #label ;liest den Wert #$01 ein, es ist also ein lda #$01. Das selbe kann auch auf einen 16-bit-Wert angewendet werden. Dafür gibt es zusätzlich "<" und ">"... label = $3000 lda #label ;Hier wird ein 16-bit-wert eingelesen - ein SuperCPU-Befehl. lda #$3000 lda #