Seite 1 von 1

Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl.)

Verfasst: 14. Januar 2013, 20:46
von lytron
Tja, sorry Jungs, ist lieder schon weg:
ChronoBoogie hat geschrieben:lytron --> hast pm.
~~~

Eine SPC700-Materialsammlung. Für "Von null auf hundert", un' so.

Brainstorming: Was muss ein allumfassendes SPC700-Tutorial alles enthalten?
  • BRR-Sample-Kompression
  • Datentransfer (65816-Code)
  • Weitere Kommunikation zwischen 65816 und SPC700 (SPC700 sendet Signale und 65816 fängt sie auf; 65816 startet oder stoppt Musik)
  • Die Register des SPC700
  • Das Instruction-Set des SPC700 (im Vergleich zum 65816)
  • Struktur eines SPC700-Programm
Der 65816 kommuniziert mit dem SPC700 über vier Register ($2140-$2143).
$2140 = Status
$2141 = Befehl / Daten
$2142/3 = Adresse
source
Was der Status ist, ist eine ganz eigene Geschichte. Augenscheinlich kann man über $2141 sowohl Befehle, als auch Daten übergeben. Im Falle von Daten ist es recht einfach beschrieben: Man schreibt das "Wohin" in $2142 (Low Byte) und $2143 (High Byte) und das "Was" in $2141.
Der Adressraum des SPC700 reicht von $0000 bis $FFFF.
SPC700 Memory Map:
$0000->$00EF Page 0
$00F0->$00FF Register
$0100->$01FF Page 1 (= Stack)
$0200->$FFBF Speicher
$FFC0->$FFFF Speicher (nicht immer, aber die Spitzfindigkeiten lassen wir erstmal aus)
source

Kurz zusammengefasst: Ab $0200 kann man sich nach Herzenslust austoben.
Die Page 0 und Page 1 sind soetwas wie die Zero-Page bzw. Direct Page. Alle zweistelligen Adressen führen auf diese Pages. Also: $3F ist folglich entweder $003F oder $013F, und man kann einen ganzen Byte sparen, weil man als Adresse eben nur 8 Byte schreiben muss. Welche von beiden es ist, entscheidet sich nach einer Flag im Statusregister, der P-Flag, dem "Direct Page Flag", Bit 5.
Dieser jene Status ist der "neunte Bit" in der Adresse. $3F bei P=0 = $003F, $3F bei P=1 = $013F.
source

Als Grundsätzliches sei gesagt, dass ich die Bezeichnung "SPC700" als "der Soundchip des SNES" hier verwandt habe.
Genauer ist: Der Klang beim SNES entsteht durch S-SMP und S-DSP. Programmiert wird der S-SMP. Der DSP bekommt vom SMP die Rohdaten geliefert und schustert daraus die Tonsignale, die dann ausgegeben werden. SMP und DSP teilen sich denselben Arbeitsspeicher.
Wenn jemand hierzu Korrekturen und Zusatzinfos anbringen kann, bin ich sehr glücklich darüber - dashier ist nur aus einem doofen englischen Wikipedia-Artikel zusammengestokelt. (wo gesagt wird, dass der SPC700 ein Prozessor innerhalb des SMP ist...?)

Um das ganze nochmal verständlich zu verketten:
Du schreibst ein Programm für den 65816, in den du Befehle schreibst, in dem Werte an den SPC700 (=SMP) übergeben werden.
Diese Werte wiederum sind Befehle für den SMP, der entsprechende Daten in 8 Stimm-Kanälen an den DSP übergibt, der daraus eine "Klang-Collage" bildet und die dann aus dem Fernseher tuten lässt.
Bildlich gesprochen gibt es zwei Möglichkeiten (auf unterschiedlichen Leveln der Galanz), Sound zu erzeugen, die man so vergleichen kann, ob man einem Singvogel das Singen beibringt, oder kräftig an seinem Schwanz zieht, um ihm Töne zu entlocken.
Die galante Variante ist, wenn man tatsächlich ein Programm für den SMP schreibt, das eigenständig, das heißt losgelöst vom 65816 agiert. Dazu muss man aber größeren Aufwand betreiben, denn die Lieder sind nicht bloß digitale Notenblätter, sondern Programme für einen eigenen Prozessor, der in manchem dem 65816 sogar überlegen ist/zu sein scheint. Bei dieser Variante programmiert man Programmer für den SPC, kompiliert sie (bei WLA DX ist ein SPC-Compiler dabei, übrigens), und lässt sie im SNES-Programm vom 65816 transferieren.
Die ungalante Art und Weise geht da mit weniger Aufwand: Man schmeißt die Samples in den SPC-RAM und arbeitet mit dem SMP lediglich als Handpuppe, um mit dem DSP zu kommunizieren - wann immer man einen Sound will, schreibt man die entsprechenden Befehle über $2140-3 in die SMP/DSP-Register. Das ist funktional, um ein bisschen Sound zu machen, allerdings vollends unpraktikabel, wenn man Musik machen will (weil der 65816 einen Großteil der Zeit damit beschäftigt wäre, im richtigen Moment den richtigen Vogelschwanz zu ziehen, und folglich weniger Zeit hätte, Sprites sinuswellen-artig über die Glotze zu schupsen).

Betrachten wir noch einmal die Memory Map, ein wenig vereinfacht:
$0000 - $00EF Page 0
$00F0 - $00FF Register
$0100 - $01FF Page 1 (Stack)
$0200 - $FFFF Speicher

Eine genaue Liste der Page 0 ist sehr aufschlussreich:
For each voice (x=voice#):
x0 | VOL (L) | Left channel volume.
x1 | VOL (R) | Right channel volume.
x2 | P (L) | Lower 8 bits of pitch.
x3 | P (H) | Higher 8-bits of pitch.
x4 | SRCN | Source number (0-255). (references the source directory)
x5 | ADSR (1) | If bit7 is set, ADSR is enabled. If cleared GAIN is used.
x6 | ADSR (2) | These two registers control the ADSR envelope.
x7 | GAIN | This register provides function for software envelopes.
x8 | -ENVX | The DSP writes the current value of the envelope to this register. (read it)
x9 | -OUTX | The DSP writes the current waveform value after envelope multiplication
and before volume multiplication.
---------------------------------------------------------------------
0C | MVOL (L) | Main Volume (left output)
1C | MVOL (R) | Main Volume (right output)
2C | EVOL (L) | Echo Volume (left output)
3C | EVOL (R) | Echo Volume (right output)
4C | KON | Key On (1 bit for each voice)
5C | KOF | Key Off (1 bit for each voice)
6C | FLG | DSP Flags. (used for MUTE,ECHO,RESET,NOISE CLOCK)
7C | -ENDX | 1 bit for each voice.
---------------------------------------------------------------------
0D | EFB | Echo Feedback
1D | --- | Not used
2D | PMON | Pitch modulation
3D | NON | Noise enable
4D | EON | Echo enable
5D | DIR | Offset of source directory (DIR*100h = memory offset)
6D | ESA | Echo buffer start offset (ESA*100h = memory offset)
7D | EDL | Echo delay, 4-bits, higher values require more memory.
---------------------------------------------------------------------
xF | COEF | 8-tap FIR Filter coefficients
source

Noch einmal im Schnelldurchgang: Die Register $00, $10, $20 ... $60, $70 tun alle dasselbe, nur jeweils für einen einzelnen der acht Soundkanäle.
Interessante Sache ist z. B. Register $6C. Das ist das Status Register, also Flag Register, vom DSP (nicht vom SMP!). Hier lassen sich Effekte wie Echo oder Mute einstellen. Weitere interessante Sachen ist z. B. das Regsiter $6D. Darüber konnte ich heute in mics SPC-Dokumentation etwas lesen. Eigentlich wollte mic nur den SPC-Player fixen, dessen Sourcecode bei Wikibooks angeboten wird. Seine Funde hat er zusammengetragen, und mir haben sie geholfen, ein etwas besseres Verständnis zu bekommen.

Es gäb dazu noch so viel zu schreiben...
... ein andern Mal...

tbc

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 14. Januar 2013, 20:55
von Moi-Même
????????


Ich raff nich ein einziges Wort. Sorry.

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 15. Januar 2013, 19:42
von lytron
Übrigens stütze ich ja, wie man gesehen hat, das meiste auf diese Erklärung. Diese wurde, wie ich heute erst sah, schrob sie EKid aka. Mukunda aka. Mukunda Johnson.
Der hat auch das bisher einzige (halb) funktionierende Tool zum SPC-Dateien erstellen geschrieben (ein Umwandler von MOD-Dateien), mit dem KungFuFurby die Mucke für Tchou und UWOL geschrieben hat. Außerdem ist Mukunda uns als Quarte des Quartetts hinter Skipp & Friends ein Name.

Die Welt ist ein Dorf, und die SNES-Szene eine Hippie-Kommune.

Aber jetzt...

Let's get super-nerdy!

Thema: PITCH

Allgemein-Musiktheoretisches
Ich habe nachgerechnet und festgestellt:
Tonfrequenzen gleichen einer Exponentialfunktion.

Hast du einen Ton mit der Frequenz x, dann ist ein Halbton darüber *immer*

x * 2^(1/12)
(iks mal zwei hoch ein Zwölftel)

Damit deckt es sich auch, dass der Oktavton immer die doppelte Frequenz seines Grundton hat.

Kammerton a' = 440 Hz
Oktavton = a'' = 12 Halbtonschritte aufwärts

440 * 2^(12/12) = 440 * 2^(1) = 440 * 2 = 880 Hz

Übrigens, wer super-super-nerdy sein will und originalgetreue Weltmusik mit dem SPC schreiben will, dem sei empfohlen, sich mal in ein wenig in südostasiatische und südosteuropäische Folklore-Musik einzulesen. Ich meine gelesen zu haben, dass die Tonleitern benutzen, die auf 2^(x/7) basieren, was der Grund ist, weshalb uns Westlern dabei die Ohren bluten.

SPC700-spezifisches
Jeder Channel hat ein Pitch-Register (bzw. zwei: $x2 und $x3, für Low Byte und High Byte, wobei der High Byte nur 6 statt 8 Bits hat).
Der Inhalt des Pitch-Registers reicht von #$0000 bis #$3FFF (weil: 14-Bits. Klaro, woll?).

Wenn man einen Sample da reinschmeißt, und ihn in Originalhöhe abspielen lassen will, muss man in das Register eine "$1000" schreiben. Folglich lässt sich ein Sample maximal (fast) zwei Oktaven aufwärts pitchen (für ganz exakt 2 Oktaven müsste man zu den $4000 kommen können. Aber entweder arrangiert man sich mit den $3FFF, oder man benutzt einen anderen Sample. Hah, versteht ihr den!? "arrangiert"! Verstanden?! Es geht hier um Musik... und ich schreibe "arrangiert". Habt ihr den verstanden, hmmm?).
Abwärts lässt sich der Sample *theoretisch* bis ins Unendliche pitchen (da ja bei konstanter Halbierung nur dank einer Rundung die null erreicht werden kann), wobei Mukunda darauf hinweist, dass es irgendwann nicht mehr anständig klingt.

Was auch klar ist, da wir ja hier mit ganzen Zahlen hantieren und die Zahlen nach unten hin immer enger beieinander liegen, und man folglich leicht out-of-tune kommt, weil man den korrekten, gebrochenen Wert nicht trifft).

Das tolle ist, dadurch, dass man im Register immer diesen willkürlichen Wert von 1000 hat und nicht die tatsächliche Hertzzahl, kann man einen Sample mit einem eingestrichenen H oder einem viergestrichenen Fis reinschmeißen, den Ganztonschritt nach oben kriegt man immer mit 1000*2^(2/12) = 11F5 hin.
source

Hammer, woll?

Das brigt aber selbstverfreilich ein wenig Verwirrungspotential, wenn man zwei Samples mit unterschiedlichem Grundton nimmt. Bei Samples mit gleichem Grundton kann man wenigstens dasselbe Set von Werten benutzen. Das ist genauso wie damals in der siebten Klasse, im Musikunterricht, mit diesen bescheuerten transponierenden Instrumenten... wie kann man sowas nur freiwillig spielen!? Dämlicher Schrott, sowas. Kauft euch mal ein anständiges Notensystem, ihr Nasen, ihr!

So.

Wo ich gerade bei "Set von Werten" sind. Ich habe mal gerade nachgerechnet, was wohl die einzelnen passend(st)en Werte für einige Töne sind. Ich habe mal zwei Oktaven rauf und drei Oktaven runter gerechnet:
versteckter Inhalt:
-48 100
-47 10F
-46 11F
-45 130
-44 142
-43 155
-42 16A
-41 17F
-40 196
-39 1AE
-38 1C8
-37 1E3
-36 200
-35 21E
-34 23E
-33 260
-32 285
-31 2AB
-30 2D4
-29 2FF
-28 32C
-27 35D
-26 390
-25 3C6
-24 400
-23 43C
-22 47D
-21 4C1
-20 50A
-19 556
-18 5A8
-17 5FE
-16 659
-15 6BA
-14 720
-13 78D
-12 800
-11 879
-10 8FA
-9 983
-8 A14
-7 AAD
-6 B50
-5 BFC
-4 CB2
-3 D74
-2 E41
-1 F1A
0 1000
1 10F3
2 11F5
3 1306
4 1428
5 155B
6 16A0
7 17F9
8 1965
9 1AE8
10 1C82
11 1E34
12 2000
13 21E7
14 23EB
15 260D
16 2851
17 2AB7
18 2D41
19 2FF2
20 32CB
21 35D1
22 3904
23 3C68
24 3FFF
Übrigens ist es interessant, dass viele Samples mit Vorliebe tiefer sind, als sie im eigentlichen Spiel auftauchen (bestes Beispiel die Samples mit Dan Owsens Stimme aus Super Metroid). Ich erkläre mit das so, dass es mit der Kompression zusammenhängt; die Samples werden bei höheren Pitches automatisch schneller abgespielt, folglich hat man die Samples langsamer und tiefer gemacht, bevor man sie BRR-komprimiert hat. Sie waren langsamer, folglich länger, folglich hatten sie mehr Daten, folglich gab es durch die Kompression weniger Abweichungen vom Original.
Das denke ich mir aber nur so. Vielleicht liegt die Begründung auch anders.

SRCN und DIR (Source Number und Directory)
Also.
Irgendwo suchst du dir max. $400 Byte freien Platz im RAM. Nehmen wir z. B. die Adresse $3A00. Beliebiges Beispiel. Es muss allerdings im Hexadezimalen auf Doppelnull enden.
Dort schreibst du eine Liste deiner Samples. Du schreibst in $3A00/1 (Low Byte, High Byte) die Adresse deines ersten Samples, Sample Nr. 0. In $3A02/3 schreibst du die Anfangsadresse des Teils des Samples, der geloopt werden soll. Danach fährst du so mit dem nächsten Sample fort, bis du 256 Einträge beisammen hast (oder eben auch nicht, wenn du weniger Samples hast, wovon ich mal ausgehe).
Das ist jetzt dein "Source Directory".
Es gibt das Register $5D, das DIR bzw. "Source Directory Register" heißt. Hier schreibst du "$3A" rein.
Dann wiederum gibt es für jeden Kanal ein SRCN-Register ($x4). Dort trägst du die Nummer des Samples ein, die dieser Kanal haben soll.

source number source source directory source

Beispiel:
Du willst in Kanal 2 eine Flöte (hohe Oktaven) haben, die wiederum, sagen wir, Sample 5 sind.
Also trägst du die 5 in Register $24 ein. Es wird die fünf genommen, dann wird in $5D nachgeschaut, wo das Source Directory liegt. in $5D steht "$3A", also wird gerechnet:
$3A00 (als Startadresse) + 4 * 5 (weil: Jeder Eintrag im Source Directory ist 4 Bytes lang, und wir wollen Sample 5).
Das heißt, es wird die Adresse für den Sample genommen, die in $3A14/5 steht. (4 * 5 = $14. It's hex, baby!).

Instruction Set
Zum Abschluss ein kurzes Wort zum Instruction Set (ich werde es nicht in der Gänze erklären, weil einiges redundant wäre, wenn man das Instruction Set des 65816 ein wenig kennt... und wer es nicht kennt, wird sicherlich schon vor dieser Stelle abgesprungen sein! :D ).

Es ist cool.

Ein etwas längeres Wort:
Im Vergleich zum 65816 muss man nicht mit einem Befehl in den Akkumulator laden und mit einem weiteren den Inhalt des Akkumulators speichern, sondern kann mit einem einzigen Befehl einen Wert in etwas speichern. Dieser Befehl heißt "MOV". Und, die zweite Wichtigkeit bei dem Befehl ist, dass das erste Argument das Ziel ist, das zweite der Ursprung. "MOV $AA, #$AA" heißt also: Speichere die Zahl #$AA in das Register $AA.

Das coole ist, dass man sich weniger Mnemonics als beim 65816 merken muss, denn wenn man sich das Instruction Set im Ganzen mal ansieht (Link unten als "source"), stellt man fest, dass es zig Spielarten von Daten-Verschiebungen gibt, die alle unter MOV läuft. Leicht zu merken (und, jaa, leicht zu verwechseln).

Der SMP ist ein 8-Bit-Prozessor, kann aber auch 16-Bit-Befehle. Das durchgeknallte ist: Dann werden Y und A einfach zusammengelegt. Irre, was?

source

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 16. Januar 2013, 12:27
von lytron
Halt, stopp, Kommando zurück.

Der SMP hat nur Spezial-Register von $F0 - $FF. Zwei davon $F2 und $F3, dienen für die Kommunikation zwischen SMP und DSP wie die Register $2141 bis $2143 bei der Kommunikation zwischen 65816 und SMP.

Die Register für die einzelnen Soundkanäle sind DSP-Register, das heißt, ich schreibe beim $F2 hinein, in welches Register vom DSP ich schreiben möchte, und in $F3, was ich schreiben möchte.

Und wenn man das vom 65816 aus steuern möchte...

... dann ist das eine "doppelte Handpuppe".

Später dazu mehr.

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 19. Januar 2013, 11:18
von lytron
Also. Geht es "nach längerer Krankheit" hier auch weiter.

Um das noch einmal herauszustellen:
$(00)00-$(00)EF vom SMP sind zur freien Verfügung. Die Channeleinstellungen etc. sind Register des DSP, den man über $F2 (Adresse) und $F3 (Data) anspricht.

DAS HEISST wenn ich zum Beipiel in den linken Main Volume Channel (Register $0C) den Wert #$3B schreiben will, schreibe ich im SMP-Assembler (das habe ich jetzt nicht im Original gecheckt, könnte also völlig falsch sein):

Code: Alles auswählen

mov $F2, #$0C
mov $F3, #$3B
Und, wenn man das Ganze jetzt vom 65816 aus machen möchte (ich lasse mal das Status-Register $2141 außen vor, weil... das doof ist).

Code: Alles auswählen

LDA #$F2
STA $2142
STZ $2143
LDA #$0C
STA $2141
LDA #$F3
STA $2142
LDA #$3B
STA $2141
Wobei das jetzt noch nicht mal funktionieren würde, hah!
Denn, man muss ja immer, wenn man in eins der APU-Register schreibt, warten und comparen, bis der Wert im Register auch tatsächlich dem Wert entspricht, den man in das Register geschrieben hat.
Das liegt daran, dass dieselben Register für Transfers in beide Fließrichtungen genutzt werden, und so Daten miteinander kollidieren könnten.
source

Aber allein das Beispiel oben zeigt schon, dass es nicht "mal eben" vom 65816 aus zu machen ist.

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 19. Januar 2013, 15:25
von lytron
Übrigens weiß ich jetzt auch, was $FFC0 bis $FFFF im RAM von SMP und DSP so besonderes macht: Da liegt der/die/das IPL, das Hochfahr-Programm.
(source dafür is' verschütt)

Glorioserweise hatte dafür vor Äonen schon Anomie einen Disassemble und eine Erklärung dargeboten, doch ich scheue keine Mühen, noch Nerven der Forenmember, um es hier auch noch einmal zu erklären.
source (Verwirrend ist, dass Anomie drei Dokumentationen geschrieben hat, die auch noch alle annähernd gleich heißen, aber inhaltlich vollends verschieden sind)

Code: Alles auswählen

.ORG $FFC0
        MOV X, #$EF    ; *** INIT ***
        MOV SP, X      ; setup stack
        MOV A, #$00    ; clear page 0 RAM
-       MOV (X),A
        DEC X
        BNE -
        MOV $F4,#$AA   ; Signal "ready" to 5A22: $2140-1 will return #$BBAA
        MOV $F5,#$BB
-       CMP $F4,#$CC   ; wait for 5A22 to write #$CC to $2140
        BNE -
        BRA Start
Trans:  MOV Y,$F4      ; *** TRANSFER ROUTINE ***
        BNE Trans      ; First, wait for 5A22 to indicate Byte 0 ready on $2140
-       CMP Y,$F4      ; start loop: wait for "next byte/end" signal on $2140
        BNE +
        MOV A,$F5      ; Got "next byte" ($2140 matches expected byte index)
        MOV $F4,Y      ; Read byte-to-write from $2141, echo $2140 to signal
        MOV [$00]+Y,A  ; ready, and write the byte and update the counter.
        INC Y
        BNE -
        INC $01        ; (handle $xxFF->$xx00 overflow case on increment)
+       BPL -
        CMP Y,$F4      ; If "next byte/end" is not equal to expected next byte
        BPL -          ; index, it's "end": drop back into the main loop.
Start:  MOVW YA,$F6    ; *** MAIN LOOP ***
        MOVW $00,YA    ; Get address from 5A22's $2142-3,
        MOVW YA,$F4    ; mode from $2141, and echo $2140 back
        MOV $F4,A
        MOV A,Y
        MOV X,A
        BNE Trans      ; Mode non-0: begin transfer
        JMP [$0000+X]  ; Mode 0: jump to address
        .DW $FFC0      ; RESET vector
Schrittweise Betrachtung:

Code: Alles auswählen

        MOV X, #$EF    ; *** INIT ***
        MOV SP, X      ; setup stack
Der Stackpointer (SP) wird auf X gesetzt, wobei X vorher auf #$EF gesetzt wird.
versteckter Inhalt:
Wirft in mir einige Fragen über die Register $01F0 bist $01FF auf. Sind $01F0 bis $01FF dasselbe wie $00F0 bis $00FF (also: gleichgeschaltet), oder sind mit dem gesetzten Direct Page Flag (DP = $0100 bis $01FF) ab DP-Reg. $F0

wieder nur $00F0 bis $00FF erreichbar?
Letzteres halte ich für unwahrscheinlich, weil etwas derartiges bestimmt genau beschrieben worden wäre und ich das sicher schon gelesen hätte. Ich gehe aus, dass sich das von selbst klären wird.

Code: Alles auswählen

        MOV A, #$00    ; clear page 0 RAM
-       MOV (X),A
        DEC X
        BNE -
Eine Schleife, die den Wert in A (#$00) in alle Register von $00EF bis hinab zu $0000 speichert.

Code: Alles auswählen

        MOV $F4,#$AA   ; Signal "ready" to 5A22: $2140-1 will return #$BBAA
        MOV $F5,#$BB
Das kennt man ja von der Gegenseite: "Warte, bis der SPC #$BBAA schreibt."

Code: Alles auswählen

-       CMP $F4,#$CC   ; wait for 5A22 to write #$CC to $2140
        BNE -
        BRA Start
Das wiederum kennt man auch, das Warten auf das Startsignal von 65816-( bzw., korrekter, Ricoh 5A22-)Seite.
Heißt also: In allen ROMs, in denen kein Sound vorkommt, bleibt der SMP in dieser Schleife hängen. Wenn er daraus befreit wird, wird zu "Start" gebrancht (das tue ich jetzt ebenfalls).

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Code: Alles auswählen

Start:  MOVW YA,$F6    ; *** MAIN LOOP ***
        MOVW $00,YA    ; Get address from 5A22's $2142-3,
Es wird, wie beschrieben, der Inhalt von $2142/$2143 in YA geladen. YA ist, wie erwähnt, ein Quasi-16-Bit-Register, bestehend aus den 8-Bittern A und Y.
Der Wert wird dann in den Registern $00 und $01 abgelegt.
Übrigens sieht man dann hier auch den 16-Bit-Befehl MOVW mal in Action.

Code: Alles auswählen

        MOVW YA,$F4    ; mode from $2141, and echo $2140 back
        MOV $F4,A
        MOV A,Y
        MOV X,A
        BNE Trans      ; Mode non-0: begin transfer
        JMP [$0000+X]  ; Mode 0: jump to address
        .DW $FFC0      ; RESET vector
Es werden auch die Register $2140/$2141 in YA geladen. Dann wird A, das den Inhalt von $2140/$F4 enthält, wieder in $2140/$F4 gespeichert ("echo $2140 back").
Anschließend wird Y ($2141/$F5/"Command/Data") in A und X kopiert (weil...?) und selbiger Wert wird dann getestet, ob er null ist oder nicht.
Bei nicht-null springt er zu "Trans", bei null springt er zu der Adresse, die zuvor in $00 abgelegt wurde. Dass X addiert wird, hat keinen Effekt, da X = 0 ist.
Hiermit wird ersichtlich, weshalb im Transferring Data from ROM to the SNES APU gesagt wird, dass man, bevor man das #$CC-Startsignal gibt,

*irgendwas anderes außer null* in $2141 schreiben muss. Ansonsten is nix mit Transfer, sondern der SMP wird in die Wüstenei eines großen, bestenfalls leeren RAM geschickt. Gerade geguckt: $00 ist der Opcode für "NOP", also passiert nix

schlimmes, irgendwann kommt er schon wieder beim Anfang vom IPL an $FFC0. Passiert nix schlimmes... aber auch nix sinnvolles.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Code: Alles auswählen

Trans:  MOV Y,$F4      ; *** TRANSFER ROUTINE ***
        BNE Trans      ; First, wait for 5A22 to indicate Byte 0 ready on $2140
Einfach ausgedrückt:Solange $2140/$F4 nicht null ist, bleibt der SMP in diesem Loop hängen.

Code: Alles auswählen

-       CMP Y,$F4      ; start loop: wait for "next byte/end" signal on $2140
        BNE +
Wenn der Abgleich von Y und $2140/$F4 ("Status"-Register) nicht gleich ist, geht es weiter bei +, ist es gleich, geht es mit dem Code im Anschluss weiter.
Da es ja jetzt gerade null ist (weil wir soeben den Inhalt von $2140/$F4 in Y kopiert haben), geht es also ohne Branch direkt weiter.

Code: Alles auswählen

        MOV A,$F5      ; Got "next byte" ($2140 matches expected byte index)
        MOV $F4,Y      ; Read byte-to-write from $2141, echo $2140 to signal
        MOV [$00]+Y,A  ; ready, and write the byte and update the counter.
        INC Y
        BNE -
Der erste Befehl lädt den ersten Daten-Byte aus $2141/$F5.
Der zweite Befehl echoed nach $2140/$F4 ("Daten erhalten!").
Der dritte Befehl speichert den Daten-Byte an die Adresse, die in $00 und $01 liegt, plus Y (zur Zeit ist Y=0).
Der vierte Befehl erhöht den Counter um eins.
Der fünfte Befehl lässt loopen, wenn Y nicht null geworden ist.

Code: Alles auswählen

        INC $01        ; (handle $xxFF->$xx00 overflow case on increment)
+       BPL -
        CMP Y,$F4      ; If "next byte/end" is not equal to expected next byte
        BPL -          ; index, it's "end": drop back into the main loop.
Wenn Y=0 ist, dann erhöhe $01. Es ist klar, weshalb.

Nehmen wir an $00 und $01 enthalten gemeinsam die Adresse $1260 und Y enthält $FF.
Dann wurde der letzte Daten-Byte nach $1260 + $FF = $1359 geschrieben. Y wurde inkrementiert und ist damit wieder 0. Das heißt, würde jetzt nicht $01 inkrementiert, wäre die nächste Adresse, in die geschrieben würde, $1260 + $00 = #

$1260, und der Datentransfer würde dieselben $100 Byte beliebig oft überschreiben. Deshalb wird $01 (der High Byte!) also erhöht, damit aus $1260 dann $1360 wird und wir morgen noch kraftvoll zubeißen können.

---

Bei dem letzten Bißchen bin ich gerade ein wenig überfordert. Ich kann nur nach dem gehen, was in den Kommentaren steht, und weiß, dass es so funktioniert. Aber, wey, ich habe es ja eh nicht so mit Befehlen und deren Auswirkungen auf das

Flagregister habe...

... denke ich nicht, dass nach zwei Stunden auf der Stelle treten noch der große Durchbruch kommt. ^_~

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 20. Januar 2013, 15:50
von papierkorb
Verstehe diesen Thread überhaupt nicht!
Was hat der Threadtitel mit dem Rest zu tun...und was beduetes alles überhaupt xD

Re: Verkaufe 700副鼻腔炎SPC~l'amour éternel~ (PAL, OVP, ohne Anl

Verfasst: 20. Januar 2013, 16:03
von ChronoMoogle
Das ist ein Thead zur Musikprogrammierung auf dem Super Nintendo :D
Ich verstehe auch nur Bahnhof, aber es ist wie immer interessant zu lesen und ein bisschen Programmierwissen schnappt man unbewusst immer mit auf.
Schoene Arbeit, lytron :)