[Fortgeschritten] Textroutinen finden und MTE/DTE Routinen mit einbauen.

  • Status: Im Aufbau


    Unser Testobjekt:
    E.V.O. - Search for Eden (USA)
    Ohne Header, CRC32: DD49911E


    Benötigte Tools:
    "Geiger's Snes9x Debugger"
    "Lunar Adress und Lunar Expand", gibt's hier
    "xkas v0.6", gibt's hier


    Was sind MTEs/DTEs?
    MTE (Multiple Table Entry) bedeutet dass mehrere Zeichen auf einem Byte in der Table liegen, zum Beispiel: 80=have
    DTE (Dual Table Entry) bedeutet dass mehrere Zeichen auf 2 Bytes liegen, dabei wird das erste zum Identifizieren genommen (sollte eins sein was nicht in der normalen Table auftaucht), und das zweite legt fest welcher Eintrag es ist, zum Beispiel: 0367=someone


    Bitte PM schreiben falls "MTE (Multiple Table Entry)" oder "DTE (Dual Table Entry)" die verkehrten Begriffe sind, kann mich da nur schwach erinnern :o)



    Wozu dienen MTEs/DTEs?
    Einfach um Platz zu sparen, eine Liste der meistbenutzten Wörter oder SIlben wird irgendwo abgelegt, mit Pointern versehen und im Haupttext wird jede dieser SIlben oder jedes dieser Wörter durch das jeweilige Byte ersetzt.


    Beispiel MTE: "You have a question?" <- Länge 20 Bytes... Ersetzen wir jetzt "have" mit dem Wert 80 erhalten wir "You \0x80\ a question?" und unser Text ist nur noch 17 Bytes lang.
    Beispiel DTE: "Ask someone else!" <- Länge 17 Bytes... Ersetzen wir jetzt "someone" mit dem Wert 0367 erhalten wir "Ask \0x0367\ else!" und unser Text ist nur noch 12 Bytes lang.



    Vorbereiten der ROM (MTE):
    Als erstes expandieren wir die ROM auf 16MBit, oder ihr sucht euch freien Platz irgendwo, dann erstellen wir uns eine Pointertable mit unseren TEST MTEs.


    Beispiel: PC Offset 0x180800, SNES LoRom Adresse $30:8800



    Finden der Textroutine:
    Als erstes sucht ihr euch etwas Text in der Rom, am besten irgendwo am Anfang des Spiels, in unserem Beispiel nehmen wir "Hi! My name is Gaia......", zu finden an Offset 0xEE8F3, dann konvertieren wir das Ganze in eine LoRom Adresse $1D:E8F3, mit dieser Adresse starten wir nun Snes9x und warten das der Breakpoint ausgelöst wird.


    Im Debug Log sollte nun dies auftauchen: $03/B608 B9 00 00 LDA $0000,y[$1D:E8F3] A:002E X:0000 Y:E8F3 P:envmxdIZc


    Wenn wir jetzt einige Male "Step Into" machen, können wir so ziemlich die ganze Routine sehen:

    Und so weiter, und so fort...


    Ich habe die ganze Routine mal nachgeschrieben und mit Kommentaren versehen, damit man es besser nachvollziehen kann:
    Einige Infos vorab:
    - Unser Rombank im Beispiel ist $1D
    - Unser erste Rombank Position ist
    $E8F3 (also Register Y=$E8F3)
    - Der OR Key an Offset $32 ist hier immer $2000



    READ_NEXT_LETTER:
    LDA $0000,y —————————> ; Y enthält unsere derzeite Position in der Rombank in der wir uns befinden ($1D:E8F3)

    AND #$00FF ——————————> ; AND Operation, z.B.: (Wert $6948 in A) -> $6948 AND &#00FF = $0048
    ORA $32 —————————————> ; OR Operation, z.B.: (Wert $0048 in A) -> $0048 OR $32 = $2048
    STA $34 —————————————> ; Speichern von A in die Ram Adresse $34 (Unsere Bytes $2048 nach Offset $34)
    AND #$00FF ——————————> ; AND Operation mit A ($2048 AND #$00FF = $0048)
    CMP #$00FF ——————————> ;
    Vergleichen (CoMPare) ob der Wert in A $00FF ist
    BEQ END_FOUND ———————> ; Wenn Ja (BRanch Equals), dann gehe zu END_FOUND (siehe weiter unten)
    CMP #$00FE ——————————> ;
    Vergleichen (CoMPare) ob der Wert in A $00FE ist
    BEQ NEXT_LINE_FOUND —> ; Wenn Ja (BRanch Equals), dann gehe zu NEXT_LINE_FOUND (siehe weiter unten)
    Wenn der Code bis hierhin durchläuft haben wir weder einen Zeilenumbruch, noch
    das Ende unseres Textes erreicht, sondern wir haben einen normalen Buchstaben

    PHX —————————————————> ; X in den STack schieben (PusH X)
    TXA —————————————————> ; Transferiere X nach A
    CLC —————————————————> ; CLear Carry, löscht den Carry falls was "drin" ist
    ADC $36 —————————————> ; ADd with Carry , adde das was an Ram Offset $36 steht zu A
    TAX —————————————————> ; Transferiere A nach X
    LDA $34 —————————————> ; LaDe nach A was an Ram Offset $34 steht (Unser Buchstabe)
    STA $7F6000,x ———————> ; STore A, Speichern von A an Ram Offset $7F6000+x
    INY —————————————————> ; INcrease Y (Unsere derzeite Position in der Rom)
    PLX —————————————————> ; PuLl X, jetzt holen wir uns unser Index Register X wieder zurück
    INX —————————————————> ; INcrease X (Unser Schreiboffset für die Buchstaben)
    INX —————————————————> ; INcrease X
    JMP READ_NEXT_LETTER


    NEXT_LINE_FOUND:
    LDA $36 —————————————> ; LaDe nach A was an Ram Offset $36 steht
    CLC —————————————————> ;
    CLear Carry, löscht den Carry falls was "drin" ist
    ADC #$002E ——————————> ; AD to Carry, adde $002E zu A
    STA $36 —————————————> ; STore A, Speicher A in Ram Offset $36
    INY —————————————————> ; INcrease Y, Y erhöhen (Unsere derzeite Position in der Rom)
    LDX #$0000 ——————————> ; LaDe $0000 in X
    JMP READ_NEXT_LETTER


    END_FOUND:
    TXA —————————————————> ; Transferiere X nach A
    CLC —————————————————> ; CLear Carry
    ADC $36 —————————————> ; ADd with Carry , adde das was an Ram Offset $36 steht zu A
    TAX —————————————————> ; Transferiere A nach X
    LDA #$FFFF ——————————> ; LaDe $FFFF in A
    STA $7F6000,x ———————> ; STore A, Speicher A in Ram Offset $7F6000+x
    JML $03B653 —————————> ; JuMp Long, Springt zu dem Punkt nach der Originalen Routine




    Jetzt packen wir ein wenig eigenen Code dazu, alles was neu ist ist Rot markiert:

    org $308000
    READ_NEXT_LETTER:
    LDA $0000,y —————————> ; Y enthält unsere derzeite Position in der Rom
    AND #$00FF ——————————> ; Die letzten 8 Bit prüfen und in A laden
    ORA $32 —————————————> ; R auf den Akkumulator mit dem Wert der in der Ram Adresse $32 steht
    STA $34 —————————————> ; Speichern von A in die Ram Adresse $34 (Unser Buchstabe)
    AND #$00FF ——————————> ; Die letzten 8 Bit prüfen und in A laden, entfernt OR wieder
    CMP #$00FF ——————————> ; vergleichen ob der Wert in A $FF ist
    BEQ END_FOUND ———————> ; Wenn Ja (BRanch Equals), dann gehe zu END_FOUND (siehe weiter unten)
    CMP #$00FE ——————————> ; vergleichen ob der Wert in A $FE ist
    BEQ NEXT_LINE_FOUND —> ; Wenn Ja (BRanch Equals), dann gehe zu NEXT_LINE_FOUND (siehe weiter unten)
    CMP #$0080 ---------
    —> ; CoMPare, Vergleichen ob A den Wert $0080 oder höher hat, wenn ja wird der "Carry Flag" gesetzt
    BCS READ_MTE_POINTER-> ; Branch if Carry is Set, Wenn der Carry Flag gesetzt ist, springe weiter und lese den MTE Pointer
    PHX —————————————————> ; PusH index register X, speichern von X im Stack
    TXA —————————————————> ; Transferiere X nach A
    CLC —————————————————> ; CLear Carry
    ADC $36 —————————————> ; ADd to Carry , adde das was an Ram Offset $36 steht zu A
    TAX —————————————————> ; Transferiere A nach X
    LDA $34 —————————————> ; LaDe nach A was an Ram Offset $34 steht (Unser Buchstabe)
    STA $7F6000,x ———————> ; STore A, Speichern von A an Ram Offset $7F6000+x
    INY —————————————————> ; INcrease Y (Unsere derzeite Position in der Rom)
    PLX —————————————————> ; PuLl X, jetzt holen wir uns unser Index Register X wieder zurück
    INX —————————————————> ; INcrease X (Unser Schreiboffset für die Buchstaben)
    INX —————————————————> ; INcrease X
    JMP READ_NEXT_LETTER

    Sollte unser "Carry Flag" gesetzt gewesen sein, landen wir hier
    READ_MTE_POINTER:
    INY -----------
    ------> ; Y erhöhen
    PHY -----------
    ------> ; Y in den Stack verschieben
    PHX ----------
    -------> ; X in den Stack verschieben
    SBC #$0080 ---
    -------> ; SuBstract with Carry, wir ziehen jetzt $0080 von unserem gelesenen Wert ab
    ASL ---------
    --------> ; Arithmethik Shift Left, einmal nach links "shiften", ist dasselbe wie mit 2 multiplizieren
    TAX ---------
    --------> ; Transferiere A nach X
    LDA $318800,x-
    -------> ; LaDe nach A von Offset $318800+X
    TAX ---------
    --------> ; Transferiere A nach X
    Das Lesen des Pointers der zum MTE Eintrag führt ist beendet, weiter zum lesen des MTEs
    JMP READ_MTE


    Jetzt lesen wir unseren MTE, den Pointer dahin haben wir vorher generiert und in X abgelegt
    READ_MTE:
    LDA $318800,x ---
    ---->
    LaDe nach A von Offset $318800+X,
    AND #$00FF ----------> Wir holen uns die Bits die wir brauchen
    CMP #$00FF
    ----------> Vergleichen ob wir das Ende erreicht haben (Terminierer ist $FF hier)
    BEQ MTE_READ_END ----> Wenn das Ende errecith wurde ($FF), dann sind wir fertig mit lesen und springen weiter
    ORA $32 -----------
    --> OR mit dem Wert an Offset $32 auf A
    STA $34 -----------
    --> Speichern von A an Offset $34
    TXY ---------------
    --> Transferiere X nach Y
    INY ---------------
    --> Erhöhe Y um 1
    PLX ---------------
    --> X aus dem Stack holen
    TXA ---------------
    --> Transferiere X nach A
    CLC ---------------
    --> CLear Carry
    ADC $36 -----------
    --> ADd with Carry, adde das was an Offset $36 steht zu A
    INX
    -----------------> Erhöhe X um 1
    INX -----------------> Erhöhe X um 1
    PHX -----------------> PusH X, Speichern von X im Stack
    TAX ---------------
    --> Transferiere A nach X
    LDA $34 -----------
    --> LaDe nach A von Adresse $34
    STA $7F6000,x -----
    --> Speichern unseres generierten Wertes im RAM
    TYX ---------------
    --> Transferiere Y nach X
    JMP READ_MTE
    --------> Da unser Vergleich am Anfang der Routine kein Ende errreicht hatte springen wir zurück und lesen das nächste Byte, bis es den Wert $FF hat


    Wenn unser Terminier-Byte ($FF) erreicht wurde ist das Lesen des MTEs beendet unn wir holen uns unsere Offsets für die normale Routine die wir ganz am Anfang in den Stack geschoben haben wieder zurück, damit die Routine damit weiter arbeiten kann
    MTE_READ_END:
    PLX
    -----------------> X aus dem Stack holen
    PLY
    -----------------> Y aus dem Stack holen
    JMP READ_NEXT_LETTER-> Und hier geht's wieder zurück um den nächsten Buchstaben zu lesen


    NEXT_LINE_FOUND:
    LDA $36 —————————————> ; LaDe nach A was an Ram Offset $36 steht
    CLC —————————————————> ; CLear Carry
    ADC #$002E ——————————> ; AD to Carry, adde 002E zu A
    STA $36 —————————————> ; Speicher A in Ram Offset $36
    INY —————————————————> ; INcrease Y (Unsere derzeite Position in der Rom)
    LDX #$0000 ——————————> ; LaDe $0000 in X
    JMP READ_NEXT_LETTER


    END_FOUND:
    TXA —————————————————> ; Transferiere X nach A
    CLC —————————————————> ; Clear Carry
    ADC $36 —————————————> ; ADd to Carry , adde das was an Ram Offset $36 steht zu A
    TAX —————————————————> ; Transferiere A nach X
    LDA #$FFFF ——————————> ; LaDe $FFFF in A
    STA $7F6000,x ———————> ; Speicher A in Ram Offset $7F6000+x
    JML $03B653 —————————> ; Springt zu dem Punkt nach der Originalen Routine

    • Offizieller Beitrag

    Mach mal bitte weiter... ich hab bis jetzt alles gerafft, aber die Neue Routine solltest du bitte noch erklären!
    Kurze Frage noch: Wird bei der getracten Routine nur 1 Zeichen gelesen oder eine ganze Zeit bis zum Endbyte? Oder passiert für jedes Zeichen der komplette Code hintereinander weg?


    Danke dir...



    Gruß


    red