Sid -> C64

Es gibt 35 Antworten in diesem Thema, welches 9.903 mal aufgerufen wurde. Der letzte Beitrag (9. Juni 2011 um 13:08) ist von BlackJack.

  • Vernunftmensch: --start-addr hat nicht bei jedem Zielsystem eine Wirkung. Bei der normalen c64-Konfiguration des Linkers zum Beispiel nicht, da wird immer $07ff (also $0801 wenn man die Ladeadresse berücksichtigt) verwendet.

    Du musst Dir eine eigene Linker-Konfiguration schreiben, bei der der Bereich frei bleibt. Als Ausgangspunkt kannst Du die voreingestellte Konfiguration nehmen und entsprechend verändern. Neue Speicherbereiche definieren die $1000—???? aussparen und die Segmente die vorher im einzigen RAM-Speicherbereich gelandet sind, auf die neuen Bereiche aufteilen. Must halt sehen wieviel man noch vor $1000 unterbringen kann neben der Basic-Startzeile.

  • Meine C-Datei:


    Meine Linker-Konfiguration:

    StartUp im LOWRAM: Warum schreibt der Computer nicht einmal mein printf ?

  • Vernunftmensch: Der Code landet nicht an den Stellen wo er hingehört. Grundsätzlich ist das Assemblieren und die Adressberechnung unabhängig davon wo der Code am Ende im Speicher landet. So wie es jetzt dort steht werden die Segmente STARTUP, LOWCODE, und INIT in die Ausgabedatei geschrieben und danach völlig nahtlos die Segmente CODE, RODATA, und DATA. Dazwischen hättest Du ja aber sicher gerne einen Haufen Füllbytes, damit CODE direkt nach dem laden der Datei auch wirklich an der Stelle im Speicher liegt, für die es kompiliert wurde.

    Dazu müsstest Du in MEMORY {} noch den Bereich für die Musik definieren und bei LOWCODE und dem Bereich für die Musik fill = yes angeben.

  • Das Hauptprogramm:

    Die Linker.cfg:

    Immerhin startet der C64 das Hauptprogramm, welches folgendes macht, besser nicht macht. :sad:

    1. Mozart.dat einbeziehen mit Erfolgsmeldung (Vielleicht Programmfehler?)
    2. Abstürzen mit leerem READY.-Bildschirm bei lda #0 und jsr $1000, obwohl Sidplay das als Initialisierung angibt
    3. Zum eigentlichen Abspielen kommt er nicht.

    Woran liegts diesmal?

  • Vernunftmensch: Was enthält denn die mozart.dat genau? Ist da wirklich nur die Abspielroutine und die Musik in der Datei? Ohne die für C64-Dateien üblichen zwei Bytes welche die Startadresse angeben? read() liest die Daten so wie sie in der Datei stehen 1:1 in den Speicher.

    Falls die Daten die Startadresse am Anfang enthalten, solltest Du vielleicht auch überlegen cbm.h zu importieren und dann cbm_load() zu verwenden. Plattformunabhängig ist das Programm ja sowieso nicht mehr so richtig wenn man SID-Songs abspielt. :smile:

    Wobei die Grösse der Musik in der Linker-Konfiguration ja ziemlich präzise angegeben ist. Willst Du in dem Programm denn wirklich die Möglichkeit haben verschiedene Songs während der Laufzeit zu laden? Falls nicht, könnte man den Song doch fest in das Programm einbinden.

  • Von "C" hab ich keine Ahnung, aber mit ASM würde ich das so zum Beispiel so machen (play/init $1000/$1003)

  • @C64-CAMPER: Bei der Lösung des Problems hilft das nicht direkt weiter, denn da klappt das Grundsätzlich in C so wie Vernunftmensch das geschrieben hat — vorausgesetzt die Musikroutine ist an der richtigen Stelle im Speicher. Das Timing ist bei Dir natürlich schöner, weil es an den Bildaufbau gekoppelt ist, statt eine Zählschleife zu verwenden. Das lässt sich in C aber auch einfach ausdrücken:


    Das funktioniert bei mir mit einer Datei mit einer entsprechenden Musik ohne Startadresse in der Datei.

  • mit Startadresse noch drinnen:

    Klappt.

    Meine nächste Fragen:
    A Was gibt es beim C64 für Interrupts und wie kann man die ändern?
    B Welche Format haben die SID-Dateien?
    C Gibt es schon irgendwo eine Routine, mit der man immer ab $1000 schreiben kann, aber die Routine die JSR JMP BNE BEQ und soweiter alle anpaßt?

  • Vernunftmensch: Muss es denn tatsächlich eine eigene Ladefunktion sein? Wenn Du da grundsätzlich Dateien im ”Commodore-Format”, also mit Startadresse am Anfang der Daten laden möchtest, ist `cbm_load()` einfacher zu benutzen. Hätte den zusätzlichen Vorteil, dass bei Systemen mit einer Cartridge wie Action Replay, Final Cartridge und ähnliche auch der Schnellader zum Laden verwendet wird. Die meisten dieser Erweiterungen gehen nämlich nur über den LOAD-Vektor und beschleunigen keine Lesezugriffe bei Dateien die mit OPEN geöffnet wurden.

    A) Es gibt die CIA-Timer und den Rasterzeileninterrupt, die für das Abspielen von Musik benutzt werden können. Die Unterbrechungsbehandlung musst Du dann in Assembler schreiben — dafür ist C nicht unbedingt geeignet.

    B) Bitte melde dich an, um diesen Link zu sehen.

    C) So etwas haben einige Maschinensprachemonitore ansatzweise eingebaut, aber es ist nicht allgemein zuverlässig möglich. Du müsstest dafür programmatisch Code und Daten sicher unterscheiden können und auch Adressen in Adresstabellen und sich selbst modifizierendem Code identifizieren können. Die Branch-Befehle sind von ”Natur” aus schon frei verschiebbar, weil das Sprungziel dort relativ gespeichert wird.

  • Ohne Interrupt völlig in c mit Schnelllader:


    Mit Interrupt völlig in c mit Schnelllader:

    Das spielt schon manche richtigen SID-Files ab:

    A@BlackJack:
    (rekursive Lader, die über Eingabeadresse Code und Data trennen und danach rel. verschieben, gibt es, dauern aber bei einem C64 unpraktisch zu lange. besser: zwei programme, bei denen der Code entweder am Anfang oder am Ende ist und ein kleines Programm, das aussucht, welches gebraucht wird.)

    B Beim C64er kann man doch in Basic komplett in ein neues Programm rüberspringen. Wie sieht das gleiche in C aus?

    C Sind im SID-Header wichtige Informationen, die das Abspielen betreffen?

  • @odg: Gibt es etwas was den Compiler daran hindern würde so etwas wie ``VIC.irr = VIC.irr`` zu einem reinen Lesezugriff zu optimieren? Für andere Compiler hätte ich jetzt gesagt, dass `VIC.irr` dafür als ``volatile`` deklariert sein müsste, aber cc65 ignoriert dieses Schlüsselwort ja komplett.

    Vernunftmensch: Du hast die Unterbrechungsbehandlung offensichtlich ohne sie zu verstehen übernommen. Du brauchst ja gar keine zwei verschiedenen Rasterzeilen für die Unterbrechung und das mit dem `zaehler` ist ein hässlicher Hack, weil die Unterbrechungsbehandlung für die Timer-Unterbrechung und zweimal pro Frame für durch eine Rasterzeile ausgeführt wird. Du rufst die Abspielroutine an der falschen Stelle auf.

    Und das mit `MEM` könnte gefährlich werden.

    A) Ich bin mir ziemlich sicher Du verwendest das Wort ”rekursiv” falsch. Und solche Lader gibt es sehr wahrscheinlich nicht. Wie willst Du zuverlässig Code und Daten trennen? Insbesondere wenn der Code sich selbst modifiziert und dadurch im Grunde beides ist. Und in den Daten, die sowohl als Operanden von Befehlen, als auch ”frei” in den reinen Datenbereichen vorkommen können, müsstest Du dann die Adressen identifizieren können, die angepasst werden müssen. Wobei die auch erst durch Berechnungen mit Werten entstehen könnten.

    Wenn Du einen allgemeinen SID-Player wie Bitte melde dich an, um diesen Link zu sehen. schreiben möchtest, und dafür C einsetzen willst, würde ich eher das Hauptprogramm in C schreiben und dann einen Lader+Player in Assembler für mehrere Positionen im RAM oder besser noch frei in 256-Byte-Schritten verschiebbar, der am Ende das Hauptprogramm wieder nachläd falls das von den Musikdaten oder beim Abspielen überschrieben worden sein sollte. Es gibt SIDs die wirklich viel Speicher benötigen, weil sie zum Beispiel Sampledaten enthalten oder die Initialisierungsroutine die Playerroutine und die Daten erst einmal quer über den ganzen Speicher verteilt. So ein Song muss nicht in dem Bereich bleiben, in den er geladen wurde!

    B) Man muss den Ladecode ausserhalb des Bereichs ablegen der durch das neue Programm belegt wird [1]_. Und dann kann man einfach laden und den geladenen Code mit den entsprechenden Aufrufen starten. Also entweder eine Assemblersequenz, die ein RUN ”nachbaut” oder direkt an die Startadresse springen, falls das nachgeladene Programm kein BASIC-Programm ist und die Startadresse bekannt ist.

    Für diesen Fall jetzt wahrscheinlich nicht so wichtig, aber man kann mit cc65 auch ”overlays” schreiben, in dem man in der Linker-Konfiguration mehrere Speicherbereiche mit den selben Adressen definiert und diese in verschiedene Dateien schreiben lässt und mit ``Bitte melde dich an, um diesen Link zu sehen.``-Anweisungen dann Funktionen und Daten gezielt in für diese Bereiche definierte Segmente platziert. Auf diese Weise kann man nachladbare Teilprogramme realisieren.

    .. [1] Das ist nicht ganz korrekt: Man könnte auch die Startadresse - 1 des nachzuladenen Maschinespracheprogramms auf dem (Hardware)Stack ablegen wenn die eigentliche Laderoutine tatsächlich der allerletzte Aufruf aus dem Code ist, der durch das Laden überschrieben wird.

    C) Ja natürlich. Beispielsweise die Lade-, Initialisierungs-, und Abspieladressen und die Quelle des Timings (Rasterzeile, CIA-Timer, Init macht was eigenes).

  • Zitat

    Vernunftmensch: Du hast die Unterbrechungsbehandlung offensichtlich ohne sie zu verstehen übernommen. Du brauchst ja gar keine zwei verschiedenen Rasterzeilen für die Unterbrechung und das mit dem `zaehler` ist ein hässlicher Hack, weil die Unterbrechungsbehandlung für die Timer-Unterbrechung und zweimal pro Frame für durch eine Rasterzeile ausgeführt wird. Du rufst die Abspielroutine an der falschen Stelle auf.

    Ja, ich klaue mir alles zusammen. Manchmal denke ich nur nach, wenn es nicht funtioniert. Hier z.B. nicht. Kannst Du mir bitte die einfachere Routine posten, die Du meinst?

    Zitat

    Und das mit `MEM` könnte gefährlich werden.

    Wie wir Kölner sagen: Es hätt noch immer jutgegangen. :smile:

    Zitat

    A) Ich bin mir ziemlich sicher Du verwendest das Wort ”rekursiv” falsch. Und solche Lader gibt es sehr wahrscheinlich nicht. Wie willst Du zuverlässig Code und Daten trennen? Insbesondere wenn der Code sich selbst modifiziert und dadurch im Grunde beides ist. Und in den Daten, die sowohl als Operanden von Befehlen, als auch ”frei” in den reinen Datenbereichen vorkommen können, müsstest Du dann die Adressen identifizieren können, die angepasst werden müssen. Wobei die auch erst durch Berechnungen mit Werten entstehen könnten.

    Wunder verbringen die alle nicht. Aber vieles wird genau richtig disassembliert. Aber vieles auch nicht, hast Du auch Recht.

    Zitat


    Wenn Du einen allgemeinen SID-Player wie Sidplay 64 schreiben möchtest, und dafür C einsetzen willst, würde ich eher das Hauptprogramm in C schreiben und dann einen Lader+Player in Assembler für mehrere Positionen im RAM oder besser noch frei in 256-Byte-Schritten verschiebbar, der am Ende das Hauptprogramm wieder nachläd falls das von den Musikdaten oder beim Abspielen überschrieben worden sein sollte. Es gibt SIDs die wirklich viel Speicher benötigen, weil sie zum Beispiel Sampledaten enthalten oder die Initialisierungsroutine die Playerroutine und die Daten erst einmal quer über den ganzen Speicher verteilt. So ein Song muss nicht in dem Bereich bleiben, in den er geladen wurde!

    Sidplay 64 runtergeladen als .d64, seltsam, aber da sind keine SID-Dateien bei, nur PRG-Dateien. Seltsam.

    Ok, also bei cc65 ist ein Programm namens overlaydemo.c bei und die zugehörige*.cfg. Es wird gezeigt, wie man Segmente mehrfach beschreibt in c und wie man in der cfg erklärt, daß er alle Versionen in eigende Dateien beim Linken schreibt. Im Programm lädt er dann drei versch. Versionen ein und desselben Segments nach und ruft jeweils daraus eine Beispielprozedur. Da könnte man was von machen.

    Wenn ich höre, daß manche SIDs unkontrolliert Speicher benutzen außerhalb der Grenzen wird mir gruselig. :-S

    Zitat


    B) Man muss den Ladecode ausserhalb des Bereichs ablegen der durch das neue Programm belegt wird [1]_. Und dann kann man einfach laden und den geladenen Code mit den entsprechenden Aufrufen starten. Also entweder eine Assemblersequenz, die ein RUN ”nachbaut” oder direkt an die Startadresse springen, falls das nachgeladene Programm kein BASIC-Programm ist und die Startadresse bekannt ist.

    Für diesen Fall jetzt wahrscheinlich nicht so wichtig, aber man kann mit cc65 auch ”overlays” schreiben, in dem man in der Linker-Konfiguration mehrere Speicherbereiche mit den selben Adressen definiert und diese in verschiedene Dateien schreiben lässt und mit ``Bitte melde dich an, um diesen Link zu sehen.``-Anweisungen dann Funktionen und Daten gezielt in für diese Bereiche definierte Segmente platziert. Auf diese Weise kann man nachladbare Teilprogramme realisieren.

    .. [1] Das ist nicht ganz korrekt: Man könnte auch die Startadresse - 1 des nachzuladenen Maschinespracheprogramms auf dem (Hardware)Stack ablegen wenn die eigentliche Laderoutine tatsächlich der allerletzte Aufruf aus dem Code ist, der durch das Laden überschrieben wird.

    Ich habe da schon eine Idee. Die Programmversion, die bis auf die Startinitialisierung mit Code am Ende ist, die kann ja den Anfang mit dem zweiten Programm laden, das am Anfang allen Code hat. Das könnte wie Du sagst schief gehen, aber ich ziele sowieso nur auf die gutartigen SIDs ab...


    Zitat

    C) Ja natürlich. Beispielsweise die Lade-, Initialisierungs-, und Abspieladressen und die Quelle des Timings (Rasterzeile, CIA-Timer, Init macht was eigenes).

    Ich meinte die hier ganz unteren ab flags....

    cbm_load ("fire",8,NULL); (um in irgendsoein Beispielsprogramm zu springen)
    //__asm__ ("jmp $07ff");
    //__asm__ ("jmp $0801");
    __asm__ ("jmp $????");

    Wie bekomme ich leicht raus, wo die main-Funktion beginnt?

    Also einfacher als das:
    __asm__ ("jmp $c00a");
    rausgeholt durch anfrage in fire.c:

    Sehe gerade, daß das zu Problemen führen könnte, weil wohl aus START_UP erst nach Säuberungen in main gesprungen wird.....

  • Vernunftmensch: Ohne es zu verstehen einfach zusammen kopieren ist aber keine besonders gute Art Programme zu schreiben. Insbesondere keine um gute Programme zu schreiben. Dann weiss man ja nie ob das Programm nicht nur zufällig funktioniert.

    Die Unterbrechungsbehandlung würde ich wie gesagt gar nicht in C schreiben, sondern direkt in Assembler.

    Man kann auch aus dem 30. Stock springen und sich bei jedem Stockwerk an dem man vorbei fällt sagen „Hey, bis jetzt ist alles gut gegangen”. :wink: Aber vielleicht sollte man Sachen, die absehbar früher oder später Probleme bereiten werden, besser nicht machen. Zumal wenn sie überhaupt nichts zur Problemlösung beitragen. Das Bitmuster für den Rahmen hat ja nichts mit dem Abspielen von Musik zu tun, sondern ist Teil des Grafikeffektes von dem IRQ-Demoprogramm von odg. Dazu wäre es jetzt wieder hilfreich wenn Du kein Programmieren über Kopieren und Einfügen betreiben würdest, sondern auch verstehen würdest was der Code bewirkt.

    Das ganz hinten im Verzeichnis nicht SID steht ist überhaupt nicht seltsam, weil das keine beliebigen Dateiendungen sein können, sondern immer einer der Dateitypen ist, die das Commodore-DOS kennt. Es werden auf Diskette keine drei Buchstaben gespeichert, sondern ein Zahlenwert, der für den Typ steht. Da gibt es im wesentlichen die Typen DEL, SEQ, PRG, USR, REL, und CBM, die am Ende von der Verzeichnisauflistung stehen können. Das hängt vom DOS im Diskettenlaufwerk ab.

    Eine Dateiendung im Sinne von Dateinamen auf dem PC müsste man also, wenn schon, in den 16 Zeichen unterbringen, die das CBM-DOS für den Dateinamen vorsieht.

    So eine Dateiendung bestimmt aber nicht den Dateityp. Weder auf dem PC noch auf dem C64. Eine Datei im PSID-Format ist nicht weniger eine Datei im PSID-Format, wenn der Dateiname nicht mit ”.sid” endet. Man kann aus so einer Datei durch umbenennen von name.sid nach name.wav ja auch nicht einfach eine WAV-Datei machen. Es bleibt eine PSID-Datei.

    PSID-Dateien haben, wie viele andere binäre Dateiformate auch, ganz am Anfang eine Kennung. Meistens sind das vier Bytes, die lesbare Zeichen enthalten. Im Falle von PSID ist es die Buchstabenfolge "psid", beziehungsweise ''rsid'' bei RSIDs. Das lässt sich bei der Sidplay-Diskette leicht überprüfen:

    Grundsätzlich sollte man davon ausgehen, dass alle PSIDs Speicher ausserhalb des Bereichs in den sie geladen wurden, benutzen. Denn es wird kaum Playroutinen geben, die keine Adressen in der Zeropage verwenden. Da muss man also auch auf Konflikte zwischen der Musikroutine und der cc65-Laufzeitumgebung achten.

    Ab Flags befinden sich auch Informationen, die zum Abspielen wichtig sein können.
    Zum Beispiel ob der Code PlaySID-spezifische Erweiterungen verwendet, die auf einem echten C64 nicht existieren (PSID), oder ob das BASIC-ROM benötigt wird und die mit "RUN" gestartet werden (RSID). Dann steckt da noch die Information drin welcher Video-Standard für Songs angenommen werden soll, die einmal pro Frame gespielt werden. Falls der nicht mit dem von dem Rechner überein stimmt, auf dem der Song abgespielt werden soll, wird er entweder etwas schneller oder langsamer abgespielt, oder man muss doch auf einen Timer-Interrupt zum Abspielen ausweichen.

    Die Information welcher grösste, zusammenhängende Speicherbereich nicht von der Playerroutine verwendet wird, ist sicher auch eine nützliche Information für Dein Programm. Dort kann dann Deine Abspielroutine liegen. Und wenn der Bereich gross genug für das Hauptprogramm ist, könnte man das auch dort hin verschieben und am Ende wieder zurück verschieben, statt es neu zu laden.

    Bei dem Fire-Demo hast Du ”oben” und ”unten” verwechselt. Das Kleiner-als-Zeichen, macht für ”unten” doch auch mehr Sinn. Das ist aber nicht die Startadresse des Programms, sondern die Adresse von der `main()`-Funktion. Bevor die nach einem Programmstart mit RUN aufgerufen wird, müssen noch einige Initialisierungsaufgaben durchgeführt werden.

    Überleg doch mal ein bisschen. Wie startest Du das Programm denn? Doch wohl mit RUN. Und was passiert dann? Liste das Programm mal auf und schau nach.

  • Zitat

    Die Information welcher grösste, zusammenhängende Speicherbereich nicht von der Playerroutine verwendet wird, ist sicher auch eine nützliche Information für Dein Programm. Dort kann dann Deine Abspielroutine liegen. Und wenn der Bereich gross genug für das Hauptprogramm ist, könnte man das auch dort hin verschieben und am Ende wieder zurück verschieben, statt es neu zu laden.

    Mein Player spielt aktuell (fast) alle SIDs ab. Ich erkläre mir das so: Zunächst lädt er brav ein ausgewähltes SID in Bereiche, die egal sind. Je nachdem lädt er einen anderen Player nach, dem nur die abzuspielende Datei übergeben wird und am Anfang des Speichers steht. Dann scheint die Init-Routine scheins noch nicht viel zu machen. Und ausbreitend oder nicht scheint er dann meine Abspielroutinen nicht anzurühren, sobald ich sie mit allen Interrupts freigebe, d.h. Interrupt mit Flackern und die Variablenänderung (quasiparallel laufend).

    Trotzdem möchte ich gerne wissen, wie man den größten zusammenhängenden unberührten Bereich ermittelt, vielleicht könnte man dann sogar einen SID-Wechsel realisieren, während der eine noch läuft. Scheins geht das nicht immer, weil SIDPLAYER64 je nachdem nur noch einen schwarzen Bildschirm zeigt?

    Ich habe zwar eine Möglichkeit, Dateien mit Link anzusetzen, aber kann man nicht hier das auch irgendwie (immerhin reden wir von c64er-Dateien) ?

    Bitte melde dich an, um diesen Link zu sehen.
    - Quellcode
    - PRG-Dateien
    - BeispielSIDs (eine mit Playadresse $0000, die SIDPLAY64 nicht kann.)

    Wo finde ich SIDPLAYER64-source-dateien?

  • Vernunftmensch: Das Programm funktioniert wahrscheinlich nur mit viel Glück überhaupt.

    Sorry, aber der Quelltext sieht sowohl vom Inhalt als auch der Form dermassen gruselig aus, dass ich wünschte da nicht hinein geschaut zu haben. Ein WTF!?-Moment jagt den nächsten.