Hello, Guest the thread was called1.2k times and contains 27 replays

last post from Acorn at the

Smoothe Sprite-Verschiebung

  • Hallo!


    Gleich vorweg: ich bin ein totaler Neuling auf Assembler-Gebiet. Nun wollte ich mal anfangen mit einem einfachen Programm, das einen kleinen Sprite-Ball von links nach rechts (X=255) und wieder zurück usw. springen lässt. Alles mit konstantem Y-Wert. Das habe ich auch geschafft, aber es gibt Probleme. Damit das Ganze mit dem Bildschirm einigermaßen synchronisiert wird, habe ich einen einfachen Raster-IRQ eingesetzt, der bei Bildschirmzeile Null in meine Routine geht. Die Probleme sind:


    1. Der Bildschirm refresht 50 mal pro Sekunde. Wenn ich also einfach $d000 um eins erhöhe, sind das ca. 50 Pixel pro Sekunde und damit zu langsam. Aber einigermaßen smooth. Also bin ich zu einer Addition von #$04 übergegangen. Das ist zwar das Tempo, das ich mir vorgestellt habe, zieht aber Schlieren. Kann man das irgendwie vermeiden?
    2. Wenn der Ball nach links geht, läuft er schneller, obwohl ich genauso #$04 abziehe. Das verstehe ich nicht. Hat jemand eine Erklärung dafür?

    Hier mein gesamter Code:

  • Hallo WebFritzi,


    zu 1: Deine Überlegung ist schon richtig. Wenn man ein Sprite schneller bewegen will,

    muß man die Koordinaten pro Bildaufbau um einen größeren Wert verändern. Ein häufigeres

    Setzen der Position als einmal pro Frame für ein einzelnes Sprite, ist nicht sinnvoll.

    Schlieren sollte es eigentlich nicht geben. Verwendest du einen Emulator?


    zu 2: Zeile 54 vor einer Subtraktion mußt du das Carry-Bit setzen. Da gehört ein Sec hin.


    Noch was: Der Code, der in den Zeilen 68 bis 73 steht, befindet sich auch im ROM. Du kannst

    die Zeilen also weglassen und statt jmp exitirq, jmp $febc schreiben.


    Gruß,


    Neptun

  • Hi Neptun!

    Schlieren sollte es eigentlich nicht geben. Verwendest du einen Emulator?

    Ja, WinVICE 2.2. Ich werde es gleich nochmal auf meinem TheC64 ausprobieren. Aber da das genauso ein Emulator ist, fürchte ich denselben Effekt. Er ist nicht stark. Es sieht halt bissl so aus als ob der Ball einen dünnen Schweif hinter sich herzieht.


    >> zu 2: Zeile 54 vor einer Subtraktion mußt du das Carry-Bit setzen. Da gehört ein Sec hin.

    Autsch! Vielen Dank. Das war es! Das hatte ich nicht auf dem Schirm. Gucke mir gleich nochmal SBC genauer an.



    >> Noch was: Der Code, der in den Zeilen 68 bis 73 steht, befindet sich auch im ROM. Du kannst die Zeilen also weglassen und statt jmp exitirq, jmp $febc schreiben.

    Oh, das ist gut, danke. Hab gerade nochmal in der Beschreibung des Kernels nachgeschaut. Es stimmt: bei Adresse $FEBC steht "Interrupt-Ende (holt Register Y, X, A vom Stack und RTI)".


    BTW: Wie kann ich hier mehrfach schön zitieren anstatt den ">>"?

  • Die Schlieren kommen von Deinem Monitor, da kann weder Vice noch Dein Programm was für. Ein schneller Gamingmonitor hat die nicht und ein Röhrenmonitor auch nicht.


    Du behandelst den CIA-IRQ noch extra, ich würde ihn in Deinem Fall eher ausschalten (sonst könnte es auch zu vereizelten Rucklern kommen, wenn Du zur gefragten Rasterzeilte gerade in der ROM-Routine bist). Das erreichst Du in Deiner set_irq Routine mit


    Code
    1. lda #%01111111 ; disable CIA irq: Bit 7 sets the value, Bit 0...4 selects the bits to be set
    2. sta $dc0d
    3. lda $dc0d ; acknowledge any pending CIA irq
  • Eventuell befindet sich im Konfigurationsmenü des Monitors die Möglichkeit, eine Overdrive-Funktion einzuschalten, wodurch die Schlieren reduziert werden.

    Dass das nichts mit VICE zu tun hat, kann man auch hier unter "Reaktionszeit" nachvollziehen: https://www.eizo.de/monitortest/


    Aber einigermaßen smooth.

    1px Vorschub pro Frame ist das smootheste, watte haben kannst. Ruckeln kann auch dadurch entstehen, dass der Monitor mit einer anderen Bildfrequenz läuft, z.B. 60Hz. Ich habe mir dafür einfach zwei Monitor-Einstellungsprofile erstellt, zwischen denen ich übers Desktop-Kontextmenü schnell wechseln kann.

  • Herzlichen Dank für eure Beiträge, Leute!

    Du behandelst den CIA-IRQ noch extra, ich würde ihn in Deinem Fall eher ausschalten (sonst könnte es auch zu vereizelten Rucklern kommen, wenn Du zur gefragten Rasterzeilte gerade in der ROM-Routine bist). Das erreichst Du in Deiner set_irq Routine mit


    Code
    1. lda #%01111111 ; disable CIA irq: Bit 7 sets the value, Bit 0...4 selects the bits to be set
    2. sta $dc0d
    3. lda $dc0d ; acknowledge any pending CIA irq

    Ok, verstehe. Danke dir. Aber dann habe ich z.B. auch kein Cursor-Blinken mehr, nicht wahr? Vielleicht könnte ich auch am Ende meines IRQ-Handlers zur Standard-CIA-Prozedur jumpen? Das wären dann zwar 50 statt 60 Hz, aber was soll's...


    EDIT: Habe ich jetzt gemacht, und es läuft. Allerdings nicht wirklich besser. Ist auch irgendwie klar, weil sich mein Ball in der vertikalen Mitte des Bildschirms befindet und der IRQ in Rasterzeile 0 ausgelöst wird. Die paar Ticks, die für die Aktualisierung der x-Koordinate nötig sind, sollten spätestens in Zeile 2 durchgeführt sein. Oder habe ich etwas übersehen?

  • Eventuell befindet sich im Konfigurationsmenü des Monitors die Möglichkeit, eine Overdrive-Funktion einzuschalten, wodurch die Schlieren reduziert werden.

    Dass das nichts mit VICE zu tun hat, kann man auch hier unter "Reaktionszeit" nachvollziehen: https://www.eizo.de/monitortest/

    Aha, interessant. Sogar bei minimaler Geschwindigkeit zeigen sich im Test Schlieren "hinter" dem weißen Rechteck. Mein Laptop-Monitor läuft mit 60 Hz. Würde sich das mit den Schlieren ändern, wenn ich ihn auf 50 Hz drosseln könnte?

    Leider weiß ich nicht, was Overdrive ist und kann es im Konfigurationsmenü der Grafikkarte auch nicht finden.

  • Ja, Cursorblinken, Timerupdate, Tastaturpuffer etc. funktionieren dann nicht mehr. Aber momentan hängst Du ja im Hauptprogramm in einer Loop, daher bin ich davon ausgegangen, dass Du das alles auch nicht brauchst. Die meisten Spiele schalten das alles aus. Aber wenn Du die Spritebewegung im Hintergrund z.B. einer Basicanwendung laufen lassen willst, solltest Du den ROM-IRQ weiter laufen lassen.


    Das Problem das ich sehe ist eher, dass Du zwei IRQ-Trigger laufen lässt, einer vom VIC mit 50Hz und einer vom CIA mit 60Hz. Die können sich hie und da in die Quere kommen, wenn sie annähernd gleichzeitig getriggert werden. Besser wäre es dann, wenn Du stattdessen am Ende Deiner Spriteroutine in die ROM-Routine springst. Dann läuft Dein ROM-IRQ etwas zu langsam (und die Uhr wird nachgehen), aber Du hast keine 2 asynchrone IRQs laufen.

  • Ach so, Laptop-Bildschirm .. da wird es dann vermutlich solche Einstellungen nicht geben. Overdrive beschleunigt die Farbumschaltungen des Panels; die Grafikkarte hat darauf wiederum keinen Einfluss. Findet man aber eher bei Stand-alone-Monitoren im OSD-Setup. Ich benutze auch ein Laptop, hab aber einen 27-Zoller am HDMI-Ausgang.


    Die Schlieren werden bei 50Hz nicht weniger, weil sich hinter dem Objekt durch eine andere Bildfrequenz auch nichts ändert. Die Schlieren erscheinen ja dadurch, dass die Kristalle kurz zuvor durch das passierende Objekt umgeschaltet wurden und nach dem Objekt nicht so schnell wieder zurückschalten können, um sofort wieder die richtige Hintergrundfarbe zu erreichen. Sehr wahrscheinlich muss man sich an diesem Laptop damit abfinden - oder eben, wie ich, einen externen Monitor dranhängen, bei dem das nicht mehr so ist, oder nicht mehr so doll.


    Auf 50Hz umzustellen lohnt sich nur, um das Ruckeln zu reduzieren, weil der C64, ob echt oder Emu, ja auch mit 50Hz arbeitet (unseren PAL-Modus vorausgesetzt).


    Man muss aber bedenken, dass diese Tests und dieses Sprite eher als synthetische Tests zu betrachten sind. Ob das einen letztendlich wirklich nervt, würde ich eher an echten Spielen festmachen, wo mehr los ist und die psychische Komponente nicht zu unterschätzen ist, dass man sich nämlich meistens auf ganz andere Dinge konzentriert als auf irgendwelche Schlieren.


    Mein Monitor ist auch kein Gamer-Monitor, hat zwar tolle Farben, tollen Kontrast und einen guten Betrachtungswinkel, aber zieht ebenfalls Schlieren. Ganz ab und zu daddel ich auch mal, aber gestört hat mich das noch nie.

  • Das Problem das ich sehe ist eher, dass Du zwei IRQ-Trigger laufen lässt, einer vom VIC mit 50Hz und einer vom CIA mit 60Hz. Die können sich hie und da in die Quere kommen, wenn sie annähernd gleichzeitig getriggert werden.

    OK, also mal allgemein gefragt: wie ist das eigentlich mit den IRQs? Sagen wir, ich schalte den CIA-IRQ nicht ab (habe ich jetzt allerdings) und sagen wir weiterhin, dass meine Routine gerade läuft, wenn der CIA-IRQ getriggert wird. Was hat dann Priorität - mein IRQ-Handler oder der CIA-IRQ? Springt dann der Prozessor kurzzeitig aus meiner Routine raus, um nach dem Abarbeiten des CIA-IRQs wieder neu einzuspringen? Oder wartet er auf mein RTI?

  • OK, also mal allgemein gefragt: wie ist das eigentlich mit den IRQs? Sagen wir, ich schalte den CIA-IRQ nicht ab (habe ich jetzt allerdings) und sagen wir weiterhin, dass meine Routine gerade läuft, wenn der CIA-IRQ getriggert wird. Was hat dann Priorität - mein IRQ-Handler oder der CIA-IRQ? Springt dann der Prozessor kurzzeitig aus meiner Routine raus, um nach dem Abarbeiten des CIA-IRQs wieder neu einzuspringen? Oder wartet er auf mein RTI?

    Kommt drauf an, wie Du es programmierst...


    Rein von der Technik betrachtet:

    - Der Prozessor hat ein I-Flag und eine IRQ (=InterruptReQuest)-Leitung.

    - Ist das I-Flag gelöscht und die IRQ-Leitung gezogen, dann läuft der Interrupt los (jetzt mal nicht auf den Taktzyklus festgenagelt).

    - Dabei setzt der Prozessor das I-Flag und springt über $fffe. Weil das I-Flag gesetzt ist kann die IRQ-Routine in Ruhe laufen, obwohl draußen immer noch an der Leitung gezogen wird.

    - Ansonsten ist eine Interrupt-Routine ganz normaler Code.

    - Am Ende der Interrupt-Routine wird das I-Flag wieder gelöscht.


    - Draußen hängen an der IRQ-Leitung VIC, CIA1, aber auch REU und sicher weitere externe Erweiterungen.

    - Die ziehen die IRQ-Leitung, bis man ihren Interrupt bestätigt (lesen von $dc0d, setzen des passenden Bits in $d019.


    Deine ursprüngliche IRQ-Routine sieht da schon sehr richtig aus:

    - Quelle des IRQ bestimmen

    - IRQ bestätigen

    - Falls Quelle CIA war, ein CLI

    Damit kann dann ein frisch kommender IRQ die gerade laufende Interrupt-Routine unterbrechen, Du hast so dem VIC höhere Prio als der CIA eingeräumt.


    Das Problem, das Claus meint:

    Da lief schon einiges an Code im ROM und in der Routine ab, bis mal das CLI kam! Grob geschätzt gehen locker 50-60 Takte drauf, und wenn währenddessen der VIC-IRQ kam, dann wird der erst entsprechend spät verarbeitet.


    Deine Frage war: Was, wenn ein CIA-IRQ während der Interrupt-Routine passiert?
    Du hast da das CLI, also unterbricht die CIA die gerade laufende Interrupt-Routine und startet eine neue.

    Dürfte in der Regel in einer Endlosschleife enden, würde ich vermeiden.

  • EDIT: zu spät.

    Was hat dann Priorität - mein IRQ-Handler oder der CIA-IRQ? Springt dann der Prozessor kurzzeitig aus meiner Routine raus, um nach dem Abarbeiten des CIA-IRQs wieder neu einzuspringen? Oder wartet er auf mein RTI?

    Das hängt vom Zustand des entsprechenden Statusflags ab:

    Nach SEI ist der Interrupt gesperrt, nach CLI ist der Interrupt freigegeben.

    Ist der Interrupt freigegeben und liegt tatsächlich an, so macht die CPU drei Dinge:

    1. Rücksprungadresse und Statusregister werden auf den Stack geschrieben.

    2. Das Sperrflag wird gesetzt.

    3. Der Handler wird angesprungen.

    Beim Rücksprung durch RTI wird neben der Rücksprungadresse auch wieder das Statusregister vom Stack geholt und somit das Sperrflag wieder gelöscht.


    Wenn Du also in Deiner Handlerfunktion weitere (geschachtelte) Interrupts zulassen willst, musst Du nur CLI ausführen lassen und schon geht das. Allerdings muss man vorher die eigene Interruptquelle matürlich quittiert haben, sonst hängt die CPU in einer Endlosschleife.


    EDIT: Das alles gilt nur für den "normalen" Interrupt, aber natürlich nicht für NMI und Reset.

  • Alle guten dinge sind drei, deshalb noch meine Erklärung dazu.


    Wenn dein Raster-IRQ läuft muss der CIA-IRQ warten, aber umgekehrt verhält sich das ganze so, angenommen der Raster-IRQ kam nachdem LDA $D019. Da BMI nicht verzweigt, wird LDA $DC0D aus geführt. Aber mit CLI wird der IRQ wieder freigegeben, dadurch wird der Raster-IRQ ausgeführt. Das passiert aber erst nach JMP $EA31, das ganze dauert damit max. 11 Takte bis der neue IRQ anläuft.

    Nachdem Raster-IRQ springt die CPU zurück nach $EA31 und führt dort JSR $FFEA aus.


    Übrigens LDA $DC0D ist unnötig, damit wäre es nur noch 7 Takte. Aber selbst ohne CLI ist die Verzögerung viel zu kurz um zu stören.

  • angenommen der Raster-IRQ kam nachdem LDA $D019...das ganze dauert damit max. 11 Takte bis der neue IRQ anläuft.

    Ist was dran, ich hatte ein lda $dc0d: bmi CIAIRQ vor dem inneren Auge, weil ich die Quelle immer sorum geprüft hab.

    Da hab ich meine 60 Takte Verzögerung oben falsch berechnet :thumbup:


    Jetzt sehe ich aber was anderes:

    - Kommt ein IRQ, dann dauert es diese ~50 Takte, bis wirklich die eigene Routine losläuft.

    - Es ist egal, ob der IRQ durch CIA oder VIC kommt.

    - Diese 50 Takte bis zur Reaktion wären der Normalfall für Raster-Interrupte.

    - Es könnte aber auch ein CIA-IRQ kommen, dann der VIC-IRQ in der Zeit bis zum lda $d019. Dann würde die eigene Routine unerwartet zu früh anlaufen!


    Ich glaube, praktisch ist mir das nie aufgefallen, weil ich sowieso immer auf die nächste Zeile gewartet hab.

    Übrigens LDA $DC0D ist unnötig, damit wäre es nur noch 7 Takte. Aber selbst ohne CLI ist die Verzögerung viel zu kurz um zu stören.

    Finde ich nicht unnötig. Vorher war keines, und am Ende der ROM-IRQ-Routine hilft es auch nicht mehr.

  • Nochmal vielen Dank für eure Antworten, Leute! Toll, dass es noch deutschsprachige C64 ASM-Profis gibt. Könnte gut sein, dass ich euch demnächst mal wieder belämmere. Ich habe einige Erfahrung mit C/C++ und tue mich doch ein wenig schwer mit Assembler. Wenn ich eine neue Routine schreibe, habe ich eigentlich immer mondestens einen Fehler drin. Den entdecke ich durch ausprobieren, wodurch der C64 des Öfteren auch mal abstürzt. Kein Problem mit C64-Studio + VICE, aber früher muss das ein Pain in the Ass gewesen sein. Hut ab vor den Leuten, die in den 80ern ASM gelernt haben und dann so tolle (funktionierende!) Spiele geschrieben haben.


    Was mir vorher gar nicht klar war: wenn ich den CIA-IRQ nicht ausschalte, läuft mein Ball mit doppelter Beschleunigung. Ich dachte vorher, der CIA-IRQ läuft unabhängig von meinem und löst nur die Standard-Prozedur in $ea31 aus. Aber der geht offenbar auch in die Prozedur, auf die in $314/$315 verwiesen wird -- also meinen Handler. Deshalb wird der Ball viel häufiger bewegt und ist somit schneller. Aufgrund der unterschiedlichen Trigger-Zeiten (1/60 vs 1/50 sec) müsste der Ball eigentlich auch unterschiedliche Geschwindigkeiten haben. Ich werde mal drauf achten.

  • Nochmal vielen Dank für eure Antworten, Leute! Toll, dass es noch deutschsprachige C64 ASM-Profis gibt. Könnte gut sein, dass ich euch demnächst mal wieder belämmere. Ich habe einige Erfahrung mit C/C++ und tue mich doch ein wenig schwer mit Assembler. Wenn ich eine neue Routine schreibe, habe ich eigentlich immer mondestens einen Fehler drin. Den entdecke ich durch ausprobieren, wodurch der C64 des Öfteren auch mal abstürzt. Kein Problem mit C64-Studio + VICE, aber früher muss das ein Pain in the Ass gewesen sein. Hut ab vor den Leuten, die in den 80ern ASM gelernt haben und dann so tolle (funktionierende!) Spiele geschrieben haben.

    Habe auch erst sehr spät mit 6502 Assembler angefangen.


    Siehe hier passt sogar zum Thema: Sprite von A nach B bewegen

    Was mir vorher gar nicht klar war: wenn ich den CIA-IRQ nicht ausschalte, läuft mein Ball mit doppelter Beschleunigung. Ich dachte vorher, der CIA-IRQ läuft unabhängig von meinem und löst nur die Standard-Prozedur in $ea31 aus. Aber der geht offenbar auch in die Prozedur, auf die in $314/$315 verwiesen wird -- also meinen Handler. Deshalb wird der Ball viel häufiger bewegt und ist somit schneller. Aufgrund der unterschiedlichen Trigger-Zeiten (1/60 vs 1/50 sec) müsste der Ball eigentlich auch unterschiedliche Geschwindigkeiten haben. Ich werde mal drauf achten.

    Das dürfte eigentlich nicht der Fall sein, da ja nur in die Routine verzweigt wird, wenn ein Raster-IRQ anliegt. Wenn aber im Hauptprogramm der BCD-Mode benutzt, dann versagen beide IRQ's da das BCD-Flag nicht löscht wird. Die Auswirkung sind aber deutlich sichtbar. Es liegt ehr an dein PC, der nur 60Hz ausgeben kann, würde zum testen Vice auf NTSC stellen (VIC-II -> VIC-Modell).

  • Das dürfte eigentlich nicht der Fall sein, da ja nur in die Routine verzweigt wird, wenn ein Raster-IRQ anliegt.

    Hmm, auf https://www.c64-wiki.de/wiki/IRQ#ISR_des_KERNAL steht aber, dass der normale IRQ den Code ausführt, auf den $314/$315 verzweigt. Das ist normalerweise $ea31. Nun steht da aber die Adresse meines Handlers drin. Also müsste doch der CIA-IRQ auch dahin springen, oder nicht?


    Da ist bei mir noch etwas merkwürdig. Wenn ich einen Zähler mitlaufen lasse, der bei jedem Raster-IRQ-Aufruf um eins erhöht wird und der Handler erst ausgeführt wird, wenn der Zähler bei 10 ist, dann müsste mein Ball sich ja jede 1/6 Sekunde bewegen. Tut er aber nicht. Es ist ungefähr jede 1/4 Sekunde... (Ich habe den CIA-IRQ ausgeschaltet.)