Hallo Besucher, der Thread wurde 3,8k mal aufgerufen und enthält 21 Antworten

letzter Beitrag von SuperIlu am

z80 Assembler Anfaengerfragen

  • Moins,


    ich lerne grade Z80-Assembler am Beispiel eines ROM-Monitors fuer meinen Z80-Selbstbau.
    Dabei stosse ich hin&wieder auf Fragen wie man bestimmt Dinge am besten in Z80-Assembler implementiert.
    Aktuell: Ich habe eine Kopierfunktion die bekommt drei Adressen uebergeben (Start, Ende und Ziel) und soll jetzt von Start bis Ende laufen.


    Wie kann ich am besten das Endekriterium abfragen? Ich habe keine Moeglichkeit gefunden zwei 16-Bit Register mit einander zu vergleichen.
    Meine derzeitige Loesung siehtwie unten aus, geht das eleganter? :hae:


  • LDIR und ähnliche Befehle verwenden. Die benötigen in HL, DE und BC ihre Daten und laufen automatisch bis zum Ende durch.
    Bei kleinen Schleifen kann man auch DJNZ verwenden.
    Schau mal auf der F64 Wolke nach Z80 Literatur. Da sollte genug in Deutsch vorhanden sein.


    http://www.cpcwiki.eu/index.php/Z80

  • ah danke, LDDR und LDIR waren mir bisher entgangen :)

    Wenn Du vom 6502 kommst... der Z80 kann/macht einiges mehr/anders. Daran muss man sich erstmal dran gewöhnen.

  • Ich *hust* komme (wenn ueberhaupt) vom m68k. Seit dem habe ich zwar noch bruchstueckhaft SPARC und ARM Assembler gemacht, aber im Grossen und Ganzen nur C und aufwaerts :)

    Da kenn ich ein bisschen den Weg vom Z80 -> 68k und hatte mich damals sehr schwer getan.

  • Ich bin jetzt auch nicht gerade ein Hardcore-Z80-Coder und wage nur eher einfache Ratschläge diesbezüglich zu erteilen, aber vielleicht einige Anmerkungen zum Ganzen:

    • Das Parsing der Argumente: da wiederholt sich die Überprüfung auf BLANK immer wieder. Das würde ich auch in ein Unterprogramm auslagern. Da kann man dann auch in einer Schleife auch mehrere Leerzeichen akzeptieren, was die Syntax etwas entspannt. Einen Fehler kann man ja mit dem Carry retournieren und individuell behandeln.
    • Beim Kopieren sollte man generell auch eine etwaige Überlappung berücksichtigen und entsprechend "von oben" oder "von unten" kommend übertragen, sonst artet eine solche Aktion in eine mehr oder weniger arge Datenvernichtung aus.

    Beim Durchlesen der Frage fiel mir doch noch etwas zu der Endebedingung ein, weil ich das immer wieder gesehen habe bzw. auch für andere CPUs oft gilt: Für solche Schleifen am Besten einen Zähler für die Anzahl der Durchläufe (zu übertragenden Bytes) ermitteln. Denn kann man dann bequem runterzählen und relativ "billig" auf 0 überprüfen (wenn man nicht gerade LDIR und Verwandte verwendet), z.B.

    Code
    1. loop:
    2. ...
    3. dec bc
    4. ld a,b
    5. or c
    6. jp nz,loop

    Es gibt auch noch die Speed-Variante. In BC ist die Anzahl, die für das Herunterzählen so korrigiert wird, dass der Unterlauf auf das High-Byte beim Übergang von 1 auf 0 (statt sonst von 0 auf 255) passiert.

    Code
    1. ld a,b
    2. jp z,nocorr ; wenn Low-Byte <> 0
    3. inc c ; High-Byte korrigieren
    4. nocorr:
    5. ...
    6. loop:
    7. ...
    8. djnz loop ; Low-Byte herunterzählen
    9. dec c ; High-Byte herunterzählen
    10. jp nz,loop
  • Danke fuer die Anmerkungen.


    Das Parsing der Argumente: da wiederholt sich die Überprüfung auf BLANK immer wieder. Das würde ich auch in ein Unterprogramm auslagern. Da kann man dann auch in einer Schleife auch mehrere Leerzeichen akzeptieren, was die Syntax etwas entspannt. Einen Fehler kann man ja mit dem Carry retournieren und individuell behandeln.

    Was wuerde das Unterprogramm im Ablauf verbessern? AFAIK verbraucht der SPACE-check 5-6 Byte, ein einzelnes CALL benoetigt schon 3 Byte, der bedingte Sprung wg. CF dann noch mal 2-3 Byte. Ausser der von Dir angesprochenen Funktionalitaet das ich auch mal mehrere SPACE weg'parsen' kann gewinne ich also nichts.
    Da ich auf absehbare Zeit der einzige Anwender fuer den Monitor bin kann ich auf die entspannte Syntax erst mal verzichten. Zumal ich das vermutlich eh sehr schnell mit Python scripten werde :)


    Beim Kopieren sollte man generell auch eine etwaige Überlappung berücksichtigen und entsprechend "von oben" oder "von unten" kommend übertragen, sonst artet eine solche Aktion in eine mehr oder weniger arge Datenvernichtung aus.

    Du hast voellig recht, ich schau mal das ich eine Fallunterscheidung fuer SADDR>DADDR und SADDR<DADDR einbaue. Ich sehe mich naemlich schon Fehler jagen wenn ich das das erste Mal falsch benutze. Da hab ich ein Haendchen fuer :)



    Beim Durchlesen der Frage fiel mir doch noch etwas zu der Endebedingung ein, weil ich das immer wieder gesehen habe bzw. auch für andere CPUs oft gilt: Für solche Schleifen am Besten einen Zähler für die Anzahl der Durchläufe (zu übertragenden Bytes) ermitteln. Denn kann man dann bequem runterzählen und relativ "billig" auf 0 überprüfen (wenn man nicht gerade LDIR und Verwandte verwendet)

    @oobdoo erwaehnte ja schon LDIR und LDDR. Die Kommandozeilensyntax mit SADDR und EADDR habe ich mir bei einem anderen Monitor abgeguckt und finde die recht nett so, deswegen habe ich mich fuer Endadressen statt Groessenangaben entschieden. Sowohl fuer die LDIR/LDDR, als auch fuer Deine Variante muesste ich jetzt die Endadresse erst "umstaendlich" in eine Groesse umrechnen.
    Dein Codebeispiel mit dec, ld und or habe ich an anderer Stelle (wo ich mit Groessen arbeite) genau so stehen :)

  • Bitte, ich möchte niemanden überzeugen oder überreden etwas zu verwenden, das ist nur Anregungen. Es is tvöllig freigestellt es zu verwenden oder auch nicht. Außerdem sind diese öffentlich, können und sollen auch von anderen gelesen werden, die vielleicht ähnliche Fragestellungen haben und auf der Suche sind.

    Was wuerde das Unterprogramm im Ablauf verbessern? AFAIK verbraucht der SPACE-check 5-6 Byte, ein einzelnes CALL benoetigt schon 3 Byte, der bedingte Sprung wg. CF dann noch mal 2-3 Byte. Ausser der von Dir angesprochenen Funktionalitaet das ich auch mal mehrere SPACE weg'parsen' kann gewinne ich also nichts.
    Da ich auf absehbare Zeit der einzige Anwender fuer den Monitor bin kann ich auf die entspannte Syntax erst mal verzichten. Zumal ich das vermutlich eh sehr schnell mit Python scripten werde

    Normalerweise geht es bei Monitoren eher sehr beengt von Platzanforderungen her zu (jetzt ungeachtet deiner konkreten Randbedingungen). Da darf ein Hinweis zur Faktorisierung schon gestattet sein. Wenn man vielleicht von mehreren Kommandos ausgehen würde, die alle dieses Parsing betreiben, dann multipliziert sich das entsprechend, selbst wenn nur ein Byte Ersparnis wäre. BTW, das INC HL könnte man auch reinpacken. Wie gesagt, es war eigentlich eine allgemeine Überlegung und keine auskalkulierte Lösung. Ein CALL mit JR braucht fix 5 Bytes im Vergleich zu den 6 (mit dem INC HL), oder? Die Faktorisierung, egal ob mit Unterprogrammen oder auch mit Makros (wenn es die Geschwindigkeit erfordern sollte) hat auch hinsichtlich Fehlerträchtigkeit beim Programmieren selbst seine Vorteile.



    @oobdoo erwaehnte ja schon LDIR und LDDR. Die Kommandozeilensyntax mit SADDR und EADDR habe ich mir bei einem anderen Monitor abgeguckt und finde die recht nett so, deswegen habe ich mich fuer Endadressen statt Groessenangaben entschieden. Sowohl fuer die LDIR/LDDR, als auch fuer Deine Variante muesste ich jetzt die Endadresse erst "umstaendlich" in eine Groesse umrechnen.
    Dein Codebeispiel mit dec, ld und or habe ich an anderer Stelle (wo ich mit Groessen arbeite) genau so stehen

    Klar, @oobdoo hat es ja schon erwähnt. Es war nur eine notwendige Referenz auf seine Erwähnung am Rande. Es lag nicht in meiner Absicht die "Lorbeeren" @oobdoos irgendwie wegzuschnappen. :huh: Die Urheberrechte wird er wohl auch nicht auf die Worte einfordern können. :rolleyes:
    Das ist ja üblich, dass Monitore eine Start- und Endadresse für einen Bereich erwarten. Das schließt ja nicht aus, dass man die Länge berechnen kann. Umständlich? Der Z80 hat doch 16-Bit-Arithmetik, wo das ziemlich unaufwändig geht, oder. Aber selbst beim 6502, wo man das byte-weise machen muss, lohnt sich das. Wenn man wegen der Überlappung herumrechnet, dann braucht man die Länge ohnehin oder ergibt sich nebenbei.
    Ich hab ja nicht behauptet die Codebeispiele seien ein Novum, genau das Gegenteil (zumindest die 1. Variante, die 2. Variante hab in freier Wildbahn ehrlich gesagt noch nicht gesehen, wobei mir aber nur sporadisch Z80-Code unterkommt und ich das in Wirklichkeit schlecht beurteilen kann), denn die 1. ist faktisch Standard. Es war ja Eingangs so gefragt, wie man denn das macht, wegen dem Endadressenvergleich - ich wäre fast geneigt zu sagen: gar nicht, wenn es nicht notwendig ist oder mit einem Zähler einfacher oder schneller geht. Aber das können Z80-Gurus vielleicht besser beurteilen. Tut mir leid, das ich mich erdreistet habe, etwas Konkretes anzugeben statt vielleicht noch ein Buch zur Lektüre zu empfehlen... :D

  • Tut mir leid, das ich mich erdreistet habe, etwas Konkretes anzugeben statt vielleicht noch ein Buch zur Lektüre zu empfehlen...

    Du hast meine Antwort scheinbar falsch verstanden. Ich wollte nur meine Motivation und die Gruende fuer meine Art der Implementation darlegen und nicht kritisieren das Du Vorschlaege machst oder Alternativen anbietest. Ich bin Dankbar fuer Vorschlaege, ich moechte aber trotzdem darlegen wie und warum ich zu meinen Loesungen gekommen bin. Wenn meine Art der 'Diskussionsfuehrung' aneckt, dann tut mir das leid.



    Das ist ja üblich, dass Monitore eine Start- und Endadresse für einen Bereich erwarten. Das schließt ja nicht aus, dass man die Länge berechnen kann. Umständlich? Der Z80 hat doch 16-Bit-Arithmetik, wo das ziemlich unaufwändig geht, oder.

    Genau solche Infos hatte ich mir aus der Diskussion erhofft. Das SBC mit 16-Bit Registern war mir voellig entgangen weil ich in den Referenzen immer nach 'sub' gesucht habe. Mit der Info sieht Dein Vorschlag gleich wieder anders aus.



    [...] SPACE-Parsing [...]

    An Macros hab ich auch schon gedacht, 'Optimierungen' vertage ich aber auf 'spaeter' wenn ich die Funktionalitaet mal im Kasten habe. An die Version mit dem INC HL hab ich noch nicht gedacht, da muss ich noch mal genauer nachsehen. Auc hier gilt wieder: Ich hab nur meine Gruende dargelegt warum ich das mit der Unterfunktion nicht gemacht habe. Ich bin beim ersten Nachrechnen drauf gekommen das der Platzgewinn minimal bis nicht vorhanden waere und auch die Anzahl Codezeilen bei der SPACE Erkennung nicht sinkt. Ich wollte nur rausfinden ob Du evtl. noch mehr Potential siehst als ich.

  • Du hast meine Antwort scheinbar falsch verstanden. Ich wollte nur meine Motivation und die Gruende fuer meine Art der Implementation darlegen und nicht kritisieren das Du Vorschlaege machst oder Alternativen anbietest. Ich bin Dankbar fuer Vorschlaege, ich moechte aber trotzdem darlegen wie und warum ich zu meinen Loesungen gekommen bin. Wenn meine Art der 'Diskussionsfuehrung' aneckt, dann tut mir das leid.

    Offenbar. Kein Problem, ist schon gut so. ;)


    Genau solche Infos hatte ich mir aus der Diskussion erhofft. Das SBC mit 16-Bit Registern war mir voellig entgangen weil ich in den Referenzen immer nach 'sub' gesucht habe. Mit der Info sieht Dein Vorschlag gleich wieder anders aus.

    Ja, irgendwie witzig, diese an diversen Stellen fehlende Symmetrie im Befehlssatz (was recht symptomatisch bei 8-Bittern zu sein scheint).
    ADD HL und ADC HL, dazu aber nur ein SBC HL ... (wenn ich richtig lese).


    An Macros hab ich auch schon gedacht, 'Optimierungen' vertage ich aber auf 'spaeter' wenn ich die Funktionalitaet mal im Kasten habe. An die Version mit dem INC HL hab ich noch nicht gedacht, da muss ich noch mal genauer nachsehen. Auc hier gilt wieder: Ich hab nur meine Gruende dargelegt warum ich das mit der Unterfunktion nicht gemacht habe. Ich bin beim ersten Nachrechnen drauf gekommen das der Platzgewinn minimal bis nicht vorhanden waere und auch die Anzahl Codezeilen bei der SPACE Erkennung nicht sinkt. Ich wollte nur rausfinden ob Du evtl. noch mehr Potential siehst als ich.

    Naja, Makros schöpfen ihren Sinn eigentlich besonders daraus, wenn man sie beim Entwicklen gleich einsetzt ... wenn man es schon mal hat, dann ist es fast nur noch Kosmetik.
    Ja wie gesagt, es gibt ja auch andere Gründe, die ich angeführt habe, die eine Kapselung in Unterprogrammen rechtfertigt - auch ohne Platzgewinn. Schon alleine zur Fehlervermeidung, der Übersichtlichkeit wegen beispielsweise. Wie gesagt, jeder nach seinem Gusto. :D

  • ReTach,


    nach einigen Ausfluegen in die DOS-Welt und einem kurzen Urlaub hab ich mal wieder Zeit fuer den Z80 gehabt.
    Ich habe Eure Anregungen aufgegriffen und umgesetzt und will jetzt mal das Ergebnis praesentieren.


    Der Code ist zwar nicht unbedingt kleiner geworden, aber an einigen Stellen doch lesbarer/praegnanter.

    Danke nochmal
    Ilu