Hello, Guest the thread was called326 times and contains 14 replays

last post from Hoogo at the

Assemblerprogramm spielt die Musik aus dem Takt

  • Hallo!


    Ich habe mich in den letzten paar Tagen viel mit C64-Assembler auseinandergesetzt, da ich SID-Musik einfach klasse finde und mal gucken wollte, wie schwer es eigentlich ist, einen nicht-blockierenden Player zu schreiben. Damit hatte ich schon auf dem ESP32 Erfolg. Aber der 64'er ist nochmal was ganz anderes. Ich habe mich mit dem IRQ beschäftigt und es geschafft, diesen IRQ anzuzapfen und "Alle meine Entchen" mit einer Frequenztabelle zu spielen. Man könnte auch einfach nur die tiefsten 12 Töne speichern und dann ROL nutzen, aber das wollte ich nicht direkt am Anfang ausprobieren. "Alle meine Entchen" spielte zwar, aber es spielte aus dem Takt. Nicht extrem schlimm, aber mich nervt sowas ganz besonders, darum versuchte ich, da was gegen zu tun. Ich habe mir diesen Rasterzeilen-IRQ angeguckt und dafür ein Beispielprogramm, welches den Hintergrund und Rahmen zur Deutschlandflagge (welche übrigens nur ganz schlecht mit der hellblauen Schrift harmoniert) macht. Ich dachte mir, dass ein IRQ, der so exakt ist, dass der Hintergrund fast garnicht flackert, unmöglich aus dem Takt sein kann, darum habe ich das Abspielprogramm mit dem Flaggenprogramm verschmolzen und zusätzlich noch diesen 60 Mal pro Sekunde gehenden IRQ abgestellt - ich hänge eh BASIC mit jmp * auf - und es spielte immernoch aus dem Takt. Ich habe auch die Rasterzeile in die Mitte gepackt, vollkommen ohne Effekt. Langsam habe ich keine Ahnung mehr, was ich tun könnte, daher frage ich euch: Wisst ihr, wie man dem C64 ein bisschen mehr Taktgefühl verpassen kann? Hier ist mein Programm:

    Da das Programm aus mehreren Programmen zusammengestückelt ist, machen die Kommentare größtenteils keinen Sinn.


    Gruß

    NoobTracker

  • BUFFERCNT scheint irgendwie das Gatebit zu steuern?

    TIMELEFT steuert die Notenlänge bzw. die Zeit zwischen 2 Aktionen?

    MUSICPOS ist ein Zeiger in die Musikdaten, die sich aus... was eigentlich zusammensetzt? Wird jedenfalls irgendwie "kreativ" hochgezählt.


    So ganz durchblick ich den Code nicht...

    Beschreib mal den Aufbau der Musikdaten und dann, was eigentlich der Plan ist.

  • Die Musik besteht aus Noten / Tonhöhen und Längen. Die Längen bestehen aus 50stel-Sekunden, da PAL mit 50Hz läuft und der IRQ davon abhängig ist. MUSICPOS ist die Position in den Musikdaten. Sie wird nach jeder Note um 2 erhöht, da die Töne ja aus zwei Bytes bestehen. Ich könnte zwar immer nur um 1 erhöhen und dann die Bits verschieben, aber der Filterbefehl $E<X> ist nur ein einziges Byte groß. Alle berechneten Werte sind zwischengespeichert, da ich mal versucht habe, immer die Noten einen IRQ, bevor sie angezeigt werden, zu berechnen. CNT stehe für Control, da ist ja das Hüllkurven-Bit mit dabei. DIe Töne werden übrigens über die lange Word-Tabelle umgewandelt, ich habe zwar in C++ es geschafft, nur die tiefsten 12 Töne zu speichern und dann mit Bitverschiebung die höheren Oktaven zu errechnen, aber ich wollte es, wo es ging, simpel halten und mich hinterher damit auseinandersetzen. Beantwortet das deine Fragen?

  • Bei CNT lese ich immer Count :) CTRL finde ich besser.


    Also besteht eine Note aus Filter ($E9), Tonhöhe (NC4 als Byte) und der Dauer (12)?

    !byte $E9,NC4,12


    Ein bisschen mehr verstehe ich von dem Code, aber grad den ersten Teil zwischen readnote und jmp readnote verstehe ich nicht richtig.

    Mach es Dir doch als Anfänger an der Stelle (technisch) einfach: Statt einer Tabelle "notes" aus 3-Byte-Werten kannst Du auch 3 Tabellen für Filter(?), Tonhöhe, Länge machen.

    "inc MUSICPOS, jmp readnote" sieht mir nämlich sehr verdächtig aus, und mit 3 Tabellen wird es einfacher, die richtige Stelle für das Hochzählen des Pointers zu finden.


    Willst Du noch was mehr mit dem Gatebit machen?

    Um einen kompletten Ton "ordentlich" zu spielen startet man mit gesetztem Gatebit, lässt es für den Lautstärkeverlauf eine Weile gesetzt, und dann löscht man es irgendwann, damit der Ton ausklingen kann.

    Schon das Basic-Handbuch macht das falsch, indem es $00 nach d404 schreibt und somit den Ton abwürgt.

    Im Moment machst Du es auch so, und Haltezeit der Note und die Taktzeit zur nächsten Note sind gleich.

  • Das mit den verschidenen Tabellen geht nicht, da der Filterbefehl $E<Filterwert> so dazwischengequetscht wird. Man kann den Filter setzen und dann 5 Noten spielen, wenn man will. Das spart Speicher.


    Nein, ich würge den Ton nicht ab. Ich schalte erst alles aus, damit ich anschließend den eigentlichen Wert mit Gate reinschreiben kann.


    Und ja, wo du's sagst, CNT ist echt unglücklich gewählt. Selbst CTR ist besser.

  • Also ich hab am Schreibtischtest keine offensichtlichen Programmfehler gefunden.

    Hier und da kann man was hübscher machen, aber nix dramatisches.


    Hab dann den Assembler angeschmissen und per Farbänderungen das Ändern des Gatebit geprüft, das passt so.

    Ich hab aber auch kein besonders musikalisches Ohr, ich GLAUBE, ich höre das Stolpern auch...


    Probier mal am Anfang andere Werte für ADSR:
    lda #$55

    sta $D405

    lda #$55

    sta $D406


    Ist das Stolpern dann weg?

    Eventuell ist das ein SID-Bug.

    Nein, ich würge den Ton nicht ab. Ich schalte erst alles aus, damit ich anschließend den eigentlichen Wert mit Gate reinschreiben kann.

    lda BufferCTRL

    beq nocnt

    ldx #$00

    stx $D404

    sta $D404


    ^^ Das ist Abwürgen in reinster Form :)
    Bei langen Noten wird die Hüllkurve AD durchlaufen und auf dem Level von S stehen bleiben.
    Wenn Du jetzt $40 nach $d404 schreiben würdest, dann würde R anfangen, und der Ton dürfte ausklingen.

    Das können schon ein paar Frames sein, die ein Ton ausklingt.

  • Es wird aber momentan nichts gesetzt, wenn eine Pause eintritt - da passiert einfach nichts, das wird ignoriert.

    Hm, vielleicht sprechen wir dann ein bisschen aneinander vorbei...

    Ich finde, da sollte unbedingt ein bisschen Code mit rein, der eine "Pause" ermöglicht.

    Also Wellenform auf $40 (also die eingestellte nur ohne Gatebit), und dann wählbar ein paar Frames Pause machen, damit der Ton ausklingen kann.

    Das mit dem SID-Bug muss ich mal ausprobieren. Ich muss also im Grunde die Hüllkurve auf $00, $00 stellen und das Gate-Bit ausstellen, dann zwei Frames warten und anschließend den eigentlichen Ton spielen?

    Irgendwie so hab ich den "Hard Restart" auch verstanden.

    Oder halt die Bedingungen vermeiden, unter denen der Fehler auftreten kann.
    Ich hab da nicht genau drüber nachgedacht, was da eigentlich beschrieben ist.

    Mir scheint es sicher, wenn man für ADR den gleichen Wert benutzt, darum oben die $55 nach d405/d406.

    Probier mal, ob Du damit noch ein Stolpern hörst.

  • Tja, irgendwie geht's jetzt: Ich habe nochmal ein älteres Programm genommen und modifiziert (ab Zeile 198), damit die Hüllkurve schon zwei Frames vor dem Ende der Note abgebrochen wird. So sieht's aus:

  • Was genau sind denn die Bedingungen für einen Bug? Das habe ich nicht ganz verstanden.

    Irgendwas mit einem internen Counter, der bis "Gleich Null" runterzählt und es daher nicht unbedingt mag, wenn man die abzuziehende Zahl unterwegs verändert. Und solche Änderungen passieren wohl, wenn man das Gatebit setzt/löscht oder ADR neu beschreibt. Ist mir jetzt zu anstrengend, über die Details nachzudenken :)


    Da war aber mal ein Thread in der CSDB, in der der Bug für verschiedene Werte auf den Taktzyklus genau erforscht wurde. Mag 5 Jahre her sein.