Weltsensation: DreaMon, der stärkste SuperCPU Monitor der Welt

Frohe Kunde für alle SuperCPU-Programmierer. Endlich gibt es ein Tool, dass schon bei der Entwicklung eigener Werke die Muskeln der Turbo-Karte spielen lässt. Wir freuen uns, euch einen Monitor präsentieren zu können, der alles auf dem C64 Dagewesene in den Schatten stellt. Dank der hohen Kompatibilität der SCPU können aber auch "reine" C64-Projekte von den Möglichkeiten des DreaMon profitieren. Doch schaut selbst...

von Christoph Thelen

Der DreaMon ist ein leistungsstarker Maschinensprachemonitor für die SuperCPU mit SuperRamCard. Zu seinen Features zählen: verschiedene Freezepoints, ein komfortabler Scrollmodus, flexible Terme, definierbare Funktionstasten und VDC-Support auf einem C128.

Der Start

Nachdem DreaMon gestartet wurde, erscheint ein kleines Menü. Dort kann die gewünschte Position im RAM eingestellt werden. Dabei wird das obere Ende des RAMs vorgeschlagen. Mit den Cursortasten wird der Balken bewegt und mit Return ein Feld zum Editieren angewählt. Dazu stehen die Cursortasten, Home, Clear, Stop und CTRL-Cursortasten (um zum Anfang bzw. Ende zu springen) zur Verfügung. Mit Return wird die Eingabe bestätigt. 'Code' ist die Position des DreaMon. 'Text' ist ein Puffer für Labels und Funktionstastenbelegungen. Der Code darf keine Bankgrenze überschreiten. Zur Kontrolle wird hinter dem Eingabefeld die momentane Endadresse angezeigt. Eine fehlerhafte Eingabe wird natürlich abgefangen. Die Größe des Textpuffers ist variabel. Sie kann von zwei Pages bis zu allem verfügbaren RAM reichen. Eine sinnvolle Größe hängt davon ab, wie viele Label und Makros ihr verwenden wollt. Für den Anfang sollte der angezeigte Vorschlag mehr als ausreichen.

Nachdem alles eingestellt wurde, verschwindet das Men¸ und auf dem Screen erscheint ein 'SYS'-Kommando. Das solltet ihr euch irgendwo notierern, denn damit wird der DreaMon von Basic aus gestartet.

Neue Parameter-Typen

Der DreaMon kennt neben dem üblichen Byte und Word noch andere Parameter-Typen, die hier vorgestellt werden sollen. So haben einige Befehle den Parameter <Range> oder <Bereich> (zum Beispiel der Befehl 'd'). <Range> ist eine Abkürzung für eine Reihe von Möglichkeiten (siehe Tabelle 1).

Ein <Range> definiert einen Speicherblock durch zwei Werte: die Start- und die Endadresse. Beide werden durch ein oder mehrere Leerzeichen getrennt. Natürlich muss die Endadresse größer als die Startadresse sein. Das Standard-Zahlensystem für diese Adressen ist Hex.

Die Befehle

**Assemble** - Mit 'a' kann Code eingegeben werden. Die Syntax lautet:

a <Startadresse> oder a <Startadresse> <Opcode>

Bei der ersten Form wird in der nächsten Zeile noch mal die Adresse angezeigt, hinter der man dann den Opcode eingeben kann. Nachdem eine Zeile eingegeben wurde, wird die nächste Adresse in der folgenden Zeile angezeigt, wo man mit dem Code fortfahren kann:

a c000

a00C000 a9 00 lda #$00

a00C002

Ist der 'follow Rep/Sep'-Modus aktiv, wird die Größe des Akkus und der Indexregister automatisch angepasst, wenn einer dieser Opcodes eingegeben wird. Das M- und X-Flag des Prozessorstatus wird davon nicht verändert. Das Standard-Zahlensystem beim Assemblieren ist Hex.

**Compare** - Vergleicht zwei Speicherbereiche und zeigt die Unterschiede an. Ist der 'Verbose Mode' aktiv, werden neben den Adressen auch die unterschiedlichen Inhalte angezeigt. Die Syntax lautet:

c <Bereich> <Ziel>

<Ziel> ist eine Adresse, die den Anfang des Speicherbereichs angibt, der mit <Bereich> verglichen werden soll. Das Standard-Zahlensystem für alle Parameter ist Hex.

**Disassemble** - Disassembliert einen Bereich. Ist der 'follow Rep/Sep'-Modus aktiv, wird die Größe des Akkus und der Indexregister automatisch angepasst, wenn einer dieser Opcodes auftritt. Das M- und X-Flag des Prozessorstatus wird davon nicht verändert. Die Syntax lautet:

d <Range>

Die Ausgabe des 'd'-Kommandos kann editiert werden.

**Fill** - Füllt einen Speicherbereich mit Bytes. Die Syntax lautet:

f <Bereich> <Pattern>

<Pattern> ist eine Liste von Parametern, die durch Kommata getrennt sind. Sie beschreibt das Bytemuster, mit dem <Bereich> gefüllt werden soll. Sie kann Zahlen beliebiger Länge (Byte, Word, Long) und Strings enthalten, die mit Anführungszeichen gekennzeichnet werden. Beispiel:

Der Speicherbereich von $1000 bis $3000 soll mit dem Text "hallo!", der von einem Nullbyte beendet wird, gefüllt werden.

f 1000 3000 "hallo!",0

Das Standard-Zahlensystem für alle Parameter ist Hex.

**Go** - Verlässt den DreaMon und springt an eine Adresse. Die Syntax lautet:

g <Adresse>

Das Standard-Zahlensystem von <Adresse> ist Hex.

**Hunt** - Sucht nach einer Reihe von Bytes in einem Speicherbereich. Bei einem Treffer wird die Adresse ausgegeben. Die Syntax lautet:

h <Bereich> <Wildpattern>

<Wildpattern> ist dem <Pattern> des Fill-Befehls sehr ähnlich. Es ist eine durch Kommata getrennte Liste aus Zahlen und Strings. Die Zahlen können bis zu drei Bytes lang sein (Byte, Word, Long). Im Hex-, Bin- und Oct-Format kann '?' als Joker in den Zahlen benutzt werden. Beispiel:

h 1000 1100 f?

sucht nach allen Bytes, deren oberes Nibble $f ist.

Bei Dez funktioniert das nicht, da hier die einzelnen Ziffern nicht

eindeutig die benutzten Bits abgrenzen. Weitere Beispiele:

h e000 10000 "basic"

sucht nach dem String 'basic' (wie in der Einschaltmeldung)

h 2300 2600 fff?

sucht nach $fff0 bis $ffff.

h a000 c000 d,"ok",d,0

sucht nach der Basic Meldung 'ok' (die von zwei Returns eingerahmt wird und mit $0 abschließt).

**Ascii und Screencode Dump** - Zeigt den Speicherinhalt eines Bereichs mit Ascii- oder Screencodes an. Die Syntax lautet:

i <Range> für Ascii-Dump

j <Range> für Screen-Dump

Die Anzahl der angezeigten Bytes pro Zeile kann mit dem 'pt'-Befehl eingestellt werden.

**Load** - Lädt ein Programm in den Speicher. Load funktioniert über Bankgrenzen hinaus und hat keine Probleme mit Files, die größer als 64K sind. Während des Vorgangs wird die Adresse angezeigt, bis zu der schon geladen wurde. Die Syntax lautet:

l "name",<Device> [,<Startadresse>]

Soll die Startadresse des Files ignoriert werden, kann mit <Startadresse> die gewünschte vorgegeben werden. Load kann mit der Stop-Taste unterbrochen werden. Das Standard-Zahlensystem für <Device> ist Dez! Für <Startadresse> ist es Hex.

**Ascii und Screencode Memory Dump** - Zeigt den Speicherinhalt eines Bereichs mit Ascii- oder Screencodes und den zugehörigen Hex-Werten an. Die Syntax lautet:

m <Range> für Ascii-Memory-Dump

n <Range> für Screen-Memory-Dump

Die Anzahl der angezeigten Bytes pro Zeile kann mit dem 'pt'-Befehl eingestellt werden.

**Opcode Hunt** - Opcode-Hunt kombiniert Disassemble und Hunt. Es disassembliert jede Adresse und durchsucht danach die Ausgabe. Bei einem Treffer wird die Adresse angezeigt. Ist der 'Verbose Hunt'-Modus aktiv, wird zusätzlich die disassemblierte Zeile angezeigt. Die Syntax lautet:

o <Bereich> <Wildpattern>

<Wildpattern> ist derselbe Parameter wie beim Hunt-Befehl.

**Preferences** - Stellt einige Vorgabewerte ein.

pt <DumpLänge>, <MemDumpLänge>

<DumpLänge> ist die Anzahl an Bytes, die bei den Ascii- und Screendump-Befehlen 'i' und 'j' pro Zeile ausgegeben wird. <MemDumpLänge> ist dasselbe für die Memory-Dump-Befehle 'm' und 'n'. Es können nur so viele Bytes angegeben werden, wie auch auf den Screen passen. Das Standard-Zahlensystem für beide Parameter ist Hex.

**Registers** - Zeigt die Register an: Akku, X, Y, Prozessor Flags, Stack-Pointer, Emulation-Flag, Programmzähler, Direct Page, Data Bank, Programmbank und den Inhalt von $01.  Die Syntax ist:

r

**Save** - Speichert einen Speicherbereich. Save funktioniert über Bankgrenzen hinaus und hat keine Probleme mit Files, die größer als 64K sind. Während des Vorgangs wird die Adresse angezeigt, bis zu der schon gespeichert wurde. Die Syntax lautet:

s "name",<Device>,<Bereich> [,<LoadAdresse>]

Die Standard-Ladeadresse des Files ist der Anfang des Bereichs. Soll das File später an eine andere Adresse geladen werden, kann diese mit dem Parameter <LoadAdresse> bestimmt werden. Save kann mit der Stop-Taste unterbrochen werden. Das Standard-Zahlensystem für <Device> ist Dez! Für alle anderen Parameter ist es Hex.

**Transfer** - Kopiert einen Speicherbereich. Die Syntax lautet:

t <Bereich> <Ziel>

<Ziel> ist eine Adresse, die den Start des Speicherbereichs festlegt, in den <Bereich> kopiert werden soll. Transfer läuft auch dann problemlos, wenn <Ziel> innerhalb von <Bereich> liegt. Das Standard-Zahlensystem für alle Parameter ist Hex.

**Verify** - Vergleicht einen Speicherbereich mit dem Inhalt eines Files. Verify funktioniert über Bankgrenzen hinaus und hat keine Probleme mit Files, die größer als 64K sind. Während des Vorgangs wird die Adresse angezeigt, bis zu der schon verglichen wurde.  Die Syntax lautet:

v "name",<Device> [,<StartAdresse>]

Normalerweise wird das File ab seiner Ladeadresse mit dem Speicher

verglichen. Eine andere Position kann mit <StartAdresse> angegeben werden. Verify kann mit der Stop-Taste unterbrochen werden. Das Standard-Zahlensystem für <Device> ist Dez! Für <StartAdr> ist es Hex.

**Swap** - Tauscht zwei Speicherbereiche aus. Die Syntax lautet:

w <Bereich> <Ziel>

<Ziel> ist eine Adresse, die den Start des Speicherbereichs festlegt, der mit <Bereich> getauscht werden soll. Swap läuft auch dann problemlos, wenn <Ziel> innerhalb von <Bereich> liegt. Das Standard-Zahlensystem für alle Parameter ist Hex.

**Exit** - Verlässt den DreaMon und fährt am momentanen Programmzähler fort.  Die Syntax lautet:

x

**Freezepoints verwalten** - Der Befehl 'z' zeigt, setzt oder löscht Freezepoints.  Mit 'z' ohne weitere Parameter werden alle Freezepoints angezeigt. Ein Freezepoint wird mit

z <Adresse> <JumpTyp> <Aktion> [<Parameter>]

gesetzt. Die genauere Beschreibung der Parameter findet ihr im speziellen Freezepoint-Abschnitt dieser Anleitung. Auch Informationen, wie man FPs beim Assemblieren setzt, sind dort vorhanden. Um einen einzelnen Freezepoint zu löschen, dient die Syntax:

z <Adresse>

Alle Freezepoints können mit

z all

gelöscht werden. Das Standard-Zahlensystem für alle Parameter ist Hex.

Ctrl-Shift-Keys

Einige Funktionen des DreaMon können jederzeit über Ctrl-Shift-Tastenkombinationen aufgerufen werden. In Tabelle 2 sind diese aufgelistet.

Der Videomodus 'VDC2531' nutzt den 80-Zeichenschirm eines C128. Mit den Tastenkombinationen 'CTRL-Shift +' und 'CTRL-Shift -' kann die Zeilenzahl zwischen 25 und 31 variiert werden.

Der 'Super6'-Modus ähnelt 'VDC2531', doch hier wird ein veränderter Zeichensatz benutzt, der nur sechs Pixel hoch ist. So passen mehr Zeilen auf den Screen. Wie viele es insgesamt sind, hängt von eurem (Hardware-) Monitor ab. Die Zeilenanzahl wird wie im 'VDC2531'-Modus variiert. Da beide Modi den VDC-Chip nutzen, sind sie nur auf einem C128 verfügbar!

Scrollmodus

Mit dem Scrollmodus könnt ihr einfach und schnell durch den Speicher blättern. Nach jeder ausgegebenen Zeile wartet der DreaMon auf eine der Tasten aus Tabelle 3. Die Ctrl-Shift-Tasten können ebenfalls benutzt werden.

Standard-Zahlensysteme

Bei den meisten Monitoren ist es üblich, dass man vor jede Hex-Zahl ein '$' setzen muss, obwohl meistens gar kein anderes Format unterstützt wird. DreaMon versteht neben Hex nicht nur Dez, Oct und Bin, sondern hat auch "Standard-Zahlensysteme". Damit ist das System gemeint, das in einer Situation am gebräuchlichsten ist, z.B. Hex beim Assemblieren. Dieses System kann ohne sein zugehöriges Präfix benutzt werden. Statt

a00C000 ldx #$c0

reicht also auch

a00C000 ldx #c0

DreaMon ergänzt das fehlende '$' automatisch. Hex ist fast immer das Standard-Zahlensystem. Ausnahmen sind z.B. die Geräteadresse, wo dezimal der Standard ist. Natürlich kann überall ein anderes System benutzt werden, indem die Zahl einfach mit dem entsprechenden Präfix versehen wird, nämlich "!" für dezimal, "$" für hexadezimal, "%" für binär und "&" für oktal. Weiterhin kann überall, wo eine Zahl erwartet wird, eine beliebige Kombination aus Binär, Oktal, Dezimal und Hexadezimal sowie Labels und Rechenoperationen verwendet werden. Im Kapitel Terme gibt es mehr Informationen dazu.

Label

Alle Labels müssen mit einem '{Pfund}' beginnen. Ein Label wird mit

{Pfund}<LabelName> = <Term>

definiert. Der Labelname darf aus allen Zeichen außer den folgenden bestehen: "+*-/()<>&{Pfeil nach oben}" und Space. Das Standard-Zahlensystem für <Term> ist Hex. Um ein Label wieder zu löschen, muss einfach nur <Term> weggelassen werden. Beispiel: Definiere das Label "border" mit dem Wert $d020.

{Pfund}border = $d020

Definiere das Label "Background" mit dem Wert $d021:

{Pfund}Background = {Pfund}border+1

Lösche das Label "border":

{Pfund}border =

Natürlich können überall die lästigen Spaces weggelassen werden.

Terme

Immer, wenn ein Befehl eine Zahl erwartet, kann sie durch einen Term dargestellt werden. Darin sind die Rechenoperationen aus Tabelle 4 möglich. Beispiele (alle Befehle machen dasselbe):

m c002

m !49152+8>>2

m (8+4)*1000 + 2

Zahlen vergrößern

Beim Assemblieren nimmt DreaMon normalerweise die kleinstmögliche Adressierungsart. Manchmal ist das jedoch nicht erwünscht (z.B. wenn Absolut statt Zeropage benutzt werden soll). Um eine längere Adressierungsart anzugeben, als eine Zahl normalerweise hat, müssen genügend Nullen vor sie gestellt werden. Beispiel ('sta $fb' soll Absolut werden):

a00C000 sta 0fb

Eine Hex Zahl mit 3 Ziffern ist schon zwei Bytes lang. Somit wird Absolut statt Zeropage benutzt. Natürlich würde das genauso gehen:

a00C000 sta 00fb

Aber wer tippt schon gerne sinnlose Nullen?

a00C000 sta 000fb

ist nicht mehr Absolut, da eine fünfstellige Hex Zahl drei Bytes benötigt. Natürlich funktioniert diese Vergrößerung nur mit Zahlensystemen, in denen die Ziffernzahl eindeutig die Anzahl der benötigten Bits angibt. Das ist bei Hex, Bin und Oct der Fall, nicht aber bei Dez. Setzt sich ein Term aus mehreren Zahlen zusammen, behält das Ergebnis die maximale Vergrößerung bei. Labels können auch vergrößert werden.

Bus-Kommandos

Mit dem '@'-Befehl werden Kommandos auf den seriellen Bus gesendet und der Status gelesen. Soll ein anderes Device als das aktuelle benutzt werden, kann das gewünschte mit '#<Device>' direkt hinter dem '@' angegeben werden. Das Standard-Zahlensystem ist dabei Dez. Das zu sendende Kommando kann in Anführungszeichen eingeschlossen werden, obwohl selbst Leerzeichen im Kommando das nicht erfordern. '$' als Kommando zeigt das Directory an. Bevor ein Scratch- oder New-Kommando ausgeführt wird, erscheint eine Sicherheitsabfrage. Beispiele:

@i      -  Initialisiert das aktuelle Device

@$      -  Zeigt das Directory des aktuellen Devices

@#10i   -  Setzt das aktuelle Device auf 10 und initialisiert es

@ #10 i -  dasselbe für Space-Liebhaber

@#9     -  Setzt das aktuelle Device auf 9 und zeigt dessen Status an

@#10$   -  Setzt das aktuelle Device auf 10 und zeigt das Directory

F-Keys

DreaMon kennt mehr als die normalen acht Funktionstasten. Acht zusätzliche können mit der C=- und Ctrl-Taste erreicht werden (siehe Tabelle 5). Alle Funktionstasten außer F16 können mit Makros belegt werden. Um ein Makro aufzunehmen, drückt man zuerst F16. Nun blinkt der Cursor schneller und wartet auf den FKey, der mit dem Makro belegt werden soll. Danach folgt der Makrotext. Die Aufnahme wird mit dem FKey des Makros beendet. Ein Makro kann auch andere Makros enthalten. Beispiel:

F1 soll den Text 'm 478' und Return erhalten.

<F16> <F1> "m 478" <RETURN> <F1>

Makros laufen auch über ein Systemauftauen weiter.

Freezepoints

Ein Freezepoint unterbricht das laufende Programm und springt zum DreaMon. Alle Register der I/O-Chips werden dabei gerettet, so dass in das Programm zurückgekehrt werden kann, als wäre nichts geschehen. Es gibt zwei Typen der Freezepoints, die sich in der Sprungart zum DreaMon unterscheiden (Tabelle 6). Der Speicherinhalt beim Freezepoint wird gerettet und durch den Sprungbefehl ersetzt. Also passt auf, dass ihr nicht über selbstmodifizierenden Code stolpert (eine Lösung dafür findet ihr weiter unten). Nachdem der Freezepoint ausgeführt wurde, wird der ursprüngliche Speicherinhalt wieder zurückgeschrieben. Im DreaMon sieht man also immer den ursprünglichen Speicher. Der Disassemble-Befehl zeigt einen Freezepoint mit einem kleinen Kommentar hinter dem Opcode an. Wird ein Freezepoint erreicht, können vier Dinge geschehen (siehe Tabelle 7). Activate- und Remote-Freezepoints sind nur in Kombination nützlich. Sie werden benötigt, wenn timing-kritische Routinen oder selbstmodifizierender Code gefreezed werden sollen und die anderen Typen nicht funktionieren. Remote-Freezepoints werden nicht in den Speicher geschrieben, wenn sie gesetzt wurden. Sie werden nur vorgemerkt, bis ein Activate-Freezepoint erreicht wird. Nun wird der Remote-Freezepoint in den Speicher geschrieben und kann ausgeführt werden. Der Parameter für beide Typen ist eine Bitmaske, die steuert, welcher Freezepoint aktiviert wird. Bei einem Activate mit Parameter $15 werden alle Remote Freezepoints gesetzt, die entweder die Bits 0, 2 oder 4 gesetzt haben, z.B. einer mit $32.

Freezepoints können auf zwei verschiedene Arten gesetzt werden. Entweder mit dem 'z'-Befehl (Bitte schaut dazu im Abschnitt "Freezepoints verwalten" nach) oder beim Assemblieren oder Editieren von Disassembler-Output. Dazu muss der Opcode an der zu freezenden Stelle einfach durch 'z<Jump Typ><Aktion>' und einem Parameter für Count, Activate and Remote ersetzt werden. Beispiele:

An dieser Stelle soll gefreezed werden:

>00c018 ad 20 d0 lda $d020

Beispiel 1:

>00c018 ad 20 d0 zjn;$d020,

um einen 'normalen' 'JSR'-Typ zu setzen (Das Semikolon nach 'zjn' zeigt das Zeilenende an. DreaMon ignoriert beim Assemblieren alles dahinter)

Beispiel 2:

>00c018 ad 20 d0 zlc12;$d020,

um einen 'zählenden' 'JSL'-Typ zu setzen, der erst beim 18. Mal (=$12) freezet.