ZX-IDE in der Praxis, Tipps & Tricks für ZX81,ZX80

ZX-Team Forum
Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

Re: ZX81 Kontext Unterschied BASIC und ASSEMBLER

Beitrag von PokeMon » 13.08.2013, 22:29

Als Parameter bei RAND kann man wahlweise eine direkte Zahl angeben mit oder ohne '#'. Hier ist es egal, ob der Zahlenwert im Assembler oder BASIC Kontext erscheint, in beiden Fällen wird die ASCII Zahl in eine ZX81 Zahl umgewandelt (Gleitkommazahl mit ZX81 interner Darstellung). Aus diesem Grund ist aber der Offset von 5 (Zeilennummer + Zeilenlänge + REM) nicht mehr automatisch berechenbar. Sonst würde der Offset auch beim Aufruf von ROM Routinen addiert oder bei simplen Berechnungen. Durch doppelte Eingabe der Raute (am Anfang und am Ende der Zahl) wird der Offset jedoch wieder automatisch berücksichtigt:

Code: Alles auswählen

10      REM _asm
LAB1:
        LD BC,0
        RET
LAB2:
        LD BC,1
        RET
        END _asm

20      PRINT USR #10
30      PRINT USR #10#
ZX81BASIC4.gif
ZX81BASIC4.gif (99.86 KiB) 16370 mal betrachtet
Zeile 20 referenziert hier nur die simple Zahl 10 (könnte auch eine komplizierte Berechnung sein), die Doppelraute am Ende bezieht sich auf die Programmzeile 10 und errechnet (in der Annahme dass es eine REM Zeile mit folgendem Code ist) daraus die Startadresse. Das Vergeben eines Labels ist hier nicht zwingend notwendig. Trotzdem ist es empfehlenswert besser mit Labels zu arbeiten, da die Verwendung eindeutiger ist.

Warum ich das so ausführlich mit Beispielen beschreibe hat folgenden Grund:

Der Aufruf von Zeilen und Labels hat sich gegenüber der früheren IDE Version geändert:
Früher:
RAND USR #10 => Code in REM-Zeile 10 ausführen
RAND USR label => Code ab Adresse label ausführen
Neu:
RAND USR #10 => Sprung zu Adresse 10
RAND USR #10# => Code in REM-Zeile 10 ausführen
RAND USR label => label als BASIC Variable LABEL interpretieren
RAND USR #label => Code ab Adresse label ausführen
GOTO #label# => label wird durch die darauf folgende Zeilennummer ersetzt
Vereinfacht kann man auch folgende Regel definieren:
#label nach einem BASIC Kommando stellt immer den Bezug zum Assembler her - ohne Raute wird label als (BASIC) Variable LABEL interpretiert

#10*10 stellt ebenfalls den Bezug zum Assembler her und läßt den Ausdruck berechnen, das können Zahlen oder auch Labels oder Konstanten sein

#label# wird ersetzt durch die BASIC Zeilennummer, die auf label folgt. Dadurch kann man auf Zeilennummern vollständig verzichten und bei GOTO o.ä. die Zeilennummer durch ein vorangestelltes Label automatisch einsetzen lassen

#100# wird ersetzt durch die Adresse der Zeilennummer 100, plus einem automatischen Offset von 5 (wegen Verpacken von Assemblerblöcken in REM statements)
Insofern müsste jemand bereits vorhandene Sourcen abändern, wenn die mit der ZX-IDE laufen sollen. Ich weiß, dass das etwas unglücklich ist, aber das ist der Preis für die Verwendung von BASIC. Insofern habe ich diese Veröffentlichung vorgezogen (vor Fertigstellung der vollen BASIC Version), damit sich die Nutzer frühzeitig umstellen und daran gewöhnen können. Einen anderen sinnvollen Weg sehe ich leider nicht, will man die traditionelle BASIC Schreibweise ohne Einschränkungen aufrecht erhalten.

Ich habe die entsprechenden früher verfassten Tutorials auf die geänderte Schreibweise angepaßt.



Es gibt noch eine zweite wichtige Änderung, die dem BASIC Kontext geschuldet ist und das betrifft Kommentare im Quelltext. Bislang wurden Kommentare immer mit einem Semikolon ';' eingeleitet, der Rest der Zeile also ignoriert und als Hinweis im Editor grau dargestellt. Das ';' ist aber ein wichtiges Zeichen, welches in Verbindung mit PRINT und Zeichenketten häufig verwendet wird. Insofern war ich gezwungen, auch den Kommentarstil zu ändern. Kommentare sollten im C++ Stil mit Doppelschrägstrich '//' eingeleitet werden, dann wird der Rest der Zeile als Kommentar erkannt. Die Verwendung des Doppelschrägstrich explizit in Zeichenketten ist natürlich kein Problem, wie im folgenden Beispiel:

Code: Alles auswählen

10      REM TESTPROGRAMM;

20      REM _asm
LAB1:
        LD BC,0
        RET
LAB2:
        LD BC,1   ; weiterhin zulässiger Kommentar aber nicht sichtbar :-(
        RET       // richtiger Kommentar
        END _asm

30      PRINT USR #LAB1 ; kein Kommentar
40      PRINT "PROGRAMMLAENGE:";#LAB2-LAB1 // richtiger Kommentar
50      PRINT ABC;"//"
So sieht es dann im Emulator aus:
ZX81BASIC5.gif
ZX81BASIC5.gif (100.74 KiB) 16367 mal betrachtet
Die Verwendung des ';' als Kommentareinleitung ist für Assembleranweisungen nach wie vor möglich, jedoch nicht mehr für BASIC Anweisungen. Weil das ';' dort Bestandteil der Anweisung sein kann. Einziger Wermutstropfen ist, dass die Darstellung des "alten" Kommentars nicht mehr in grau im Editor markiert werden kann, sonst müsste der Editor den BASIC oder Assembler Kontext verstehen können. Und das führt dann doch etwas zu weit, finde ich. Den Aufwand habe ich mir erspart. Neue Kommentare (im C++-Stil) werden auch im Editor erkannt und grau markiert. Ein alter Kommentar in Assembleranweisungen muss dann aber nicht korrigiert werden sondern wird weiterhin als Kommentar behandelt (beim Compileren zumindest) - das hält den Änderungsaufwand in Grenzen und so viel BASIC Zeilen wird es noch nicht geben, da ja bisher nur RAND und REM unterstützt wurden. :wink:

Ich hoffe, dass weitere tiefgehende Eingriffe in die Strukturen von FASM nun nicht mehr notwendig sind. :wink:
Zuletzt geändert von PokeMon am 29.08.2013, 23:05, insgesamt 3-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 PRINT, Zahlenformate, Berechnungen

Beitrag von PokeMon » 13.08.2013, 23:38

So es wurde aber auch ein weiteres Statement (PRINT) und die Zahlenwerte und Berechnungsfunktionen des ZX81 implementiert.
Man kann also nun auch mit der ZX-IDE Berechnungen in BASIC schreiben. :wink:
Ich orientiere mich dabei am ZX81 Handbuch und habe nun Kapitel 2 bis 5 vollständig implementiert.
Die weiteren Kapitel bzw. BASIC Anweisungen kommen dann im Laufe der nächsten Wochen.

Code: Alles auswählen

10      REM PRINT UND GANZE ZAHLEN

        PRINT 2+3
        PRINT 2-3
        PRINT 2*3
        PRINT 3/2
        PRINT 2**3
        PRINT 20-2*3**2+4/2*3
        PRINT 20-2*3**2+4/2*3
        PRINT 3*2+2
        PRINT 3*(2+2)
        PRINT #3*2+2
Die Zahlen werden automatisch in das ZX81 Zahlenformat umgewandelt. Die Berechnungen führt der ZX81 aus, in der Reihenfolge wie sie für die Operatoren (+,-,*,/,**) vorgesehen ist. Gegebenenfalls muss mit Klammern gearbeitet werden wie in der vorletzten Zeile (100). Die Zeilennummern werden nicht benötigt, da diese automatisch berechnet werden (Schrittweite siehe AUTOLINE). Die letzte Zeile (110) wird durch Angabe der '#' im Kontext der IDE berechnet (von Assemblerkontext kann man jetzt nicht unbedingt sprechen).
ZX81BASIC6.gif
ZX81BASIC6.gif (101.49 KiB) 16365 mal betrachtet


Das nächste Beispiel wendet sich den Gleitkommazahlen zu. Rein technisch ändert das für den ZX81 nicht viel, da sämtliche Zahlen (auch Zeilennummern und Ganzzahlen) in das Gleitkommaformat umgerechnet wird und auch bei der Ausführung von GOTO oder ähnlichen Statements werden die Gleitkommazahlen in Ganzzahlen (Zeilennummern) umgerechnet. Das hat den Vorteil, dass man auch mit Zeilennummern wilde Berechnungen anstellen kann, auf der anderen Seite dauert die Berechnung natürlich und verlangsamt die Ausführung der BASIC Programme deutlich im Vergleich zum ZX80. :wink:

Code: Alles auswählen

10      REM PRINT UND GLEITKOMMAZAHLEN

        PRINT 2.34
        PRINT 2.34E0
        PRINT 2.34E2
        PRINT 234
        PRINT 2.34E3
        PRINT 2.34E-2
        PRINT 1,2,,3,4
        PRINT 1;2;3;4
        PRINT 5E9-5E9+1
        PRINT 1234567890
Die "echten" Gleitkommazahlen werden durch die IDE anders umgerechnet, spielt aber für das Ergebnis keine große Rolle. Die Mantisse wird mit dem Punkt als Trennzeichen für Nachkommastellen geschrieben und der Exponent ggf. mit E angegeben. Für den ZX81 macht es keinen Unterschied ob man z.B. 234 als Zahl angibt oder 2.34E2 - nur rein optisch für die Anzeige. Die resultierende gespeichert Zahl ist in beiden Fällen gleich, Mantisse ist 6A 00 00 00 und Exponent 88 (internes Zahlenformat des ZX81). Siehe folgenden Listingausschnitt:

Code: Alles auswählen

00B0: [40B9] 00 28 0E 00 F5 1E 1B 1F            PRINT 2.34E2
             20 2A 1E 7E 88 6A 00 00    
             00 76                      
00C2: [40CB] 00 32 0B 00 F5 1E 1F 20            PRINT 234
             7E 88 6A 00 00 00 76       
Das Listing im Emulator:
ZX81BASIC7.gif
ZX81BASIC7.gif (97.27 KiB) 16365 mal betrachtet
Beim Ausführen des Programms fällt auf, dass die Zahlenausgabe gerundet wird. Aus 1234567890 wird 1234567900. Auch kann man mit PRINT mehrere Zahlenwerte angeben und wahlweise durch Komma oder Semikolon zu trennen. Bei Semikolen werden die Ziffern direkt nacheinander geschrieben und die Verwendung des Kommas entspricht festen Tabulatoren auf dem Bildschirm.
ZX81BASIC8.gif
ZX81BASIC8.gif (101.64 KiB) 16365 mal betrachtet
Zuletzt geändert von PokeMon am 14.08.2013, 00:13, insgesamt 1-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 Mathematik, Funktionen

Beitrag von PokeMon » 13.08.2013, 23:57

Auch die mathematischen Funktionen sind implementiert.
Es handelt sich konkret um folgende Funktionen, die für Berechnungen zur Verfügung stehen:
ABS (Absolutwert)
ACS (Arc-Cosinus)
ASN (Arc-Sinus)
ATN (Arc-Tangens)
COS (Cosinus)
EXP (Exponential)
INT (Ganzzahl)
LN (Logarithmus)
PI (Konstante Pi)
RND (Zufallszahl)
SGN (Vorzeichen)
SIN (Sinus)
SQR (Quadratwurzel)
TAN (Tangens)

Hier ein Beispielprogramm mit verschiedenen Funktionen (aus Kapitel 5 entnommen):

Code: Alles auswählen

10      REM PRINT UND FUNKTIONEN

        PRINT SQR 9
        PRINT SQR 2 * SQR 2
        PRINT SQR (2*2)
        RAND 1
        PRINT RND
        RAND 0
        PRINT RND
        PRINT LN 2/LN 10
        PRINT TAN(45/180*PI)
        PRINT INT (RND*6)+1
        PRINT INT(2.9+0.5)
        PRINT INT(2.4+0.5)
        PRINT PI-3.1415
ZX81BASIC9.gif
ZX81BASIC9.gif (102.53 KiB) 16368 mal betrachtet
Und hier das Ergebnis der Berechnungen:
ZX81BASIC10.gif
ZX81BASIC10.gif (95.99 KiB) 16368 mal betrachtet
Die letzte Zeile (140) zeigt, dass die interne Auflösung von PI besser ist als mit PRINT angezeigt. :wink:
Zuletzt geändert von PokeMon am 14.08.2013, 00:13, insgesamt 1-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 Startup Variablen

Beitrag von PokeMon » 14.08.2013, 00:06

Betreffend der Startup Variablen gibt es auch eine kleine Änderung bzw. Erweiterung:

MEMAVL = gibt die verfügbaren Speicherausstattung an bzw. die zu erwartende. Erlaubt sind die angegeben Konstanten.
STARTMODE = kann wahlweise SLOW_MODE(*) oder FAST_MODE(*) sein, der entsprechende Modus ist nach dem Laden des Programms automatisch eingestellt.
DFILETYPE = Kennt jetzt neben COLLAPSED und EXPANDED auch die Option AUTO. Bei Speicherausstattung > 3k (siehe MEMAVL) wird automatisch Expanded gewählt, ansonsten COLLAPSED.
AUTORUN = hat sich nicht geändert, es wird vor die auszuführende Zeile gesetzt. Mit dieser Zeile startet das Programm dann automatisch. Wer kein AUTORUN möchte, kann das Statement weglassen oder nach der letzten BASIC Zeile setzen.

Code: Alles auswählen

format zx81
;labelusenumeric
;LISTOFF

        // hardware options to be set and change defaults in ZX81DEF.INC
        MEMAVL     =       MEM_16K         // can be MEM_1K, MEM_2K, MEM_4K, MEM_8K, MEM_16K, MEM_32K, MEM_48K
                                           // default value is MEM_16K
        STARTMODE  EQU     SLOW_MODE       // SLOW_MODE or FAST_MODE
        DFILETYPE  EQU     AUTO            // COLLAPSED or EXPANDED or AUTO
        STARTUPMSG EQU    'CREATED WITH ZX81-IDE' // any message will be shown on screen after loading, max. 32 chars

        include 'SINCL-ZX\ZX81.INC'        // definitions of constants
;LISTON
10      REM NUR SO ...

        include 'SINCL-ZX\ZX81POST.INC'          ; include D_FILE and needed memory areas
(*) Bezeichnung wurde ab Release 1.71.01e.Z80 geändert von SLOW in SLOW_MODE und von FAST in FAST_MODE da es sonst Kollisionen mit den BASIC Statements SLOW und FAST gibt.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, Variablen, LET

Beitrag von PokeMon » 29.08.2013, 23:50

Die Verwendung von Variablenzuweisungen mit dem LET statement dürfte eigentlich soweit klar sein.
Hier ein Beispiel aus dem Handbuch:

Code: Alles auswählen

        REM VARIABLES TEST
        REM
        LET EGGS=58
        PRINT EGGS
        PRINT EGGS/2
        PRINT COS(EGGS/12)**2
        LET EGGS=61
        PRINT EGGS
        LET MILK=18.5
        PRINT MILK
Damit sind eigentlich alle Rechenoperationen und Ausgaben in Verbindung mit den mathematischen Funktionen, LET und PRINT machbar.
So sieht das Listing dann im ZX81 aus:
tut1.gif
tut1.gif (112.66 KiB) 16293 mal betrachtet
Es gibt allerdings ein Besonderheit, die ich selbst nicht kannte und erst durch das Studium des Handbuchs ans Tageslicht kam. Der ZX81 unterstützt Leerzeichen in Variablennamen. Das so zu realisieren würde den Parser sprengen ohne erkennbaren Mehrnutzen. Ich weiß auch gar nicht, ob das allgemein bekannt ist und in der Praxis genutzt wird. Einzige Bedingung ist, dass der Name der Variable mit einem Buchstaben beginnen muss. Siehe folgendes Listing:

Code: Alles auswählen

        LET M12M=0
        LET 'M 3'=1
        PRINT M3
        PRINT 'M 3'
        CLEAR
Hier wird die Variable M3 definiert, die auch ersatzweise als M 3 (mit Leerzeichen dazwischen) geschrieben werden kann. Die Empfehlung lautet darauf möglichst zu verzichten, es gibt auch keine mir bekannte Programmiersprache, die solche Variablen erlaubt. Interessanterweise verweisen beide Bezeichnungen / Schreibweisen M3 und M 3 auf die gleiche Variable.
tut2.gif
tut2.gif (111.83 KiB) 16293 mal betrachtet
Es ist möglich Leerzeichen bei Variablen mit (einfachem) Hochkomma zu verwenden. Dann wird das wertfrei als String interpretiert. In diesem Zusammenhang unterscheidet die ZX-IDE aber ab sofort zwischen Strings in einfachen Hochkommas und denen mit doppelten Hochkommas (wie bei PRINT). Bei einfachen Hochkommas werden nämlich für das BASIC keine Hochkommas erzeugt. Die doppelten Hochkommas sollten nur in Verbindung mit BASIC und möglichst nur bei PRINT oder direkten String Argumenten verwendet werden:

Code: Alles auswählen

        REM VARIABLES TEST 3
        REM
        CLEAR
        PRINT "HI THERE.I AM YOUR ZX81."
        LET EGGS=61
        PRINT "THE PRICE OF EGGS IS ";EGGS;" NEW PENCE A DOZEN"
        LET A$="DOUBLE GLOUCESTER"
        PRINT A$
tut3.gif
tut3.gif (111.16 KiB) 16293 mal betrachtet
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, Labels als Zeilennummern (GOTO, etc.)

Beitrag von PokeMon » 30.08.2013, 00:08

Das folgende Beispiel enthält einige weitere neue Funktionen für Stringmanipulationen und Auswertungen/Evaluierungen wie LEN, STR$, VAL.

Code: Alles auswählen

        REM MISCELLANEOUS
        REM
.loop:
        PRINT 2*VAL "78.5"
        PRINT LEN STR$(55*30)
        LET X$="17+4"
        PRINT X$;"=";VAL X$
        LET BUTTER=75
        LET YEAST=40
        PRINT BUTTER,YEAST
        LIST #.loop#
        RUN
Die wichtigste Neuerung ist jedoch die Verwendung von Labels als Zeilennummern(ersatz). Sofern man bei BASIC Programmierung auf manuelle Zeilennummern verzichtet und die AUTOLINE Funktion zum Durchnummerieren benutzt, kann man über ein Label die Zeilennummer referenzieren. Das Statement LIST #.loop# referenziert dabei das (lokale) Label .loop: - genau genommen wird die Zeilennummer des auf das Label folgenden BASIC Statements benutzt.
So sieht das ausgeführte Programm dann aus: :mrgreen:
tut4.gif
tut4.gif (114.72 KiB) 16291 mal betrachtet
Überall wo man eine Zeilennummer benötigt, kann man daher alternativ ein Label verwenden. :wink:
Ein weiteres Beispiel mit GOTO:

Code: Alles auswählen

        REM 'NEW IN TOWN ...'
        REM
.loop2:
        INPUT A
        PRINT A,SQR A
        GOTO #.loop2#
        LIST
        CONT
tut5.gif
tut5.gif (111.85 KiB) 16291 mal betrachtet
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, graphische Zeichen, Sonderzeichen, Zeichensatz

Beitrag von PokeMon » 27.11.2013, 19:37

Der ZX81 verfügt über zusätzliche Grafikzeichen, die im normalen Zeichensatz eines PC nicht vorhanden sind (Kapitel 11 des Handbuchs). Um sie dennoch in der ZX-IDE benutzten zu können, muss ein speziell präparierter Font geladen werden (Sinclair.ttf). Die Font Datei ist im Hauptverzeichnis der ZX-IDE und kann entweder durch Doppelklick automatisch im Windows Fontviewer aufgerufen und mit Klick auf Install im System installiert werden oder alternativ manuell in das Verzeichnis Windows/Fonts kopiert werden.
ttfinst.jpg
ttfinst.jpg (42.56 KiB) 16078 mal betrachtet
Danach ist im System (auch in anderen Anwendungen) ein Zeichensatz mit dem Namen "SinclairZX" verfügbar. Hier sind alle Sonderzeichen des ZX81 neben den normalen ASCII Zeichen verfügbar und ein paar wenige Sonderzeichen. Prinzipiell kann man zwar beliebig viele Zeichen in einer TTF Datei definieren, praktisch ist es aber begrenzt auf die Zeichen $00 - $FF wenn man Quelldateien mit Programmcode im Textformat speichern will. Zusätzlich lassen sich bestimmte Bereiche in einer Font Datei nicht oder nur mit Einschränkungen verwenden ($00-$1F sowie $80-$9F).

Aus diesem Grund sind nur ein paar Sonderzeichen und die deutschen Umlaute erhalten geblieben wie das € Zeichen, üöäÜÖÄß, §, ´.
Andere Zeichen im Bereich $A0 bis $FF wurden durch die Grafikzeichen ersetzt, sofern es sich nicht um die eben genannten Zeichen wie Umlaute handelt. Die Zuordnung ist dabei nicht identisch zum ZX81 Zeichensatz sondern wird durch eine Zeichentabelle intern in der IDE umgesetzt.

Dennoch kann man den Zeichensatz grundsätzlich auch in anderen Anwendungen benutzen, die die Auswahl eines Monospaced Font zulassen, z.B. OpenOffice, MS Office, usw. Da es sich um einen TTF Outline Zeichensatz handelt, ist er beliebig in der Größe skalierbar im Gegensatz zu "bitmapped" Fonts, für die pro Schriftgröße separate "Glyphs" erforderlich sind. Ich habe den Font mit FontForge basierend auf dem Consolas Zeichensatz erstellt. Insofern ist die Darstellung des Quellcodes etwas anders als mit einem Zeichensatz wie Courier New. Außerdem wird der Zeichensatz SinclairZX etwas größer dargestellt als der Standard Zeichensatz (Courier New), was auch der Tatsache geschuldet ist, dass die Zeichen nur ab einer gewissen Größe einigermaßen schick aussehen und zu pixelig dargestellt werden wenn sie zu klein sind.
fasmgraph.jpg
fasmgraph.jpg (56.69 KiB) 16078 mal betrachtet
Der Grafik Modus kann mit der Tastenkombination ALT+9 ein- und genauso wieder ausgeschaltet werden. Wer die Darstellung mit dem Font nicht mag oder wem das zu groß ist oder wie auch immer kann nach Nutzung der Zeichen den Modus auch wieder ausschalten und mit dem Standard Font weiterprogrammieren. Einzig die Zeichen werden anders dargestellt in dem Standard Zeichensatz, der die ZX81 Grafikzeichen eben nicht kennt. Beim Laden einer Datei mit Quellcode wird automatisch geprüft, ob Sondergrafikzeichen enthalten sind und falls ja wird der Grafikmodus automatisch aktiviert.

Da die ZX-IDE mehrere Textdateien gleichzeitig öffnen und bearbeiten kann, kann man den Grafikmodus pro Datei bzw. pro Fenster separat einstellen bzw. aktivieren oder abschalten. Das Auswählen der Zeichen erfolgt einfach per Mausclick, ein Klick auf die oben eingeblendete Toolbar auf das entsprechende Zeichen fügt es automatisch an die aktuelle Stelle der Quelldatei (Cursor Position) ein. Das geht recht fix und ist effektiver als irgendwelche kryptischen Tastenkombinationen zu lernen wie beim Original ZX81. Die Zeichen kann man auch im Editor markieren und mit CTRL-C (kopieren) und CTRL-V (einfügen) beliebig im Quelltext kopieren. Außerdem merkt sich die IDE das zuletzt gewählte Zeichen, welches man mit CTRL-R (repeat) wiederholen kann.

So kann man recht effektiv Masken o.ä. in der ZX-IDE definieren.

Hier ein Beispiel, wie die Grafikzeichen im Original und ggf. im Standard Zeichensatz aussehen (normalerweise sieht man in der ZX-IDE nur eine von beiden Ansichten):
fasmgraph2.jpg
fasmgraph2.jpg (30.72 KiB) 16078 mal betrachtet
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, graphische Zeichen, Sonderzeichen, Zeichensatz

Beitrag von PokeMon » 27.11.2013, 21:14

Hier ein simples Programmbeispiel mit einigen PRINT Anweisungen und Grafikzeichen und der Effekt wie es auf dem ZX81 aussieht.
So kann man einfach ein paar Masken generieren mit Copy & Paste und Einfügen von Grafikzeichen und ggf. mit einer Abfrage der Tastatur ergänzen:

Das Programm:
graphpgm.gif
graphpgm.gif (46.36 KiB) 16071 mal betrachtet

und so siehts aus im Emulator:
graphpgm2.gif
graphpgm2.gif (91.63 KiB) 16071 mal betrachtet


Hier ein kleines Programm, um den Zeichensatz auszugeben.
Nicht druckbare Zeichen werden als Fragezeichen oder als TOKEN ausgegeben.

Code: Alles auswählen

2000    REM CHARSET
        LET A=0
2100    PRINT CHR$ A;
        LET A=A+1
        IF A<256 THEN GOTO 2100
        STOP
So siehts dann aus: :wink:
graphpgm3.gif
graphpgm3.gif (104.63 KiB) 16070 mal betrachtet
Das Programm nutzt die Funktion CHR$, gibt also das Zeichen zu einem Wert zwischen 0 und 255. Die Umkehrfunktion lautet CODE und ist auch implementiert.
Zuletzt geändert von PokeMon am 27.11.2013, 23:08, insgesamt 2-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, IF-THEN Konstrukt, Vergleiche, STOP

Beitrag von PokeMon » 27.11.2013, 21:26

Ab der Version 1.71.01g der ZX-IDE wird auch das IF-THEN Konstrukt für Vergleichsoperationen unterstützt.
Generell kann eine beliebige Bedingung oder Größenvergleich in dem IF Teil abgefragt werden, siehe auch Kapitel 10 des Handbuchs).

Die generelle Syntax lautet IF <Bedingung> THEN <Kommando> wobei Kommando ein beliebiges BASIC Statement sein darf.
Folgende Bedingungen können geprüft werden:

Code: Alles auswählen

=   gleich
>   größer als
<   kleiner als
>=  größer gleich
<=  kleiner gleich
<>  ungleich
AND logische UND Verknüpfung
OR  logische ODER Verknüpfung
NOT Invertierung eines Ergebnis oder Wertes


Beispielprogramm:

Code: Alles auswählen

1000    REM NEXT EXAMPLE
        PRINT "NUMBER","BIGGEST SO FAR"
        INPUT A
        LET BIGGEST=A
back:   PRINT A,BIGGEST
        INPUT A

        IF A=BIGGEST THEN PRINT "A=BIGGEST"
        IF A<BIGGEST THEN PRINT "A<BIGGEST"
        IF A>BIGGEST THEN PRINT "A>BIGGEST"
        IF A<=BIGGEST THEN PRINT "A<=BIGGEST"
        IF A>=BIGGEST THEN PRINT "A>=BIGGEST"
        IF A<>BIGGEST THEN PRINT "A<>BIGGEST"
        IF A=1 AND BIGGEST=1 THEN PRINT "A=BIGGEST=1"
        IF A=1 OR BIGGEST=1 THEN PRINT "A=1 OR BIGGEST=1"
        IF NOT A=1 AND NOT BIGGEST=1 THEN PRINT "A<>1 AND BIGGEST<>1"
        IF BIGGEST<A THEN LET BIGGEST=A
        GOTO #back#
        STOP
Das STOP Kommando in der letzten Zeile unterbricht oder beendet das laufende Programm. Eine Fortsetzung ist mit CONT möglich. Die Fortsetzung erfolgt dann an der auf STOP folgenden Programmzeile.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, FOR-NEXT Schleife, STEP, FAST, SLOW

Beitrag von PokeMon » 27.11.2013, 21:43

Die neue Version unterstützt nun auch FOR-NEXT Schleifen (Kapitel 12).
Die Syntax lautet:
FOR <variable>=<startwert> TO <endwert> STEP <schrittweite>
wobei die Angabe der Schrittweite optional ist und der Defaultwert ist 1.


Zwei Beispiele dazu:

Code: Alles auswählen

4100    FOR C=1 TO 5 STEP 3/2
        PRINT C
        NEXT C
        STOP

4200    FOR M=0 TO 6
        FOR N=0 TO M
        PRINT M;":";N;" ";
        NEXT N
        PRINT
        NEXT M
        STOP

Im ersten Beispiel ist eine Schrittweite angegeben (3/2 = 1,5) und im zweiten Beispiel zwei verschachtelte Schleifen.
Als Schrittweite kann generell jede x-beliebige Floating Point Zahl verwendet werden und natürlich auch eine Variable.
fornext1.gif
fornext1.gif (111.84 KiB) 16068 mal betrachtet

Zu guter Letzt unterstüzt die Version 1.71.01g noch die Kommandos SLOW und FAST.

Im FAST Modus wird die Bildschirmausgabe abgeschaltet und dadurch läuft das Programm schneller, leider ohne Bildschirminhalt. Im SLOW Modus wird der Bildschirminhalt 50 mal pro Sekunde angezeigt und dementsprechend langsam läuft das Programm, welches nur in den Anzeigepausen ausgeführt wird bzw. in den nicht sichtbaren Bildzeilen ober- und unterhalb des eigentlichen Bildes.

Folgendes Programm verdeutlicht den Geschwindigkeitsunterschied:

Code: Alles auswählen

5000    SLOW
        FOR N=1 TO 64
        PRINT "º";
        IF N=32 THEN FAST
        NEXT N
        GOTO 5000
slowfast.gif
slowfast.gif (97.85 KiB) 16068 mal betrachtet
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX80 BASIC, Übersicht Befehlsimplementierung

Beitrag von PokeMon » 02.03.2014, 13:09

Die ZX80BASIC Implementierung ist jetzt abgeschlossen.
Insbesondere wurde der Charset angepaßt und wie bei der ZX81 Version kann man jetzt auch Grafikzeichen einfügen (ALT-9 zum Ein/Ausschalten des Grafikmodus). Damit die Grafikzeichen angezeigt werden in der IDE, muss man den mitgelieferten Font SinclairZX.ttf installieren.

Als Besonderheit gilt zu beachten, dass der ZX80 Funktionen nur im ausgeschriebenen Zustand mit Klammern akzeptiert und es auf der Tastatur dafür keine Tokens gibt wie beim ZX81. Für die IDE ist das aber im Prinzip egal, da man dort sowieso mit einem Texteditor arbeitet.
Konkret sind das:
CHR$(), STR$(), TL$(), PEEK(), CODE(), RND(), USR(), ABS() - die logischen Funktionen AND,OR,NOT sind hingegen als Tokens implementiert.

Es werden nicht alle Syntaxregeln des ZX80 geprüft. So sind z.B. nur Schleifenvariablen FOR-NEXT mit nur einem Buchstaben zulässig. Ggf. wird der ZX80 beim Ausführen dann eine Syntax Fehlermeldung anzeigen. Einige Kommandos sind in 2 Schreibweisen implementiert:
RAND, RANDOMISE
CONT,CONTINUE
GOTO, GO TO
GOSUB, GO SUB

Hier die Liste aller weiteren Statements:
IF-THEN
CLS
DIM
FOR-NEXT
LET
NEW
REM
RUN
LIST
LOAD
POKE
SAVE
STOP
CLEAR
INPUT
PRINT
RETURN

PS: Da mal wieder ein kompletter Releasestand der ZX-IDE entstanden ist, werde ich die Sourcen zur ZX-IDE auf dem flatassembler board veröffentlichen.
http://board.flatassembler.net/topic.php?t=15062
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

Z80 Assembler - Undocumented Opcodes

Beitrag von PokeMon » 20.10.2014, 22:47

In der neuen Version 1.71.01m.Z80 unterstützt die ZX-IDE nun auch alle undokumentierten Opcodes, wie sie von Sean Young recherchiert und publiziert wurden. Die Dokumentation dazu kann hier downgeloaded werden.
http://www.myquest.nl/z80undocumented/

Bisher hat die ZX-IDE nur die offiziellen, von ZILOG definierten Instruktionen unterstützt. Da mit weiteren neuen Features auch das Lesen von .p Files und Disassemblieren von Assembler Teilen unterstützt wird, war es für die Neu-Assemblierung (z.B. nach einer Änderung) doch notwendig weil die nicht dokumentierten Befehle recht häufig in Programmen verwendet wurden.

Hauptsächlich betrifft es die zusätzliche Nutzung der IX und IY Register anstelle von H und L und hier auch der höherwertige oder niederwertige Anteil. Wie bekannt, kann man das HL Register als 16 Bit Register nutzen oder auch als 2 getrennte 8 Bit Register H und L. IX und IX können oder sollen laut ZILOG nur als 16 Bit Register verwendet werden, können aber auch als 8 Bit Register genutzt werden, wobei dann die höherwertigen 8 Bit und die niederwertigen 8 Bit genutzt werden können.

Die gängige Bezeichnung ist dann IXH und IXL oder auch IXh und IXl und analog dazu auch für IY. Bei Symbolen (nicht Labels !) und Instruktionen unterscheidet die ZX-IDE nicht zwischen Groß- und Kleinschreibung. Auch IxH oder iYl ist möglich. Daneben gibt es noch eine weitere SHIFT Instruktion und einige spezielle Bit Instruktionen und zwei IN/OUT Befehle. Dazu später mehr.

Hier eine Auswahl der neuen Befehle:

Code: Alles auswählen

0097: [40A0] DD 7C                              LD  A,IXh
0099: [40A2] DD 7D                              LD  A,IXl
009B: [40A4] FD 7C                              LD  A,IYH
009D: [40A6] FD 7D                              LD  A,IYL
                                        
009F: [40A8] 67                                 LD  H,A
00A0: [40A9] DD 67                              LD  IXH,A
00A2: [40AB] DD 60                              LD  IXH,B
00A4: [40AD] DD 61                              LD  IXH,C
00A6: [40AF] DD 62                              LD  IXH,D
00A8: [40B1] DD 63                              LD  IXH,E
                                        
00AA: [40B3] DD 64                              LD  IXH,IXH
00AC: [40B5] 65                                 LD  H,L
00AD: [40B6] DD 65                              LD  IXH,IXL
00AF: [40B8] DD 6C                              LD  IXL,IXH
00B1: [40BA] DD 6D                              LD  IXL,IXL
                                        
00B3: [40BC] FD 64                              LD  IYH,IYH
00B5: [40BE] FD 65                              LD  IYH,IYL
00B7: [40C0] FD 6C                              LD  IYL,IYH
00B9: [40C2] FD 6D                              LD  IYL,IYL
Man sieht recht deutlich die Substituierung der Register H und L indem dem eigentlich Opcode der Präfix für die Nutzung der Register IX und IY (DD bzw. FD) vorangestellt wird. LD H,A entspricht Opcode $67, LD IXH,A entspricht $DD,$67.

Neben den Load Befehlen funktionieren aber auch alle arithmetischen und logischen Instruktionen, die mit den Registern H und L verwendet werden können.
Hier eine Auswahl:

Code: Alles auswählen

00C1: [40CA] DD 8C                              ADC A,IXh
00C3: [40CC] DD 85                              ADD A,IXl
00C5: [40CE] FD A4                              AND IYh
00C7: [40D0] FD BD                              CP IYL
00C9: [40D2] DD 25                              DEC IXh
00CB: [40D4] DD 2C                              INC IXl
00CD: [40D6] FD B4                              OR  IYH
00CF: [40D8] FD 9D                              SBC A,IYL
00D1: [40DA] DD 94                              SUB IXh
00D3: [40DC] FD AD                              XOR IYl
Interessant ist der Einsatz der IX und IY Register mit Displacements (IX+... oder IY-...) und einem Zweitregister für die Bit (SET/RES), Shift (SLA,SRA,SRL) und Rotationsbefehle (RL, RLC, RR, RRC). Hier wird das Ergebnis sowohl in dem Speicheroperanden (IX+/-n) als auch in einem Register gespeichert. Ungewöhnlich ist die Notation mit einem zweiten Register während sonst nur ein Register notwendig ist.

Code: Alles auswählen

00DB: [40E4] DD CB 03 86                        RES 0,(IX+3)
00DF: [40E8] DD CB FD 87                        RES 0,(IX-3),A
00E3: [40EC] DD CB FD 80                        RES 0,(IX-3),B
00E7: [40F0] DD CB FD 81                        RES 0,(IX-3),C
00EB: [40F4] DD CB FD 82                        RES 0,(IX-3),D
00EF: [40F8] DD CB FD 83                        RES 0,(IX-3),E
00F3: [40FC] DD CB FD 84                        RES 0,(IX-3),H
00F7: [4100] DD CB FD 85                        RES 0,(IX-3),L
                                        
00FB: [4104] DD CB 7F C0                        SET 0,(IX+$7F),B
00FF: [4108] FD CB 80 C7                        SET 0,(IY-128),A
                                        
0103: [410C] DD CB FF 17                        RL  (IX-1),A
0107: [4110] FD CB 02 10                        RL  (IY+2),B
010B: [4114] FD CB 00 01                        RLC (IY+0),C
010F: [4118] DD CB 40 1D                        RR  (IX+40h),L
0113: [411C] FD CB 0F 0F                        RRC (IY+0Fh),A
                                        
0117: [4120] FD CB FE 21                        SLA (IY-2),C
011B: [4124] DD CB 11 2B                        SRA (IX+17),E
011F: [4128] FD CB F8 38                        SRL (IY-8h),B
Die erste RES Instruktion ist die Standard Instruktion und speichert das Ergebnis (auch) in IX+3. Bei den folgenden Varianten wird anstelle des substituierten (HL) Registers eines der 8 Bit Register referenziert. IX und IY mit Displacement sind hier ja auch nur Substitutionen des (HL) Registers. Das Ergebnis der entsprechenden Operation wird sowohl in der referenzierten Speicherstelle gespeichert als auch eine Kopie des Ergebnisses in dem genannten Register. Dadurch hat man bei Folgeoperationen einen schnelleren Zugriff auf den Wert und gleichzeitig wird er gespeichert. Sofern IX oder IY und Displacement eine Adresse im ROM referenzieren, ändert sich zwar das Ergebnis im Speicher (ROM) nicht, dennoch hat das entsprechende 8 Bit Register den berechneten Wert.

Code: Alles auswählen

0129: [4132] CB 37                              SLL A
012B: [4134] CB 30                              SLL B
012D: [4136] CB 31                              SLL C
012F: [4138] CB 32                              SLL D
0131: [413A] CB 33                              SLL E
0133: [413C] CB 34                              SLL H
0135: [413E] CB 35                              SLL L
0137: [4140] CB 36                              SLL (HL)
0139: [4142] DD CB FC 36                        SLL (IX-4)
013D: [4146] FD CB 08 36                        SLL (IY+8)
0141: [414A] DD CB 01 37                        SLL (IX+1),A
0145: [414E] DD CB 01 30                        SLL (IX+1),B
0149: [4152] DD CB 01 31                        SLL (IX+1),C
014D: [4156] FD CB 01 32                        SLL (IY+1),D
0151: [415A] DD CB 01 33                        SLL (IX+1),E
0155: [415E] DD CB 01 34                        SLL (IX+1),H
0159: [4162] DD CB 01 35                        SLL (IX+1),L
Mit SLL steht ein neuer Shift Befehl zur Verfügung, Shift logisch links. Der funktioniert wir SLA (Shift arithemetisch links) jedoch schiebt er eine 1 statt eine 0 in Bit 0.

Code: Alles auswählen

0163: [416C] ED 78                              IN  A,(C)
0165: [416E] ED 70                              IN  F,(C)
0167: [4170] ED 79                              OUT (C),A
0169: [4172] ED 71                              OUT (C),0
Es gibt auch 2 neue I/O Instruktionen, IN (F),C liest ein Datum vom Port (C) ein, ohne dieses in ein Register zu speichern resp. ein Register zu überschreiben. Dennoch werden die Flags entsprechend der I/O Instruktionen gesetzt, daher auch das Akronym IN F,(C) was auf das Einlesen in das Flag Register hindeutet, jedoch nicht den Wert einliest sondern nur die Flags in Abhängigkeit vom gelesenen Wert setzt. Mit OUT (C),0 kann man recht einfach den Wert 0 auf eine Portadresse schreiben, ohne ein Register zu benutzen.
Flags die bei einer IN Instruktion gesetzt werden:
S is set if input data is negative; reset otherwise
Z is set if input data is zero; reset otherwise
H is reset
P/V is set if parity is even; reset otherwise
N is reset
C is not affected
Es gibt noch 2 Control Directives, die den Assembler steuern und die undokumentierten Instruktionen zulassen oder sperren. Z80LOOSE läßt die undokumentierten Instruktionen zu, Z80STRICT erzeugt bei Verwendung eine Fehlermeldung. Standardmäßig ist beim Start eines Assemblierungsvorgangs Z80LOOSE gesetzt, die Instruktionen also erlaubt. Diese Steuerdirektiven können beliebig oft im Quelltext verwendet werden und man kann daher auch den Einsatz der undokumentierten Opcodes auch auf bestimmte Programmbereiche beschränken.

Code: Alles auswählen

                                                Z80strict
0171: [417A] CE 63                              ADC A,99
0173: [417C] 8F                                 ADC A,A
0174: [417D] 8E                                 ADC A,(HL)
0175: [417E] DD 8E 01                           ADC A,(IX+1)
0178: [4181] FD 8E FF                           ADC A,(IY-1)
                                                Z80loose
017B: [4184] DD 8C                              ADC A,IXh
017D: [4186] DD 8D                              ADC A,IXl
017F: [4188] FD 8C                              ADC A,IYh
0181: [418A] FD 8D                              ADC A,IYL
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, Variablen Definition im Speicher I

Beitrag von PokeMon » 24.10.2014, 23:31

Wie bereits angekündigt, kann man Variablen anstelle einer Definition im Programm (mit LET) auch direkt Variablen im Speicher definieren. Neben weiteren Vorteilen spart man hier primär Speicherplatz und Rechenzeit, weil die Variablen nicht erst durch das Starten des Programms angelegt werden sondern bereits nach dem Laden des Programms im Speicher sind.

Einziger Nachteil: Man darf das Programm nicht mit RUN starten sondern entweder mit GOTO 1 (für den Start am Beginn) oder durch den Autostart des Programms direkt nach dem Laden an einer spezifizierten Stelle. Die Startzeile kann in der ZX-IDE einfach definiert werden, indem das Label AUTORUN: vor die betreffende BASIC Zeile gesetzt wird:

Code: Alles auswählen

format zx81
;labelusenumeric
;LISTOFF

        // hardware options to be set and change defaults in ZX81DEF.INC
        MEMAVL     =       MEM_16K         // can be MEM_1K, MEM_2K, MEM_4K, MEM_8K, MEM_16K, MEM_32K, MEM_48K
                                           // default value is MEM_16K
        STARTMODE  EQU     SLOW_MODE       // SLOW or FAST
        DFILETYPE  EQU     AUTO            // COLLAPSED or EXPANDED or AUTO
        STARTUPMSG EQU    'CREATED WITH ZX81-IDE' // any message will be shown on screen after loading, max. 32 chars

        include 'SINCL-ZX\ZX81.INC'        // definitions of constants
;LISTON
        AUTOLINE 10

        REM 'VAR DEFINITION'
        LET A=99
AUTORUN:
        PRINT A
;LISTOFF
        include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
;LISTON
VARS_ADDR:
        VAR A=100
        db 80h
WORKSPACE:

assert ($-MEMST)<MEMAVL
// end of program
Der obige Code zeigt ein vollständiges ZX81 BASIC Programm. Bei den Startup Variablen hat sich nichts geändert, anstelle der ZX81POST.INC wird nach dem BASIC Programm jedoch ZX81DISP.INC eingefügt, die nur noch das Display (DFILE) definiert, nicht aber mehr die Variablen Deklaration. Die ZX81POST.INC enthält einfach einen leeren Variablen Bereich. Der Variablenbereich schließt sich an das DFILE an und hat zum Start das Label VARS_ADDR: gefolgt von den benötigten Variablen und zum Schluß (wichtig !) ein Ende Marker durch ein Byte mit $80 (oder 80h). Nach dem Variablenbereich schließt sich das Label WORKSPACE: an. Die genannten Labels sind wichtig, da sie die entsprechenden Adressen im Bereich der Systemvariablen des ZX81 Programms setzen.

Code: Alles auswählen

009C: [40A5] 00 1E 0C 00 F1 26 14 25            LET A=99
             25 7E 87 46 00 00 00 76    
                                        AUTORUN:
00AC: [40B5] 00 28 03 00 F5 26 76               PRINT A
                                        ;LISTON
                                        VARS_ADDR:
03CC: [43D5] 66 87 48 00 00 00                  VAR A=100
03D2: [43DB] 80                                 db 80h
                                        WORKSPACE:
Eine numerische Variable wird im einfachsten Fall mit der Direktive VAR eingeleitet, gefolgt vom Variablennamen und einer (optionalen) Initialisierung mit "=" und dem entsprechenden numerischen Wert. Im obigen Listing sehen wir den Unterschied zwischen der LET Anweisung (in diesem Fall mit einem anderen Wert) und der Variablendefinition im Speicher. Die pure Variablendefinition belegt 6 Byte und die Variante im Programm mit LET z.B. 16 Byte (also fast 3 mal so viel) durch den Overhead wie Variable in ASCII und als Floating Point Wert und dem Kommando LET, Zeilennummer, Zeilenlänge usw. Man sollte sich hier auch vor Augen halten, dass das Programm wenn es läuft, die Variable sowieso im Speicher anlegt und dann die Definition doppelt vorhanden ist, sowohl im Speicher als auch noch in der betreffenden LET Zeile. Unter diesem Gesichtspunkt hat man 6 Byte statt 22 Byte, also nur etwa ein Viertel des Speicherbedarfs.

Im Beispiel oben ist auch AUTORUN: vor der PRINT A Anweisung gesetzt. So wird direkt nach dem Laden der Wert 100 ausgegeben (auch nach einem erneuten GOTO 40), beim Start mit RUN dagegen der Wert 99 (wird durch das Programm gesetzt).

Es folgen ein paar willkürliche, numerische Variablen Definitionen:

Code: Alles auswählen

                                        VARS_ADDR:
03B8: [43C1] 66 87 48 00 00 00                  VAR A=100
03BE: [43C7] 67 87 76 00 00 00                  VAR B=123
03C4: [43CD] 68 87 B0 00 00 00                  VAR C=-88
03CA: [43D3] 69 87 30 00 00 00                  VAR D=+88
03D0: [43D9] 6A 00 00 00 00 00                  VAR E
03D6: [43DF] A6 27 A8 00 00 00 00 00            VAR ABC=0
03DE: [43E7] A7 31 26 27 31 A6 9F 0F            VAR BLABLA=12E8
             0D 18 00                   
03E9: [43F2] EE 84 20 00 00 00 85 20            VAR I=10,20,2,41
             00 00 00 82 00 00 00 00    
             29 00                      
03FB: [4404] 80                                 db 80h
                                        WORKSPACE:
Numerische Variablen können einen oder mehrere Buchstaben besitzen und werden von dem 5-Byte Floating Point Wert gefolgt. Verschiedene Bits in dem ersten oder ggf. auch letzten Buchstaben definieren den Variablentyp (numerisch, String, Array) und dessen Länge. Die Definition ist optional, kann also auch weggelassen werden, siehe Variable E. In dem Fall wird sie automatisch mit 0 angelegt. Diese Eigenschaft ist für eine einfache Variable nicht besonders nützlich, wohl aber für Arrays.

Die letzte Variablendefinition mit F stellt eine Schleifenvariable dar, wie sie von einer FOR-NEXT-Schleife verwendet wird. Diese enthält insgesamt 4 Werte per Definition (siehe Handbuch) und enthält in der genannten Reihenfolge a) den aktuellen Wert, b) das Limit (TO), c) die Schrittweite (STEP) und d) die auf die FOR-NEXT Definition folgende Zeile, im einfachsten Fall immer die Zeile mit der FOR-NEXT Definition +1. Falls sie nicht existiert springt das BASIC automatisch zur nächsten verfügbaren Zeile. Die ersten 3 Werte a-c sind Floating Point Definitionen, die letzte ist die Zeilennummer als WORD deklariert. Hier ist die abweichende Notation als Little Endian interessant, während die Zeilennummer im BASIC Programm immer als Big Endian codiert ist.

Interessanterweise funktioniert der gesamte Mechanismus NEXT <> auch ohne eine FOR Definition, wenn die Variable im Speicher definiert ist. Die Schleifenvariable benötigt im Variablenspeicher lediglich 18 Byte während sie als Programmzeile hier 25 oder 33 Byte benötigt, je nachdem ob mit oder ohne STEP Angabe. Und auch hier wird die Variable beim Starten des Programms sowieso angelegt, so dass man hier über 50 Byte Speicher benötigt anstelle der lediglich erforderlichen 18 Byte. Auch kann man hier im laufenden Programm die anzuspringende Zeile oder den aktuellen Wert oder das Limit manipulieren.

Hier ein witziges Programm dazu (ohne FOR Schleife aber mit NEXT Verwendung):

Code: Alles auswählen

        AUTOLINE 10

        REM 'FUNNY PROGRAM'
AUTORUN:
        PRINT I
        NEXT I
;LISTOFF
        include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
;LISTON
VARS_ADDR:
        VAR I=10,20,2,11
        db 80h
WORKSPACE:
Auf dem gleichen Wege lassen sich auch Arrays definieren. Hier ist es recht praktisch, dass die Arrays automatisch mit Null initialisiert werden, es können aber auch gezielt Werte angegeben werden:

Code: Alles auswählen

                                        VARS_ADDR:
03AE: [43B7] 8C 17 00 01 04 00 81 00            VAR G(4)=1,2,3,4
             00 00 00 82 00 00 00 00    
             82 40 00 00 00 83 00 00    
             00 00                      
03C8: [43D1] 8D 1C 00 01 05 00 81 00            VAR H(5)=1,2
             00 00 00 82 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00       
03E7: [43F0] 90 35 00 01 0A 00 00 00            VAR K(10)
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
041F: [4428] 80                                 db 80h
                                        WORKSPACE:
Arrays können vollständig G(4), teilweise H(5) oder auch gar nicht K(10) initialisiert werden. Alle nicht angegebenen Werte werden automatisch mit Null initialisiert, ggf. also auch alle.

Code: Alles auswählen

VARS_ADDR:
        VAR L(3,5)=11,12,13,14,15,\
        21,22,23,24,25,\
        31,32,33,34,35

        VAR M(1000)

        db 80h
WORKSPACE:
Die obige Definition zeigt die Initialisierung eines 2-dimensionalen Arrays, wobei die Werte in der Reihenfolge von der letzten bis zu ersten Definition übergeben werden. Wenn es sehr viele Werte sind, ist es möglich hier mit dem Slash "\" die Definition in der folgenden Zeile fortzusetzen. Das ist im Prinzip an jeder Stelle im Quelltext möglich, nicht jedoch INNERHALB (!) einer Zeichenkette (String). Die zweite Definition zeigt eine einfach Möglichkeit, viel Speicher zu belegen, Variable M(1000) hat etwa 5000 Byte (5 Byte pro Wert als Floating Point).
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 BASIC, Variablen Definition im Speicher II

Beitrag von PokeMon » 25.10.2014, 00:25

String Variablen werden analog zu den numerischen Variablen mit VAR definiert, können jedoch nur aus einem Buchstaben, gefolgt von einem $ Zeichen bestehen. Die gleiche Einschränkung gilt übrigens für numerische Arrays und FOR-NEXT Variablen. Hier ein paar typische Beispiele:

Code: Alles auswählen

        REM 'STRING VARS'
AUTORUN:
        PRINT A$
        PRINT B$
        PRINT C$
        PRINT D$
        PRINT E$
        PRINT F$
        PRINT G$
;LISTOFF
        include 'SINCL-ZX\ZX81DISP.INC'          ; include D_FILE and needed memory areas
;LISTON
VARS_ADDR:
        VAR A$
        VAR B$='SHORT TEXT'
        VAR C$='LONGER TEXT WITH SOME MORE WORDS'
        VAR D$='' // empty text
        VAR E$="TEXT WITH AUTOMATIC QUOTES"
        VAR F$='QUOTE IN TEXT '' SOMEWHERE'
        VAR G$='%'
        db 80h
WORKSPACE:
und hier das Listing mit dem Quelltext (nur Variablen Bereich):

Code: Alles auswählen

                                        VARS_ADDR:
03D6: [43DF] 46 00 00                           VAR A$
03D9: [43E2] 47 0A 00 38 2D 34 37 39            VAR B$='SHORT TEXT'
             00 39 2A 3D 39             
03E6: [43EF] 48 20 00 31 34 33 2C 2A            VAR C$='LONGER TEXT WITH SOME MORE WORDS'
             37 00 39 2A 3D 39 00 3C    
             2E 39 2D 00 38 34 32 2A    
             00 32 34 37 2A 00 3C 34    
             37 29 38                   
0409: [4412] 49 00 00                           VAR D$='' // empty text
040C: [4415] 4A 1C 00 0B 39 2A 3D 39            VAR E$="TEXT WITH AUTOMATIC QUOTES"
             00 3C 2E 39 2D 00 26 3A    
             39 34 32 26 39 2E 28 00    
             36 3A 34 39 2A 38 0B       
042B: [4434] 4B 19 00 36 3A 34 39 2A            VAR F$='QUOTE IN TEXT '' SOMEWHERE'
             00 2E 33 00 39 2A 3D 39    
             00 0B 00 38 34 32 2A 3C    
             2D 2A 37 2A                
0447: [4450] 4C 01 00 0C                        VAR G$='%'
044B: [4454] 80                                 db 80h
                                        WORKSPACE:
Variable A$ ist wie Variable D$ als Leerstring definiert (Länge 0). Normalerweise werden die Variablendefinitionen ohne Anführungszeichen gespeichert und sind in der ZX-IDE mit einfachen Anführungszeichen anzugeben. Bei doppelten Anführungszeichen werden diese automatisch in den String übernommen was normalerweise nicht gewünscht ist. Durch 2 aufeinanderfolgende einfache Anführungszeichen (single quotes) in einer Zeichenkette kann explizit mittendrin ein Anführungszeichen aus dem ZX BASIC gesetzt werden. Das %-Zeichen steht übrigens für das Pfund Zeichen, welches im ASCII Zeichensatz sonst nicht enthalten ist.

Wie bei den numerischen Arrays können auch Strings als Arrays definiert werden:

Code: Alles auswählen

                                        VARS_ADDR:
03D6: [43DF] D0 0B 00 01 08 00 1D 1E            VAR K$(8)='12345678'
             1F 20 21 22 23 24          
03E4: [43ED] D1 13 00 01 10 00 38 2D            VAR L$(16)='SHORT'
             34 37 39 00 00 00 00 00    
             00 00 00 00 00 00          
03FA: [4403] D2 0B 00 01 08 00 00 00            VAR M$(8)
             00 00 00 00 00 00          
0408: [4411] D3 2D 00 02 0A 00 04 00            VAR N$(10,4)='ABC','DEF','GHI',\
             26 27 28 00 29 2A 2B 00            'JKL','MNO'
             2C 2D 2E 00 2F 30 31 00    
             32 33 34 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
             00 00 00 00 00 00 00 00    
0438: [4441] 80                                 db 80h
                                        WORKSPACE:
Variable K$ ist vollständig initialisiert, Variable L$ ist kürzer initialisiert als definiert, Variable M$ ist mit Nullwerten (Leerzeichen) initialisiert, Variable N$ ist teilweise initialisiert mit nur 5 Zeichenketten, alle 1 Byte kürzer als das Maximum, Rest mit Leerzeichen initialisiert.

Interessant beim Einsatz mit String Variablen ist die Nutzung für Assemblercode oder für Datenbereiche.

Code: Alles auswählen

0074: [407D] 00 0A 0B 00 EA 28 34 29            REM 'CODE VARS'
             2A 00 3B 26 37 38 76       
                                        AUTORUN:
0083: [408C] 00 14 0E 00 F9 D4 1D 23            RAND USR #labels
             1F 1F 24 7E 8F 07 74 00    
             00 76                      
                                        
                                        ;LISTON
                                        VARS_ADDR:
03AE: [43B7] 5F 07 00                           VAR Z$ _asm
                                        labels:
03B1: [43BA] 2A 0C 40                           LD HL,(D_FILE)
03B4: [43BD] 23                                 INC HL
03B5: [43BE] CB FE                              SET 7,(HL)
03B7: [43C0] C9                                 RET
                                                END _asm
                                        
03B8: [43C1] 80                                 db 80h
                                        WORKSPACE:
Das obige Listing zeigt die Definition eines kleinen Programms in der Variable Z$, welches nach dem Laden des Programms automatisch ausgeführt wird durch RAND USR #labels. Die Definition ist etwas kürzer als in einer REM Zeile, der entscheidende Vorteil ist aber, dass man temporär benötigte Daten oder Programme recht einfach wieder löschen und den belegten Speicher freigeben kann durch LET Z$="" wenn man die anderen Variablen noch behalten will oder ggf. durch ein gezieltes CLEAR oder RUN (diese beiden Kommandos löschen jedoch alle Variablen).

Solange am Programm nichts geändert wird (kein Löschen oder Hinzufügen von Programmzeilen oder Editieren) und solange man mehr als 4k Speicher hat (expanded DFILE) braucht das betreffende Programm nicht relozierbar sein, weil es im Speicher nicht verschoben wird. Variablen selbst sind im Speicher nicht alphabetisch oder nach Typ sortiert. Sofern man diese Code Variablen also am Anfang platziert, werden auch Änderungen der Variablen keinen Einfluss auf den Code haben.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
siggi
User
Beiträge: 2140
Registriert: 06.12.2005, 08:34
Wohnort: D, Hessen, tiefste Werreraa
Kontaktdaten:

Re: ZX81 BASIC, Variablen Definition im Speicher II

Beitrag von siggi » 25.10.2014, 09:43

PokeMon hat geschrieben: Solange am Programm nichts geändert wird (kein Löschen oder Hinzufügen von Programmzeilen oder Editieren) und solange man mehr als 4k Speicher hat (expanded DFILE)
... und solange man im BASIC-Programm kein SCROLL benutzt (denn da wird eine leere Bildschirmzeile, bestehend nur aus $76 angehängt und damit das DFILE verkürzt) ...
braucht das betreffende Programm nicht relozierbar sein, weil es im Speicher nicht verschoben wird. Variablen selbst sind im Speicher nicht alphabetisch oder nach Typ sortiert. Sofern man diese Code Variablen also am Anfang platziert, werden auch Änderungen der Variablen keinen Einfluss auf den Code haben.
Gruß
Siggi
Mein ZX81-Web-Server: online seit 2007
http://zx81-siggi.endoftheinternet.org/index.html

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

Re: ZX-IDE in der Praxis, Tipps & Tricks für ZX81,ZX80

Beitrag von PokeMon » 25.10.2014, 14:42

Danke für den wertvollen Hinweis. Das mit SCROLL war mir jetzt nicht bewußt, verwende das Scrolling gewöhnlich aber auch nicht.
Man kann die ursprüngliche Adresse des Programms (bzw. der Variable) aber ggf. auch mit einem PRINT AT 0,0; vor dem Aufruf wiederherstellen oder ganz hart auch mit einem CLS.

Das ist also dann ein bischen vom Programm abhängig, ob es mit SCROLL funktioniert oder der Code tatsächlich relozierbar sein muss. Solange man keine Calls verwendet (außer ggf. ins ROM) und keine absoluten Sprünge (JP xx) sondern nur JR's dürfte die genaue Position des Codes keine Rolle spielen. Natürlich darf man ihn dann nicht von außen anspringen, also in dem Fall nicht ideal für einen Bildschirm- oder Tastaturtreiber.

Auch muss man dann die aktuelle Adresse vom BASIC Programm zuerst ermitteln. Wenn man nur eine Code Variable hat und diese am Anfang steht, ist das ggf. recht einfach mit "PEEK 16400+256* PEEK 16401+3" machbar.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 P-File Konverter

Beitrag von PokeMon » 26.10.2014, 15:35

Die neueste Version der ZX-IDE enthält jetzt auch einen P-File Konverter, der .p oder .81 Dateien einlesen und als Quellcode (BASIC und ggf. Assembler) übersetzen kann. Damit kann man ggf. Programme analysieren und ändern. Der Disassembler ist derzeit aber nur rudimentär implementiert und kann zur Zeit nur Code Bereich disassemblieren und interpretiert gemischte Code/Daten Bereich als reinen Code, was mit Vorsicht zu genießen ist.

Das Feature ist daher derzeit nur für BASIC Programme zu empfehlen und sollte als "experimental feature" betrachtet werden.
Ich habe im nächsten Beitrag ein .zip angehängt mit ausgewählten Beispiel-Programmen, die vermutlich fehlerfrei übersetzt wurden.
Zumindest laufen Sie im Emulator. :wink:

Fragen und Feedback bitte nicht in diesen Thread hier posten (der ja sozusagen ein Art Handbuch ist) sondern nur hier:
viewtopic.php?f=2&t=632

Für das Einlesen gibt es einen Extra Menüpunkt unter File mit der Bezeichnung "Open ZX binary":
p2a0.jpg
p2a0.jpg (15.78 KiB) 15141 mal betrachtet
p2a1.jpg
p2a1.jpg (52.15 KiB) 15141 mal betrachtet
Im Quelltext werden am Anfang die Systemvariablen gesetzt und angezeigt, gefolgt vom BASIC Programm und dem Display File und ggf. Variablen:
p2a2.jpg
p2a2.jpg (57.44 KiB) 15141 mal betrachtet
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 P-File Konverter

Beitrag von PokeMon » 26.10.2014, 15:40

Assemblerbereiche werden zum Teil richtig übersetzt, aber nur wenn der Aufbau des Programms einem bestimmten Schema folgt (erst CODE in einer REM Zeile, danach ggf. Daten). Die gemischte Verwendung von Code und Daten ist mit Vorsicht zu genießen und kann zu falsch übersetzten Programmen führen. In einer späteren Version soll das Feature auch für Assembler möglichst fehlerfrei ablaufen.
p2a3.jpg
p2a3.jpg (53.63 KiB) 15141 mal betrachtet

Wer das Feature austesten möchte, sollte ggf. die Programme aus dem angehängten P2A-TEST.zip mit getesteten Beispielprogrammen wählen. :wink:
P2A-TEST.zip
(38.29 KiB) 283-mal heruntergeladen
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ROM Images / Kompositionen erstellen (ZX80CORE)

Beitrag von PokeMon » 12.05.2015, 23:11

Auf dem Treffen in Mahlerts hatte ich eine kurze Einführung in die Nutzung der ZX-IDE gegeben und versprochen, die Programmbeispiele hier zu veröffentlichen. Das möchte ich nun einlösen. :wink:

Zunächst möchte ich hervorheben, dass man dafür im Grunde weder großartige Assemblerkenntnisse noch BASIC Programmierkenntnisse braucht. Die Beispiele werden erklärt und können 1:1 benutzt werden mit anderen Daten/ROMs/Programmen, ganz nach Belieben. Im Grunde ist es mehr Konfiguration als Programmierung. Dafür kann man recht einfach ROM Images mit verschiedenen ROMs zusammenstellen, wie sie z.B. für den ZX80CORE oder auch später für den ZXmore verwendet werden können. Wobei das beim ZXmore noch viel einfacher gehen wird.

So - die Aufgabenstellung:
Ich möchte ein 32k EEPROM oder EPROM mit ZX80, ZX81, ASZMIC oder anderen ROMs erstellen und mit dem Umschalter dann je nach Bedarf das gewünschte Image auswählen. Hier wird mit der IDE ein gesamtes 32k Image erstellt aus vorhandenen ROM Ressourcen und man muss sich keine Gedanken machen, wie man das in einen EPROMMER laden und welche Adressen und ROM Größen man angeben muss. Auf diesem Wege ist auch das ausgelieferte ZX80CORE ROM Image entstanden. Hier der Quelltext:

Code: Alles auswählen

format binary as 'rom'
romimage:
file '../ROM/zx80.rom'
file '../ROM/zx80.rom'
file '../ROM/zx81.rom'
file '../ROM/h4th.rom'
file '../ROM/aszmic.rom'
db 4096 dup($ff)
Mit der Direktive file können beliebige binäre Daten eingelesen, hier verschiedene ROMs nacheinander und als eine Datei ZX80CORE.ROM ausgegeben (wenn die Quelldatei ZX80CORE.ASM genannt wird). Es werden nacheinander zx80.rom, zx81.rom, h4th.rom und aszmic.rom eingelesen. Dabei wird die Binärdatei vollständig eingelesen. Die ROM Dateien sind Bestandteil des Emulators EightyOne, der mit der IDE mitinstalliert wird. zx80.rom wird zweimal hintereinander eingelesen, um die Page Größe von 8kB zu gewährleisten. Das ROM hat nur 4kB, daher zweimal einlesen gibt 8kB. Das aszmic.rom hat ebenfalls nur 4kB und hier wird am Ende mit db (databytes) manuell mit 4kB Daten ($ff) aufgefüllt.

Eine andere Möglichkeit ist die Verwendung von align:

Code: Alles auswählen

format binary as 'rom'
romimage:
file '../ROM/zx80.rom'
align 8192
file '../ROM/zx81.rom'
file '../ROM/h4th.rom'
file '../ROM/aszmic.rom'
db 4096 dup($ff)
Hier wird das zx80.rom nur einmal eingelesen und das zx81.rom beginnend auf ein "Speicherraster" (hier 8kB) geladen. Es werden automatisch Füllbytes erzeugt, so viele wie notwendig sind um bis zum nächsten Raster zu gelangen. Das funktioniert im Grunde auch, wenn nur 1 Byte eingelesen würde, dann wird mit 8191 Bytes aufgefüllt. Der Füllwert von align ist allerdings festgelegt auf $00 (NOP). align funktioniert nicht am Ende einer Datei, hier muss ggf. manuell mit db 4096 dup($ff) aufgefüllt werden, wenn man exakt 32kb haben möchte. Bei dieser Direktive db kann man dagegen auch einen von $00 abweichenden Füllwert angeben wie z.B. $ff.

align funktioniert weiterhin nur mit 2er Potenzen, es wäre z.B. nicht möglich ein Alignment von 3kb zu wählen. Gültige oder sinnvolle Werte sind daher:

Code: Alles auswählen

2, 4, 8, 16, 32, 64, 128, 256
512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
usw.
Nachfolgend ein weiteres Beispiel, wie man Binärdaten teilweise einlesen kann oder ggf. auch durch eigene Code Stücke ersetzen kann:

Code: Alles auswählen

format binary as 'rom'

romimage:
file '../ROM/zx80.rom':0,2048
file '../ROM/zx80.rom':2048,2048
align 8192

OUT ($FD),A
LD  BC,$FFFF
file '../ROM/zx81.rom':5

file '../ROM/h4th.rom'

file '../ROM/aszmic.rom'
db 4096 dup($ff)
Bei der Direktive file kann man optional ein Offset angeben (erster Wert) und die Größe der einzulesenden Bytes angeben, wenn man nur teilweise Daten importieren möchte. Das erste Beispiel liest zunächst die erste Hälfte vom zx80.rom ein, und danach die zweite Hälfte. Das ist jetzt nicht so wahnsinnig sinnvoll aber man kann natürlich auch wie im folgenden Beispiel der zx81.rom Datei die Startupsequenz recht einfach abändern. Hier wird der originäre ROM Code wie folgt abgeändert:

Code: Alles auswählen

;; START
L0000:  OUT     ($FD),A         ; Turn off the NMI generator if this ROM is 
                                ; running in ZX81 hardware. This does nothing 
                                ; if this ROM is running within an upgraded
                                ; ZX80.
        LD      BC,$7FFF        ; Set BC to the top of possible RAM.
                                ; The higher unpopulated addresses are used for
                                ; video generation.
        JP      L03CB           ; Jump forward to RAM-CHECK.
Standardmäßig prüft der ZX81 nur auf maximal 16kB RAM, selbst wenn mehr verfügbar ist. Das ist recht lästig wenn man mehr Speicher zur Verfügung hat und nutzen will. Sonst muss man explizit nach dem Starten die Sequenz POKE 16389,192 und NEW eingeben für 32k RAM oder POKE 16389,255 und POKE 16388,255 plus NEW für 48k RAM. Möglicherweise reicht auch POKE 16389,0 - ich bin mir aber nicht sicher, ob es möglicherweise in bestimmten Konstellationen mit diesem Wert Probleme geben kann. Die maximale Größe steht im BC Register (LD BC,$7FFF) und ist 32767 (Obergrenze des RAM). Wer also entsprechende HW Ausstattung hat, kann hier einfach die ersten beiden Befehle in die ZX-IDE schreiben (OUT ($FD),A und LD BC,$FFFF) und anschließend die restlichen Bytes des ZX81 ROM einlesen (Offset 5 da die beiden Befehle die ersten 5 Bytes belegen).

Zum guten Schluss noch ein weiteres Beispiel für den ZXmore, wie ich es z.B. mal für Testzwecke erstellt habe:

Code: Alles auswählen

romimage:
@@: IN A,($FE)
OUT A,($FF)
repeat 4
LD A,A
end repeat
repeat 4
LD A,($4000)
end repeat
JR @b
align 65536

IN A,($FE)
OUT A,($FF)
JR $FA
align 65536

file '../ROM/zx81.rom'
align 65536

file '../ROM/zx80.rom'
align 65536

file '../ROM/sg81.rom'
align 65536

file '../ROM/zx80.rom'
align 65536

file '../ROM/zx81.rom'
db 100h-$ mod 100h dup($55)
db 10000h-$ mod 10000h dup(0)

file '../ROM/ZXmore.rom'
db 10000h-$ mod 10000h dup(0)
Hier sind insgesamt 8 Instanzen/ROM Images möglich mit je maximal 64kB.
Die ersten beiden Images sind für reine Testzwecke gedacht und erzeugen Messsignale für ein Oszilloskop mit OUT und IN Befehlen zum Ausmessen von Adressierungen, MREQ, IORQ usw.
align 65536 setzt das jeweils nächste Image immer im 64kb Raster. Wie bereits geschrieben, erzeugt es jedoch nur NOPs und kann nur für Adressen mit glatten 2er Potenzen verwendet werden. Beim vorletzten Image (zx81.rom) habe ich ein Beispiel ausgewählt, welches zunächst ein Alignment auf die nächste Adresse im $100 Raster macht, also von $2000 bis $2100 mit $55 auffüllt und anschließend bis zum nächsten 64kB Raster mit NOPs.

Das Problem mit dem komischen Ausdruck "10000h-$ mod 10000h" liegt an der Überschreitung der 64kB Grenzen. Generell ist die IDE für die Z80 Programmierung nur für den Adressraum von 16 Bit (64kB oder 65536) ausgelegt. Hier wird mit dem Programm ein 512kB ROM Image erstellt und die eigentliche (aktuelle) Adresse mit $ ist nach mehreren 64kB Images größer als 65536. Die Berechnungsformel 10000h-$ berechnet sozusagen immer die noch fehlenden Bytes bis zur nächsten 64kB Grenze und mod schneidet hier den Wert auf 16 Bit ab.

Beim vorletzten Image (zx81.rom) hätte man auch mit align 65536 arbeiten können, beim letzten Image ist das nicht möglich weil align (wie schon bereits ausgeführt) immer nur auffüllt solange noch Code folgt. Wenn nichts mehr folgt, macht align an der Stelle nichts und man erhält ein kleineres Image als 512kB.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

Programme aus dem ROM laden

Beitrag von PokeMon » 14.05.2015, 00:16

Fortsetzung des Mahlerts Vortrags mit weiteren nützlichen Anwendungen.
Im Grunde basieren diese Beispiele auf dem Prinzip der Erstellung von ROM Images, nur dass man hier eben zusätzliche Programme anstelle von umschaltbaren ROMs laden kann und den zur Verfügung stehenden Platz nutzen kann. Standardmäßig fängt das RAM beim ZX81 erst bei 16k ($4000) an und das ZX81 ROM belegt nur 8k ($0000-$2000) so dass man weitere 8k zur freien Verfügung hat. Da kann man dann z.B. häufig benötigte Programme, Tools oder Treiber reinpacken, die sozusagen immer nach dem Einschalten zur Verfügung stehen, ohne dass man von Tape oder irgendwoanders was laden muss. Man lädt dann aus dem ROM.

Dazu braucht es nur ein minimales Stück Code und eine kleine Datenstruktur. Wir fangen mit einem ganz einfachen Beispiel an. Mangels HW Treiber und eigener Programme habe ich mich mal der schönen Tools von Entwickler Swatosch bedient, zu finden hier:
http://www.swatosch.de/zx81/

Das Power BASIC ist wirklich cool und belegt nur etwa 4k zusätzlichen Speicher. Einer der großen Vorteile ist das Ausführen von IN und OUT Befehlen mit einem ganz normalen BASIC Programm oder auch Fehlersuche während der Entwicklung mit schrittweiser Ausführung (STEPON/OFF) oder einer ONERROR Funktion zum Abfangen von BASIC Fehlermeldungen. Gerade wenn man mit Hardware experimentiert, ist das sehr nützlich. Habe das Power BASIC inzwischen sehr schätzen gelernt. :wink:

Code: Alles auswählen

format binary as 'rom'
romimage:
file '../ROM/zx81.rom'
loadpfile:
        OUT     ($FD),A
        LD      DE,$4009
        LD      HL,.data
        LD      BC,.dataend-.data
        LDIR
        JP      $0207
.data:
file 'PB16K.P'
.dataend:
Das obige Programm lädt zunächst das Original ZX81 ROM (8k) und anschließend wird ein kurzes Programm integriert, welches eine nachfolgende Datei (.p File) in den Arbeitsspeicher lädt, genauso wie dies das LOAD Kommando ermöglicht. Das Power BASIC ist die Datei PB16K.P welches von der o.g. Website downgeloaded werden kann. Der Aufruf des Loaders erfolgt als USR Kommando und ist wahlweise als PRINT USR oder RAND USR möglich. Beim Testen bin ich des Öfteren auf komische Fehlermeldungen gestoßen bei der Verwendung von RAND USR und manche Programme mögen kein PRINT USR, zumindest hinterläßt es unschön auf dem Bildschirm den Rückgabewert. Ich persönlich arbeite beim Aufruf daher am Liebsten mit einer Variablenzuweisung wie LET L=USR 8192.

8192 ist die erste Adresse nach dem 8k ROM und die EInsprungadresse der kleinen Routine loadpfile. Diese schaltet eigentlich nur den NMI Generator ab, um Abstürze beim Laden zu vermeiden und führt im Wesentlichen einen LDIR Befehl aus und setzt vorher die benötigten Register. LDIR schreibt die Datei direkt in den Speicher ab Adresse $4009 und springt anschließend in die ROM Routine "SLOW/FAST" die den Ausgabemodus entsprechend den Einstellungen im geladenen Programm setzt (in der Regel Aktivierung des SLOW Mode). LDIR erwartet in DE die Zieladresse, in HL die Quelladresse und in BC die Größe des zu übertragenden Speicherbereichs. LDIR arbeit immer aufsteigend (Increment) - alternativ gibt es auch die umgekehrte Variante LDDR (Decrement) jedoch sind die da die Register anders zu setzen. Die Verwendung von LDIR oder LDDR spielt nur eine Rolle bei sich überschneidenden Speicherbereichen, was hier aber nicht der Fall ist.

So weit so gut. Vermutlich möchte der eine oder andere das auch gerne testen, und dies geht eigentlich wunderbar mit dem Emulator Eighty One. Dieser kann beliebige ROMs laden und auch eigene oder modifizierte ROMs. Das ist auch eine wunderbare Gelegenheit dem Emulator im Alltag Dinge beizubringen, die er so nicht kann bzw. benötigte Programme automatisch mitzuladen wie z.B. auch HRG. Dazu weiter unten mehr.
zx81auto1.jpg
zx81auto1.jpg (45.96 KiB) 14332 mal betrachtet
zx81auto2.jpg
zx81auto2.jpg (50.59 KiB) 14332 mal betrachtet
zx81auto3.jpg
zx81auto3.jpg (58.94 KiB) 14332 mal betrachtet
So kann man ein selbst erstelltes ROM laden.
Options->Hardware->Advanced Settings->ROM File

Ideal eignet sich der Emulator auch bei der Entwicklung eigener ROMs oder Abänderung des ZX81 ROMs. Dabei wird der Emulator automatisch mit F8 das zuletzt aktivierte ROM beim Starten laden, hier z.B. das ZX81AUTO.ROM und jede Änderung im Quellcode in der ZX-IDE kann mit einem Tastendruck (F8) und normalerweise innerhalb von 1 bis 2 Sekunden automatisch geladen werden. Bequemer gehts nicht mehr und das ist für mich ein wahnsinnig nützliches Feature bei der Entwicklung des ZXmore. Damit der Emulator das zuletzt geladene ROM File dauerhaft in den Einstellungen speichert, ist es notwendig den Emulator nach dem Laden einmal ordnungsgemäß zu Beenden (Schließen). Danach ruft er beim Neustart automatisch das zuletzt eingestellte ROM File auf.
Zuletzt geändert von PokeMon am 14.05.2015, 00:35, insgesamt 1-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 Power BASIC

Beitrag von PokeMon » 14.05.2015, 00:34

zx81auto4.jpg
zx81auto4.jpg (40.71 KiB) 14333 mal betrachtet
Nach dem Einstellen der ROM Datei kann ich nun bequem das Programm Power BASIC mit LET L=USR 8192 laden.
zx81auto5.jpg
zx81auto5.jpg (52.91 KiB) 14333 mal betrachtet
Das Programm wird nicht automatisch aufgerufen, es ist daher noch RUN einzugeben.
zx81auto6.jpg
zx81auto6.jpg (69.85 KiB) 14333 mal betrachtet
Danach begrüßt das Programm mit einer Hilfeanleitung (ausführlich als PDF auf der o.g. Website), die auch jederzeit mit **HELP oder **? aufgerufen werden kann. Wichtig ist das Potenz Symbol (SHIFT-H) zu verwenden und nicht 2x * (SHIFT-B).
Zuletzt geändert von PokeMon am 14.05.2015, 00:51, insgesamt 1-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ZX81 Power BASIC (2)

Beitrag von PokeMon » 14.05.2015, 00:46

Nach dem Aufruf hat sich das Programm automatisch über RAMTOP installiert, so dass man anschließend mit NEW ein leeres BASIC starten kann und der verfügbare Speicher angezeigt wird, in diesem Fall etwa 12k.
zx81auto7.jpg
zx81auto7.jpg (47.2 KiB) 14334 mal betrachtet
Die zusätzlichen Befehle können wie unten angezeigt einfach eingegeben werden, dabei ist einfach nach Eingabe der Zeilennummer SHIFT-H zu drücken (**) und dann im Klartext bzw. einzelnen Buchstaben eines der zusätzlichen BASIC Kommandos. Anschließend wird dieses ** entfernt, so dass ein gültiges BASIC Programm entsteht, siehe Listing unten.
zx81auto8.jpg
zx81auto8.jpg (53.42 KiB) 14334 mal betrachtet
Man kann auch ein vorhandenes BASIC Programm mit LOAD laden und bearbeiten. BASIC Programme mit diesen Zusatzkommandos funktionieren nur mit Power BASIC und führen bei normalen Sinclair BASIC zu einer Fehlermeldung.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ROM Patches - am Beispiel MS HRG / virtual address space

Beitrag von PokeMon » 16.05.2015, 01:16

Manchmal ist es sinnvoll, Programme oder auch ROMs zu patchen, insbesondere wenn der Quellcode nicht vorliegt, wohl aber Einsprungadressen oder Parameter oder Ähnliches. Tun wir also einfach mal so, als stünde das ZX81 ROM als Assemblerlisting zum Neu Compilieren nicht zur Verfügung. :wink:

Die Herausforderung sind jetzt 2 Programme unterzubringen und den Aufwand für das Laden zu minimieren auf einen Knopf- bzw. Tastendruck. Benötigt wird das Ganze für das schöne HRG von der o.g. Website.
http://www.swatosch.de/zx81/

Um das Demoprogramm für das Zeichnen einer Sinuskurve zu starten braucht man zunächst als Grundlage den HRG Treiber. Dieser muss separat installiert werden, es sind also 2 Ladevorgänge notwendig für 2 Programme. Zusätzlich soll das Laden selbst vereinfacht werden und dazu die selten benutzten Befehle LPRINT und LLIST benutzt werden. Zu guter letzt wird noch der angezeigte Name der Kommandos LPRINT und LLIST auf HRG und DEMO1 geändert.
Und so gehts, zunächst schrittweise und am Ende dieses Beitrags ist das gesamte Programm zu finden.

Der Anfang ist noch recht einfach und zu den vorangegangenen Beispielen identisch:

Code: Alles auswählen

format binary as 'rom'
romimage:
file '../ROM/zx81.rom'
prog1:
        LD      HL,drvend
        LD      DE,drv
        JR      loadpfile
prog2:
        LD      HL,demoend
        LD      DE,demo
        JR      loadpfile

loadpfile:
        OUT     ($FD),A
        AND     A
        SBC     HL,DE
        LD      B,H
        LD      C,L
        EX      DE,HL
        LD      DE,$4009
        LDIR
        JP      $0207

drv:
file 'hrg-16k.P'
drvend:
demo:
file 'sinus.P'
demoend:
Zunächst wird das ZX81 ROM geladen, dann folgen 2 Mini Routinen, die den eigentlichen Lader (hier etwas anders aufgebaut) aufrufen. Man kann so auch drei, vier oder fünf Programme laden abhängig vom zur Verfügung stehenden Speicher bzw. Programmgröße. Das Gesamtbeispiel paßt wieder gut in ein 16k ROM. Im HL bzw. DE Register wird zunächst die Anfangs- und Endeadresse vom HRG Treiber und Demo Programm geladen. Im Prinzip funktioniert das schon, wenn man die entsprechenden Einsprungadressen für prog1 und prog2 kennt und diese wieder mit LET L=USR .... starten kann. Wenn man sich die Adressen merken kann.

Anschließend folgt nun die Patchroutine, die hinten drangehängt wird:

Code: Alles auswählen

;LISTON
define DEFB db
define DEFW dw

virtual at 0
        patcharea::
        first:
;; P-LPRINT
L0CB4:  DEFB    $00             ; Class-00 - No further operands.
        DEFW    prog1           ; Address: $0ACB; Address: LPRINT
;; P-LLIST
L0CB7:  DEFB    $03             ; Class-03 - A numeric expression may follow
                                ; else default to zero.
        DEFW    prog2           ; Address: $072C; Address: LLIST
        last:
end virtual

repeat last - first
       load x byte from patcharea:%-1
       store byte x at $0CB4+%-1
end repeat
Hier ist ein Code Abschnitt aus dem ZX81 ROM (zu finden online unter http://www.wearmouth.demon.co.uk/zx81.htm ) verwendet, und zwar aus den Syntax Tabellen kopiert. Diese legen recht clever fest, wie ein BASIC Keyword aufgebaut ist, also welche Parameter zulässig sind oder benötigt werden und wo die eigentliche Einsprungadresse im ROM sitzt. Das erste Byte legt die Syntax fest. Das folgende WORT (nicht BYTE) legt die Adresse fest.
Class-00 - No further operands.
Class-01 - A variable is required.
Class-03 - A numeric expression may follow (default zero)
Class-04 - A single character variable must follow
Class-05 - Variable syntax checked entirely by routine
Class-06 - A numeric expression must follow
Im Beispiel oben wurde bei LPRINT das Syntaxbyte auf 0 geändert, für LLIST wurde es auf 03 (optional ein numerischer Ausdruck) gelassen. Ein numerischer Ausdruck kann ggf. dann auch ausgewertet werden, allerdings soll das nicht zu kompliziert werden in dem Beispiel. Es reicht aus lediglich die beiden Kommandos ohne weitere Parameter aufzurufen. Natürlich kann man auch andere Kommandos verwenden wie LOAD oder SAVE aber die könnten natürlich auch gebraucht werden und LPRINT und LLIST sind doch nicht häufig benutzt.

Die beiden define Anweisungen sind Syntaxkorrekturen zwischen dem TASM und der ZX-IDE (DEFB=db und DEFW=dw). Der interessante Teil kommt nun in dem Block virtual, der es erlaubt Assembleranweisungen zu assemblieren, ohne sie tatsächlich in das aktuelle Programm einzubauen oder hinten dranzuhängen. Dennoch kann man diesen assemblierten Block zugreifen und daher der Name virtual. Wir erzeugen hier sozusagen einen gepatchten Block, den wir anschließend in dem ROM überschreiben. virtual at 0 legt übrigens die Adresse des Adressspace fest, das entspricht in normalen Programmen einen ORG (Offset) für den Programmcounter. Normalerweise müsste dieser auf den tatsächlichen Adressbereich gesetzt werden, solange aber keine Adressen sondern nur Datenbytes erzeugt werden, kann man darauf verzichten. :wink:

Die folgende repeat Anweisung greif auf den Block (in FASM Jargon virtual adress space) byte für byte zu mit load und einem frei erfundenen Variablennamen (hier x) und schreibt es an Adresse $0CB4. last-first ermittelt hier die Größe des virtuellen Adressspace und da repeat hier mit 1-6 arbeitet, muss man beim 1 subtrahieren um die gültigen Adressen von 0-5 zu erhalten.

Hier kommt nun der zweite Block, der im ROM den ausgegebenen Text für ein Token festlegt:

Code: Alles auswählen

virtual at 0
        patcharea2::
        first2:
;; TOKENS
L0111:  DEFB    $0F+$80                         ; '?'+$80
        DEFB    $0B,$0B+$80                     ; ""
        DEFB    $26,$39+$80                     ; AT
        DEFB    $39,$26,$27+$80                 ; TAB
        DEFB    $0F+$80                         ; '?'+$80
        DEFB    $28,$34,$29,$2A+$80             ; CODE
        DEFB    $3B,$26,$31+$80                 ; VAL
        DEFB    $31,$2A,$33+$80                 ; LEN
        DEFB    $38,$2E,$33+$80                 ; SIN
        DEFB    $28,$34,$38+$80                 ; COS
        DEFB    $39,$26,$33+$80                 ; TAN
        DEFB    $26,$38,$33+$80                 ; ASN
        DEFB    $26,$28,$38+$80                 ; ACS
        DEFB    $26,$39,$33+$80                 ; ATN
        DEFB    $31,$33+$80                     ; LN
        DEFB    $2A,$3D,$35+$80                 ; EXP
        DEFB    $2E,$33,$39+$80                 ; INT
        DEFB    $38,$36,$37+$80                 ; SQR
        DEFB    $38,$2C,$33+$80                 ; SGN
        DEFB    $26,$27,$38+$80                 ; ABS
        DEFB    $35,$2A,$2A,$30+$80             ; PEEK
        DEFB    $3A,$38,$37+$80                 ; USR
        DEFB    $38,$39,$37,$0D+$80             ; STR$
        DEFB    $28,$2D,$37,$0D+$80             ; CHR$
        DEFB    $33,$34,$39+$80                 ; NOT
        DEFB    $17,$17+$80                     ; **
        DEFB    $34,$37+$80                     ; OR
        DEFB    $26,$33,$29+$80                 ; AND
        DEFB    $13,$14+$80                     ; <=
        DEFB    $12,$14+$80                     ; >=
        DEFB    $13,$12+$80                     ; <>
        DEFB    $39,$2D,$2A,$33+$80             ; THEN
        DEFB    $39,$34+$80                     ; TO
        DEFB    $38,$39,$2A,$35+$80             ; STEP
        DEFB    $00,$2D,$37,$2C,$00,$00+$80     ; HRG (LPRINT)
        DEFB    $29,$2A,$32,$34,$1D+$80         ; DEMO1 (LLIST)
        DEFB    $38,$39,$34,$35+$80             ; STOP
        DEFB    $38,$31,$34,$3C+$80             ; SLOW
        DEFB    $2B,$26,$38,$39+$80             ; FAST
        DEFB    $33,$2A,$3C+$80                 ; NEW
        DEFB    $38,$28,$37,$34,$31,$31+$80     ; SCROLL
        DEFB    $28,$34,$33,$39+$80             ; CONT
        DEFB    $29,$2E,$32+$80                 ; DIM
        DEFB    $37,$2A,$32+$80                 ; REM
        DEFB    $2B,$34,$37+$80                 ; FOR
        DEFB    $2C,$34,$39,$34+$80             ; GOTO
        DEFB    $2C,$34,$38,$3A,$27+$80         ; GOSUB
        DEFB    $2E,$33,$35,$3A,$39+$80         ; INPUT
        DEFB    $31,$34,$26,$29+$80             ; LOAD
        DEFB    $31,$2E,$38,$39+$80             ; LIST
        DEFB    $31,$2A,$39+$80                 ; LET
        DEFB    $35,$26,$3A,$38,$2A+$80         ; PAUSE
        DEFB    $33,$2A,$3D,$39+$80             ; NEXT
        DEFB    $35,$34,$30,$2A+$80             ; POKE
        DEFB    $35,$37,$2E,$33,$39+$80         ; PRINT
        DEFB    $35,$31,$34,$39+$80             ; PLOT
        DEFB    $37,$3A,$33+$80                 ; RUN
        DEFB    $38,$26,$3B,$2A+$80             ; SAVE
        DEFB    $37,$26,$33,$29+$80             ; RAND
        DEFB    $2E,$2B+$80                     ; IF
        DEFB    $28,$31,$38+$80                 ; CLS
        DEFB    $3A,$33,$35,$31,$34,$39+$80     ; UNPLOT
        DEFB    $28,$31,$2A,$26,$37+$80         ; CLEAR
        DEFB    $37,$2A,$39,$3A,$37,$33+$80     ; RETURN
        DEFB    $28,$34,$35,$3E+$80             ; COPY
        DEFB    $37,$33,$29+$80                 ; RND
        DEFB    $2E,$33,$30,$2A,$3E,$0D+$80     ; INKEY$
        DEFB    $35,$2E+$80                     ; PI
        last2:
end virtual

repeat last2 - first2
       load x byte from patcharea2:%-1
       store byte x at $0111+%-1
end repeat
Die Token Tabelle steht im ROM auf Adresse $0111 und hier ist streng darauf zu achten, den Code weder zu kürzen noch länger zu machen sondern nur den Text von LPRINT und LLIST zu überschreiben mit den Keywords " HRG " und "DEMO1". Das muss man im ZX Zeichensatz machen und das letzte Byte enthält quasi als Stop Character den inversen Character (+$80).

So nun bringen wir alle 3 Teile zusammen:

Code: Alles auswählen

format binary as 'rom'
romimage:
file '../ROM/zx81.rom'
prog1:
        LD      HL,drvend
        LD      DE,drv
        JR      loadpfile
prog2:
        LD      HL,demoend
        LD      DE,demo
        JR      loadpfile

loadpfile:
        OUT     ($FD),A
        AND     A
        SBC     HL,DE
        LD      B,H
        LD      C,L
        EX      DE,HL
        LD      DE,$4009
        LDIR
        JP      $0207

drv:
file 'hrg-16k.P'
drvend:
demo:
file 'sinus.P'
demoend:

;LISTON
define DEFB db
define DEFW dw

virtual at 0
        patcharea::
        first:
;; P-LPRINT
L0CB4:  DEFB    $00             ; Class-00 - No further operands.
        DEFW    prog1           ; Address: $0ACB; Address: LPRINT
;; P-LLIST
L0CB7:  DEFB    $03             ; Class-03 - A numeric expression may follow
                                ; else default to zero.
        DEFW    prog2           ; Address: $072C; Address: LLIST
        last:
end virtual

repeat last - first
       load x byte from patcharea:%-1
       store byte x at $0CB4+%-1
end repeat

virtual at 0
        patcharea2::
        first2:
;; TOKENS
L0111:  DEFB    $0F+$80                         ; '?'+$80
        DEFB    $0B,$0B+$80                     ; ""
        DEFB    $26,$39+$80                     ; AT
        DEFB    $39,$26,$27+$80                 ; TAB
        DEFB    $0F+$80                         ; '?'+$80
        DEFB    $28,$34,$29,$2A+$80             ; CODE
        DEFB    $3B,$26,$31+$80                 ; VAL
        DEFB    $31,$2A,$33+$80                 ; LEN
        DEFB    $38,$2E,$33+$80                 ; SIN
        DEFB    $28,$34,$38+$80                 ; COS
        DEFB    $39,$26,$33+$80                 ; TAN
        DEFB    $26,$38,$33+$80                 ; ASN
        DEFB    $26,$28,$38+$80                 ; ACS
        DEFB    $26,$39,$33+$80                 ; ATN
        DEFB    $31,$33+$80                     ; LN
        DEFB    $2A,$3D,$35+$80                 ; EXP
        DEFB    $2E,$33,$39+$80                 ; INT
        DEFB    $38,$36,$37+$80                 ; SQR
        DEFB    $38,$2C,$33+$80                 ; SGN
        DEFB    $26,$27,$38+$80                 ; ABS
        DEFB    $35,$2A,$2A,$30+$80             ; PEEK
        DEFB    $3A,$38,$37+$80                 ; USR
        DEFB    $38,$39,$37,$0D+$80             ; STR$
        DEFB    $28,$2D,$37,$0D+$80             ; CHR$
        DEFB    $33,$34,$39+$80                 ; NOT
        DEFB    $17,$17+$80                     ; **
        DEFB    $34,$37+$80                     ; OR
        DEFB    $26,$33,$29+$80                 ; AND
        DEFB    $13,$14+$80                     ; <=
        DEFB    $12,$14+$80                     ; >=
        DEFB    $13,$12+$80                     ; <>
        DEFB    $39,$2D,$2A,$33+$80             ; THEN
        DEFB    $39,$34+$80                     ; TO
        DEFB    $38,$39,$2A,$35+$80             ; STEP
        DEFB    $00,$2D,$37,$2C,$00,$00+$80     ; HRG (LPRINT)
        DEFB    $29,$2A,$32,$34,$1D+$80         ; DEMO1 (LLIST)
        DEFB    $38,$39,$34,$35+$80             ; STOP
        DEFB    $38,$31,$34,$3C+$80             ; SLOW
        DEFB    $2B,$26,$38,$39+$80             ; FAST
        DEFB    $33,$2A,$3C+$80                 ; NEW
        DEFB    $38,$28,$37,$34,$31,$31+$80     ; SCROLL
        DEFB    $28,$34,$33,$39+$80             ; CONT
        DEFB    $29,$2E,$32+$80                 ; DIM
        DEFB    $37,$2A,$32+$80                 ; REM
        DEFB    $2B,$34,$37+$80                 ; FOR
        DEFB    $2C,$34,$39,$34+$80             ; GOTO
        DEFB    $2C,$34,$38,$3A,$27+$80         ; GOSUB
        DEFB    $2E,$33,$35,$3A,$39+$80         ; INPUT
        DEFB    $31,$34,$26,$29+$80             ; LOAD
        DEFB    $31,$2E,$38,$39+$80             ; LIST
        DEFB    $31,$2A,$39+$80                 ; LET
        DEFB    $35,$26,$3A,$38,$2A+$80         ; PAUSE
        DEFB    $33,$2A,$3D,$39+$80             ; NEXT
        DEFB    $35,$34,$30,$2A+$80             ; POKE
        DEFB    $35,$37,$2E,$33,$39+$80         ; PRINT
        DEFB    $35,$31,$34,$39+$80             ; PLOT
        DEFB    $37,$3A,$33+$80                 ; RUN
        DEFB    $38,$26,$3B,$2A+$80             ; SAVE
        DEFB    $37,$26,$33,$29+$80             ; RAND
        DEFB    $2E,$2B+$80                     ; IF
        DEFB    $28,$31,$38+$80                 ; CLS
        DEFB    $3A,$33,$35,$31,$34,$39+$80     ; UNPLOT
        DEFB    $28,$31,$2A,$26,$37+$80         ; CLEAR
        DEFB    $37,$2A,$39,$3A,$37,$33+$80     ; RETURN
        DEFB    $28,$34,$35,$3E+$80             ; COPY
        DEFB    $37,$33,$29+$80                 ; RND
        DEFB    $2E,$33,$30,$2A,$3E,$0D+$80     ; INKEY$
        DEFB    $35,$2E+$80                     ; PI
        last2:
end virtual

repeat last2 - first2
       load x byte from patcharea2:%-1
       store byte x at $0111+%-1
end repeat
Und nun laden wir das ROM im Emulator. Zuunächst wird die ROM Datei wie oben beschrieben abgeändert, zusätzlich sollte aber noch HRG, hier WRX im Emulator aktiviert werden. Das war es dann auch schon. Das BASIS ZX81 ROM kann ganz normal verwendet werden. Der Aufruf von LPRINT (SHIFT+S) bringt dann das Wort HRG und mit Enter wird ganz einfach der HRG Treiber aus dem ROM geladen:
HRG1.jpg
HRG1.jpg (49.08 KiB) 14300 mal betrachtet
HRG2.jpg
HRG2.jpg (52.75 KiB) 14300 mal betrachtet
Ein Tastendruck auf SPACE kehrt zur Kommandozeile zurück. Hier kann man getrost NEW eingeben und hat den HRG Treiber immer zur Verfügung oder auch gleich mit SHIFT+G (LLIST) dann das DEMO1 aufrufen und mit Enter laden.
HRG3.jpg
HRG3.jpg (49.77 KiB) 14300 mal betrachtet
Zuletzt geändert von PokeMon am 16.05.2015, 01:29, insgesamt 1-mal geändert.
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

ROM Patches - am Beispiel MS HRG

Beitrag von PokeMon » 16.05.2015, 01:23

Ein RUN startet das Demoprogramm, welches hier eine Sinuskurve zeichnet:
hrg4.jpg
hrg4.jpg (53.86 KiB) 14301 mal betrachtet
Mit SPACE kann man das Programm unterbrechen, dabei bleibt der Grafikbildschirm jedoch erhalten und mit den Tasten 9+0 gleichzeitig gedrückt kann man zwischen TEXT (Listing) und GRAFIK jederzeit wechseln. Weitere Infos zum HRG Programm finden sich auf der o.g. Website.
hrg5.jpg
hrg5.jpg (79.75 KiB) 14301 mal betrachtet
Ziel dieses Beitrags war zu erklären, wie man virtuelle Adress Spaces nutzen kann und wie man ROMs oder Programme gezielt verändern (Patchen) und ggf. auch mit Parametern und Berechnungen dynamisch anpassen kann. Und wie man coole Kommandos ins ROM bringen kann und die Tokens verändern und nette Phantasienamen wir POKEMON oder ähnlich reinpatchen kann. Bei der o.g. Tabelle ist nur wichtig, dass die gesamte Größe stimmt. Wenn man bestimmte Tokens kürzt, können andere auch länger ausfallen.
8)
Wer seinen Computer ehrt, lebt nicht verkehrt.

Benutzeravatar
PokeMon
User
Beiträge: 4478
Registriert: 31.08.2011, 23:41

Re: ZX-IDE in der Praxis, Tipps & Tricks für ZX81,ZX80

Beitrag von PokeMon » 05.01.2016, 01:56

Im ersten Posting dieses Threads befindet sich eine aktualisierte Version der ZX-IDE (FASMW-ZX) als Bugfix Release.
ZX-IDE 1.71.01q.Z80 (01/2016)

Folgende fehlende "undokumentierte" Opcodes wurde ergänzt:

Code: Alles auswählen

LD IXh,n
LD IXl,n
LD IYh,n
LD IYl,n
Außerdem wurde der Parser geändert, was auch recht wilde Konstrukte ermöglicht wie:

Code: Alles auswählen

63 LET DI=1-(2 AND(L=20 OR INKEY$="M"))
Wer seinen Computer ehrt, lebt nicht verkehrt.

Antworten