Hallo Besucher, der Thread wurde 54k mal aufgerufen und enthält 88 Antworten

letzter Beitrag von BastetFurry am

Basic-Scrollings

  • Zitat

    Neefi: Wiedermal gut aufgepasst! Es ist doch erstaunlich, wieviel Fehler man selbst nach drei mal durchlesen nicht mitbekommt! Die neugefundenen Fehler von Neefi habe ich korrigiert.


    Hat gar nicht mal unbedingt was mit aufpassen zu tun. Wenn man das ganze (noch) nicht aus dem Schlaf heruas herbeten kann und es nachvollziehen will, kommt man an solchen Stellen einfach mit dem Verständnis ins Stolpern.
    Nichts desto Trotz bin ich begeistert von Deinem Crashkurs. Du könntest ruhig noch ein wenig weitermachen (selbst geänderten Zeichensatz verwenden etc.).

  • Damit nicht jeder nach dem Ass Kurs fragen muss, habe ich diesen auf meinen Webspace geschmissen. Ihr könnt das .TXT File von dort herunterladen:


    http://home.t-online.de/home/bundyman/ass_kurs.txt


    Alle Bugs die bis dato aufgetaucht waren, sind in dieser Version gefixt worden. Die leute die den Kurs schon per Mail bekommen haben, sollten nochmal Updaten.


    Greets

  • So, bin durch mit dem Kurs hier und hab mir gleich ein paar weitere Fragen notiert! :)


    Aber erst nochmal zu hannenz Beispiel mit dem POKEn:
    Sogar das normale PRINTen ist ja um Einiges schneller als mit POKE. Als Beispiel:
    10 PRINT"{SHIFT+CLR/HOME}"
    20 FORA=0TO999: PRINT"*";:NEXTA


    Dann noch zwei kleine Fehler im Kurs:
    1.
    Teil 4a ziemlich mittig in dem Absatz über der BIT-Wertetabelle

    Zitat

    ...Fragen wir uns doch erstmal, warum in eine Speicherzelle gerade dieser dumme Wert 255, die Höchstgrenze ist. Eine Runde Zahl wäre doch viel schöner gewesen. Nun das ganze stark mit den BITS zusammen...


    Hier fehlt das Wort "hängt" im letzten Satz.


    2.
    Teil 5b
    In dem Text, der gescrollt werden soll, kommt ein großes A vor, im Wort Assembler. Das wird dann bei Ausführung des Programms als inverses A angezeigt.
    War das beabsichtigt?
    Wenn nicht, haben wir ja immerhin ein inverses A und nicht ein total anderes Zeichen! :)


    Und nun zu meinen Fragen:
    1.
    Könnte man hinter die Befehle BNE und JMP auch RAM-Adressen schreiben anstatt Lables, wenn man wüsste, in welcher Adresse die betroffene Zeile abgespeichert ist?


    2.
    Teil 5a


    Wieso benutzen wir hier überhaupt ein Lable als Variable, könnte man nicht einfach das X-Register verwenden? Oder noch besser das Y-Register, damit es keine Kollisionen mit den anderen Programmteilen gibt, weil dort das X-Register auch noch vorkommt?
    Ach, und kann man auch Variablen mit anderem Startwert definieren? Z.B. so:
    COUNTER .byte 4
    Letzteres natürlich ganz allgemein gefragt und nicht in Bezug auf das Scrollprogramm.


    3.
    Ich habe leider den letzten Teil des Programms nicht verstanden, und zwar die Routine, um die Buchstaben zu holen.
    Das größte Problem bereitet mir wohl der .text Befehl. Also erstmal ist das ja kein Assembler-Befehl wie LDA usw., sondern eine spezielle Anweisung an den TurboAss. Sehe ich das richtig?
    Aber was macht dieser .text Befehl nun genau? Was macht er mit dem Lable TEXT?
    Einen Hinweis im Kurs gibt es ja:

    Zitat

    ...Der .TEXT Befehl des Turbo Ass Assembliert die Texte nicht als Screencode, sondern als ASCII Werte...


    Na gut, nehmen wir mal den zweiten Buchstaben aus dem Scrolltext, ein "i". Und hier die Routine:

    Zitat

    ;------ neuen Buchstaben holen nach Hardscroll
    LDX TEXTPOS ;aktuelle TEXTPOSition aus Variable holen
    INC TEXTPOS ;TEXTPOS um 1 erhöhen, für den nächsten Buchstaben
    LDA TEXT,X ;TEXT+X ergibt den aktuellen Buchstaben
    STA 1063 ;diesen in letzte Cursorspalte von Textzeile 1 (1024+39=1063)


    Die aktuelle TEXTPOSistion müsste ja 1 sein, weil die Variable TEXTPOS mit 0 initiiert wurde und wir uns aber schon beim "i" befinden. Also hat TEXTPOS momentan den Wert 1, genauso wie das X-Register, nachdem der Befehl LDX TEXTPOS abgearbeitet wurde.
    Dann kommt INC TEXTPOS, ist aber erstmal unwichtig.
    Und nun LDA TEXT,X
    Der ASCII-Wert vom Buchstaben "i" ist nach C64-Handbuch 73 (wo ist denn da das kleine "i"?), also ist TEXT auch 73 und X ist immer noch 1. Und 73+1 ist 74. Aber 74 ist nicht mehr "i" sonder "j".
    Also wo ist mein Denkfehler? Der muss doch irgendwie mit dem .text Befehl zu tun haben!



    Ach und was mir noch an dem letzten Listing in Teil 5a aufgefallen ist:
    Gebt doch mal irgendein Zeichen in Zeile 2 Spalte 1 ein und führt das Programm ein paar Mal aus! :)

  • Zitat

    Könnte man hinter die Befehle BNE und JMP auch RAM-Adressen schreiben anstatt Lables, wenn man wüsste, in welcher Adresse die betroffene Zeile abgespeichert ist?


    Beim JMP-Befehl stellt dies gar kein Problem dar. JMP label ist genauso möglich wie JMP $4711.
    Beim BNE, BEQ etc. siehts da leider etwas anders aus. Hierbei handelt es sich um einen 2-Byte-Befehl. Aber ich fange besser von ganz vorne an: Nochmal zum JMP...ein JMP-Befehl (z.B. JMP $4711) setzt sich aus 3 Bytes zusammen und zwar $4C $11 $47. $4C ist die Byte-Form für JMP und $11 $47 geben die Adresse im Low-High-Byte-Format an.


    Beim BNE, BEQ siehts anders aus. Hier gibt man nicht direkt eine Adresse an. Ein BNE, BEQ-Befehl besteht nicht aus 3 sondern nur aus 2 Bytes. Und zwar im Falle eines BNE $XXXX aus $D0 $XX. $XX gibt hier nicht eine Speicheradresse an, sondern die Entfernung vom BNE-Befehl. Ein Wert von $00-$7F wird addiert und ein Wert von $80-$FF wird sutrahiert. Insofern kann niemals weiter als 128 ($80) Bytes gesprungen werden.


    Da man also immer nachrechnen müßte wieviel bytes nun die geplante Einsprungadresse entfernt ist, ist es einfach sinnvoller Labels zu verwenden.


    Ach, und wenn man mal weiter als 128 Bytes springen muß, dann springt man einfach 'n JMP-Befehl an, der dann die erforderliche Distanz überwindet. ;)


    Zitat

    Wieso benutzen wir hier überhaupt ein Lable als Variable, könnte man nicht einfach das X-Register verwenden? Oder noch besser das Y-Register, damit es keine Kollisionen mit den anderen Programmteilen gibt, weil dort das X-Register auch noch vorkommt?


    Ja, das Y-Register würde gehen, sofern das Y nicht in irgendwelchen anderen Programmteilen verwendet wird. Da dies der Fall sein wird, wenn man anfängt gößere Programme zu schreiben, wurde hier gleich auf ein Label zurückgegriffen. Hier ein Label zu verwenden hat noch eine zweiten Vorteil. Bei der Scroll-Routine wie wir sie jetzt haben, besteht der Nachteil, dass wir nur max. 256 Bytes scrollen können. Logisch, oder? Wenn wir den Counter immer um 1 erhöhen und er bei 255 angelangt ist, geht's wieder mit 0 los. Was ist nun aber, wenn man mehr text scrollen möchte? Dann müßte man eigentlich, wenn man vorher das Y-Register benutzt hat nun auch noch das X-Register benutzen. Um das zu vermeiden, man also alle Register noch zur Verfügung hat, ist es besser mit Labeln zu arbeiten.


    Ich glaube, das ist mal wieder eine nette kleine Aufgabe für die angehenden Assembler-Meister! Also, was müßte man machen, wenn man statt nur 256 Bytes noch mehr Bytes scrollen möchte?


    Zitat

    Ach, und kann man auch Variablen mit anderem Startwert definieren? Z.B. so:
    COUNTER .byte 4


    Jau, von 0 bis 255 ist alles möglich. Nur hätte das in diesem Fall u.U. den Effekt, dass der Text mitten im Satz anfängt loszuscrollen. :)


    Zitat

    Aber was macht dieser .text Befehl nun genau? Was macht er mit dem Lable TEXT?


    Mit dem Label TEXT selbst passiert gar nix. Nur weiß das Assembler-Programm (z.B. Turbo-Ass) nun, dass wenn er das Programm assembliert, also in Assembler umwandelt, dass alles was nach TEXT kommt in ASCII umgewandelt werden soll.


    Zitat

    Die aktuelle TEXTPOSistion müsste ja 1 sein, weil die Variable TEXTPOS mit 0 initiiert wurde und wir uns aber schon beim "i" befinden.


    Genau!


    Zitat

    Also hat TEXTPOS momentan den Wert 1, genauso wie das X-Register, nachdem der Befehl LDX TEXTPOS abgearbeitet wurde.


    Genau!


    Zitat

    Dann kommt INC TEXTPOS, ist aber erstmal unwichtig.


    Genau, ist erst beim nächsten Durchlauf wieder von Bedeutung!


    Zitat

    Und nun LDA TEXT,X


    Ja, also die Speicherstelle von TEXT,X also TEXT+1. Oder anders: Wenn TEXT bei der Speicherstelle $1000 stehen würde, würden wir den Inhalt der Speicherstelle $1001 holen.


    Zitat

    Der ASCII-Wert vom Buchstaben "i" ist nach C64-Handbuch 73, also ist TEXT auch 73 und X ist immer noch 1. Und 73+1 ist 74. Aber 74 ist nicht mehr "i" sonder "j".


    Ne, genau hier ist der Fehler. TEXT ist nicht 73. Sondern TEXT ist eine Speicherstelle, zu der wir 1 addieren. Damit hatte ich zu Anfang auch meine Probleme...wenn man das aber erstmal verinnerlicht hat, ist es ganz simpel.


    Also, vielleicht ist es einfacher zu verstehen, wenn wir wieder davon ausgehen, dass unser Text bei $1000 bis $10FF (256 Bytes also) steht. TEXT wäre also $1000. Der Wert im X wird immer zu $1000 hinzuaddiert. Und dann wird der Wert, der in der entsprechenden Speicherstelle steht in den Akku geladen. Also befindet sich bei obigem Beispiel der Wert der Speicherstelle $1001 im Akku->also 73. Merke: Das ,x hat nie was mit dem INHALT der Speicherstelle zu tun.


    So, ich hoffe ich konnte weiterhelfen...ich bin schon auf die Lösungen zu meiner Frage gespannt. ;)

  • Der Print befehl ist tatsächlich schneller als der Poke, denn die umrechnung der angegebenen Speicheradresse beim Poke dauert ewig lange. Beim Print Befehl, liegt die aktuelle position schon als Pointeradresse vor, und braucht nicht umgerechnet zu werden. Daher ist der Poke langsamer.


    Der Bug in 4A ("hängt") ist gefixt.


    Der Großschrift Bug im Wort "A"ssembler ist gefixt. Nun ist es ein kleines assembler und es erscheint kein Reverses A. Aber die Stelle mit den total anderen Zeichen konnte ich nicht finden.



    Kurz zu deiner Frage, warum zum holen des nächsten Buchstabens, ein Label (Variable im RAM) benutzt worden ist, anstatt dem "freien" Y Register.
    Die gesammte Routine ist so gedacht, das man sie von Basic aus aufrufen kann. Nach dem Aufruf, wird die Routine abgearbeitet, und am Ende wird das Wort an den Basic Interpreter wieder abgegeben (RTS).
    Wenn das Programm geschlossen wäre, quasie danach nicht an den Basic Interpreter abgibt, hätte man es mit einem Y Register hinbekommen können. Dieser hätte dann aber komplett unbenutzt sein dürfen. Das Problem ist aber, das durch die Rückgabe an den Basic Interpreter, das Y Register verändert wird. Auch der BI ist nur ein Assembler Programm, das mit A,X & Y arbeitet. Daher zählen wir unseren COUNTER in einer festern Speicherzelle hoch, und holen uns diesen TEMPORÄR in ein Register, den wir als Offsetwert zum holen des aktuellen Buchstabens nutzen.


    Zitat


    Ach und was mir noch an dem letzten Listing in Teil 5a aufgefallen ist:
    Gebt doch mal irgendein Zeichen in Zeile 2 Spalte 1 ein und führt das Programm ein paar Mal aus!


    Na Pohli? Ne Idee wie das zustande kommt? Jedenfalls ist die Hardscrollroutine schuld. Ausserdem wird ein wenig zu viel gescrollt. In diesem Beispiel kommt es nicht auf Rechenzeit Optimierung an, aber bei großen Projekten mit ganzen Scrollflächen, muss schon darauf geachtet werden. Ich habe letzten darauf verzichtet das anzusprechen, um nicht noch mehr Verwirrung zu stiften. Wie wäre denn der Hardscroll Optimiert? Wir haben ja keinen 40 Zeichen Bildschirm mehr, sondern nur noch einen 38 Zeichen Bildschirm! Zwei Adressen müssen verändert werden. Na, hat jemand eine Idee?

  • Es gab zwischenzeitlich immer wieder mal nachfragen zu der Formatierten Textdatei des Scrollkurses.


    Leider liegt die Datei nicht mehr auf seinem alten Webspace wie weiter oben gepostet
    unter -> http://home.t-online.de/home/bundyman/ass_kurs.txt


    sondern man findet diesen jetzt
    unter -> http://www.protovision-previews.de/ass_kurs.txt

  • Hi all


    Als neuling möchte ich mich ganz herzlich für dieses Tutorial bedanken, jetzt kann auch ich ein bischen ASM proggen. Auf dem C64 ist das ganze noch überschaubarer als bei den neuen Rechnern heutzutage, durch diesen Thread
    bin ich wieder zu meiner alten Liebe zurückgekommen 8)


    Ich muss sagen das ich eine bessere erklärung noch nie im Netz gefunden habe, ich habe sofort alles kapiert, meine volle bewunderung gilt denen die so ein großes wissen in programmierung haben und dies auch noch in so einer einfachen Sprache vermitteln können......... einfach Spitze !


    Gruß


    Mit der hoffnung auf weitere Tut's :winke:


  • Dir ist klar das du damit gerade eben die Spritevektoren mitgekillt hast? ;)


    Könnt ihr einfach als Routine ans Ende eures Codes kopieren und mit jsr clearscreen aufrufen.


    Hier noch ein schöner um Dateien von Floppy nachzuladen.


    In Dateiname den Namen laden und in Dateinamenlaenge die länge des Namens.
    Aufrufen und schon wirds an die Originaladdresse geladen.
    Originaladdresse holt der Kernel sich nebenbei aus den ersten beiden Bytes einer Datei welche selbstverständlich nicht in den Speicher wandern.
    Wer hat sonst noch so schicke Snippets die einem das Leben erleichtern?


    Ich sollte bei meinen Routinen vorwarnen das ihr irgendwie eure Register in Sicherheit bringen solltet, solltet ihr deren Inhalt noch brauchen nach dem Aufruf.
    Laut "Handbuch" geht das so:


    Mag gescheitere Lösungen geben, aber sie funktioniert.

  • Zitat

    Original von BastetFurry
    Dir ist klar das du damit gerade eben die Spritevektoren mitgekillt hast? ;)


    Ja.
    Bist auch nicht der erste der das merkt... ;) und nebenbei ist das keine Clrscr-Funktion sondern ein kleiner Demonstrationsvesuch Geschwindigkeit BASIC vs. ASM
    Wer noch mehr KERNAL Routinen nutzen will, die gibts alle hier auf meiner Seite...
    (html-Version des entspr. Kapitels aus dem "Programmer's Reference Guide")


    Auch elegant zum Registerretten ist mit Selbstmodifikation:


    z.B.

    Code
    1. sta abck+1
    2. stx xbxk+1
    3. sty ybck+1
    4. ...
    5. abck lda #0
    6. xbck ldx #0
    7. ybck ldy #0


    Es spricht aber sicher nichts gegen den Stack (pha/pla etc.)

  • Also noch ein paar Kleinigkeiten zur Anmerkung.


    Der CLRSCR ist gut zu machen mit


    Das Argument, dass die Sprites nicht gelöscht werden dürfen, dem stimme ich natürlich zu, aber die unütz verbratenen Zyklen müssen nicht sein. Mit 250*4 STA wird der Bildschirm perfekt gelöscht und keine unötigen Zyklen verbraten. Überhaupt ist es eine gute Angewohnheit, in solchen Fällen von oben nach unten zu zählen und auf ein einfaches BEQ zu testen. Das spart den Aufwand, jedesmal ein CPY #Endwert zu benötigen. Sollte man sich unbedingt angewöhnen.


    Zum Softscrolling noch eine Anmerkung. Wer das oben vorgetragene Scrolling laufen läßt, stellt -zumindest auf deinem original C=64- fest, dass es ab und zu ruckelt. Das liegt natürlich daran, dass der Scroller nicht mit dem Bildschirmaufbau synchonisiert wurde.


    Um einen Scroller mit jedem Bildaufbau zu synchronisieren verbiegt man den IRQ-Handler auf seine eigene Routine und füttert sie zusätzlich mit einem Rasterzeileninterrupt. Zu Beginn des Handlers prüft man, ob die IRQ-Quelle auch wirklich die Rasterzeile ist und legt los - oder springt zurück zur alten. Natürlich kann man auch gleich den original Timer-Interrupt löschen und das alles selbst übernehmen.


    Will jemand das vormachen?

  • Zitat

    Original von BastetFurry
    Man kann das letzte STA auch ein paar Bytes früher anfangen lassen... :)


    Schon, klar. Aber dann sind das 256 Durchläufe, 6 mehr als bei dieser Version. Mit jeder Indizierung verbrät man 5 Taktzyklen, vier Befehle davon macht 20 und davon 6 Durchläufe, das sind 120 Zyklen. Absolut zu vermeiden, oder? In 120 Zyklen kann man eine Menge andere Sachen erledigen. Zumindest, wenn man ein Prozessor ist.

  • Apropos Taktzyklen... Warum macht mich niemand aufmerksam darauf, dass ich welche verschenkt habe? ldy #250 reicht, damit läuft die Schleife genau einen weniger. Und spart somit 25 weitere Zyklen oder so.


  • Ach, ich seh grade was du tust :)


    Hier so wie ich das meinte:

  • Ja, da jede Loop, ich habs gerade nochmal nachgemessen, 24 Zyklen kostet sind das bei dir tatsächlich 6 Durchgänge mehr und damit 144 Zyklen, die verloren gehen. Und das ohne Grund, also es spart kein Byte, es macht die Sache nicht eleganter, es gibt einfach keinen Grund einen Speicher mehrfach zu überschreiben. Ist ja ein C=64 und kein Robotron. :D


    Interessante Anmerkung übrigens zu dem Problem. Macht doch mal aus Spaß folgendes, dass ihr den Interrupt mit SEI blockiert und einfach mal den gesamten Speicher des C=64 mit Nullen füllt. Danach geht ihr mit einem CMP nochmal durch den Speicher und zählt mal die Bits, die immer noch auf 1 sind. Das ist lustig.


    Das selbe nochmal mit alles auf $FF und die 0 zählen danach. Ihr werdet feststellen, dass der C=64 von seiner Million Bits, die er speichert (64k * 8 ) beileibe nicht so fehlerlos und unbegrenzt zuverlässig ist, wie die "Elektronengehirne" gern dargestellt werden. Normalerweise sind bei dieser Aktion ein paar Bits FALSCH. In beide Richtungen. An zufälliger Position.


    Aber funktioniert wahrscheinlich nur auf Original 64ern, nicht auf Emulatoren wie dem VICE. Obwohl... Die Fehler der modernen Computer sind wahrscheinlich geringer, aber da die Anzahl der Bits gestiegen ist, wird es am Ende das selbe Problem sein.


    Ich weiss es nicht genau, man kann es ja nur vermuten, aber es könnte entweder am VIC liegen, der ja den Speicherrefresh macht, oder an den Speichern selbst. Raumstrahlung, russische Spionagesatelliten oder andere paranoide Ausreden vom Bastard Operator from Hell. :D

  • Also bei angenommenen mehreren Bitfehlern pro Sekunde würde es mich aber verdammt wundern, wenn sich ein Programm auch nur gescheit laden liesse, mal davon abgesehen, daß es laufen könnte...Bist Du sicher, daß Du da nicht irgendeinen Quatsch mitzählst wie z.B. die Lösch- und Zählroutine selber? Oder irgendwelche nicht-RAM-Bereiche?

  • Definitiv.


    Ich habs erst auch nicht glauben können. Habs bestimmt dreißigmal wiederholt, dann nochmal meine (absolut zuverlässige, weil primitive Zählroutine kontrolliert), danach mit einem Speichermonitor den gesamten Speicher per Hand abgeklappert - ja - es ist wahr.


    Und -nein- es macht dem Rechner überhaupt nichts aus, wenn ab und zu ein Bit umkippt. Die Bedeutung eines einzelnen Bits wird absolut überbewertet. Interessant, aber so ist es. Wider aller Erwartungen. Ich empfehle den Test einmal selbst zu machen. Absolut einfaches, kleines Programm.


    Ich habs dann mitgenommen auf andere Rechner und auch dort - selbes Ergebnis. Fehlerbits. Das ist auch einfach eine Frage der Wahrscheinlichkeit. Die Fehlerwahrscheinlichkeit für ein einzelnes Bit ist astronomisch gering. Aber die Anzahl nötiger (fehlerfreier) Operationen für das Schreiben von einer Million davon ist astronomisch hoch. Die Wahrscheinlichkeit, dass überhaupt kein Fehler passiert ist daher ziemlich nahe bei Null. Stichwort Geburtstagsparadoxon, selbes Prinzip.


    Wie gesagt: ich konnte es auch nicht glauben, daher empfehle ich es unbedingt, es einmal selbst zu versuchen. Auf einem Original-Rechner. Wie gesagt, auf einem Emu hab ichs noch nicht probiert. Seit dieser Erfahrung traue ich den Kisten auch nicht mehr 100%ig. Jedesmal, wenn ich meinen Rechner gebootet habe und er sich tatsächlich bewegt, habe ich das dringende Gefühl, eine Sektflasche entkorken zu müssen zur Feier des Tages.


    Eine wichtige Erfahrung. Wie gesagt: GLAUB mir nicht. Mach es selbst. Bitte. Das was in diesem Falle zählt, ist die eigene Erfahrung und nicht, ob man irgendeiner Kapazität Glauben schenken will. Ich vertraue da zutiefst auf das wissenschaftliche Prinzip.