Hilfe bei Routine

  • Die folgende Routine ist meine erste (größtenteils) selbsterstellte, also seid bitte gnädig :D

    Bei dem Versuch den Code zu kompilieren, kam die Fehlermeldung, die xor Befehle und der mod Befehl wären "bad instructions". Dass der mod Befehl vllt. nicht klappt habe ich mir gedacht, aber was ist denn an den exklusiven oder Befehlen falsch?

    Zum Code: Die Routine soll abhängig von der variable 0x8000 und je nach Typ des Pokemons die Farbe des Statuswertes ändern (blau für gesteigerten ATK Wert, Rot für geschwächten). Ich bin mir sicher, dass der Code voller Fehler ist, wem was auffällt bitte sagen, denn aus Fehlern lernt man bekanntlich am besten8o

  • Das sind bad instructions... Weil es bad instructions sind (welp) - Weil es die Instructions einfach nicht gibt. Ein exklusives oder ist ein EOR, und ein MOD, wobei ich annehme, das soll eine Ganzzahlige Division mit Rest sein, gibt es einfach nicht.


    Es gibt einen Software Interrupt (Eine Funktion, die schon im GBA BIOS implementiert ist, aber halt trotzdem Software) für Division, und Division mit Rest. Der Befehl um einen Software Interrupt auszulösen, ist swi <imm> und führt die durch <imm> indizierte Routine im BIOS aus. Die Division ist der swi 6 wobei in r0 der dividend ist, r1 der divisor, und nachdem die Kontrolle vom Interrupt Handler wieder an dich übergeben wird, hast du in r0 das Ergebnis r0 / r1, und in r1 das Ergebnis r0 % r1 - Ich weiß nicht wie genau die Konvention für die ISR ist, aber ich würde davon ausgehen, dass der Inhalt von r2 und r3 nach der Ausführung invalidiert wird, wenn du dort also wichtige Informationen hast, musst du die vorher sichern, entweder am Stack, oder in einem Callee Save Register (r4-r7, r8-r12)


    Hier kannst du mehr über die Software Interrupts lesen: https://problemkaputt.de/gbatek.htm#biosfunctions


    ~Sturmvogel


    E: Nachdem Assembly Code im Normalfall eine Qual zu lesen ist, und ich sowieso nicht genau weiß, was du tun willst, bzw. was deine Ausgangsbedinungen sind, spare ich mir mal ein Korrekturlesen, sorry :D - Ich schlage vor du probierst das einfach aus.


    Let the old ways live and prosper in the hearts of our young


  • Das kommt ein bisschen darauf an welchen Assembler du verwendest, aber i.d.r. ist XOR dann nur ein alias für EOR, so heißt der Befehl zumindest in auch in der offiziellen armref: http://infocenter.arm.com/help…oc.dui0068b/BABGIEBE.html


    Wie du einen Typen auslesen kannst... Ich weiß nicht ob es irgendetwas sinnvolleres gibt, als über das Spezies Array: Spezies des Pokémon lesen -> Im Array nachschauen, was die für Typen hat.


    ~Sturmvogel


    Let the old ways live and prosper in the hearts of our young


  • Kurze Frage, wie kann ich: "Startoffset + ( PokemonID-1) *28+6" so schreiben, dass ich den Byte an dem entstehenden Offset auslesen kann? (Wenn das Startoffset 0x082546C4 ist und die PokemonID [=Spezies] in r5 liegt?)

    Code
    1. ldr r6,.Startoffset
    2. sub r5,r5,#1
    3. mov r7,#28
    4. mul r5,r5,r7
    5. add r5,r5,r6
    6. add r5,r5,#6
    7. ldrb r1,r5
    8. bx lr

    So gehts es scheinbar nicht^^

  • Wäre halt immer interessant zu wissen, was denn nicht geht. Auf den ersten Blick verursacht der Codeblock mal einen Assemblererror, weil mul ein binärer opcode ist: mul rD, rS multipliziert rD mit rS und speichert das Ergebnis in rD. Es kann sein, dass dir dein Assembler das durchgehen lässt, und deine Instruktion als mul r5, r7 assembliert, würd mich aber net drauf verlassen und mir über etwaige Seiteneffekte bewusst sein. Außerdem gibt es die Syntax ldr(b/h) rD, rP nicht, sondern nur ldr(b/h) rD, [rP, #imm] um zu suggerieren, dass du rP als Pointer behandeln willst, und von der Addresse in rP + imm einen Wert lesen, bzw. sofern du , #imm nicht schreibst, wird angenommen, dass imm = 0. E.G.: ldrb r1, [r5]


    Ansonsten: Du kannst ja deinen Code einmal debuggen, z.B. mit dem vba-sdl-h, wenn du dir nicht ganz sicher bist, ob das was du tust semantisch richtig ist, das ist bei Assembler nicht immer so leicht nachzuvollziehen, gerade am Anfang wenn man sich noch nicht so ganz auskennt, wenn "S" die Spezies ist, die in r5 kommt (sofern das stimmt)


    Code
    1. ldr r6, .Startoffset @r6 := .Startoffset
    2. sub r5, r5, #1 @r5 := S-1
    3. mov r7, #28 @ r7 := 28
    4. mul r5, r7 @ r5 := (S-1)*28
    5. add r5, r5, r6 @ r5 := (S-1)*28 + .Startoffset
    6. add r5, r5, #6 @ r5 := (S-1)*28 + .Startoffset + 6
    7. ldrb r1, [r5] @ r1 := [1 Byte von] ((S-1) *28 + .Startoffset +6)

    Was zumindest in der Theorie stimmen sollte, wenn du den ersten Typen des Pokémon mit Spezies S lesen willst, und dein Startoffset auf die Pokémon Basisdaten zeigt.


    Ein Wort zum Codestil: Es ist etwas befremdlich, wenn du Labels mit einem Punkt beginnen lässt, aus dem einfachen Grund, dass ein Compiler das im Regelfall auch nicht tut. Die Assemblersyntax zwingt dich nicht dazu, das zu tun. Außerdem solltest du deinen Labels Aussagekräftige Namen geben, sonst weißt du in 2 Monaten nicht mehr, was "Startoffset" sein sollte, und es schickt sich, zur besseren Lesbarkeit, Wörter in einer (konsistenten) Form zu trennen, z.B. UpperCamelCase, lowerCamelCase oder delimiter_seperated, das erhöht die Lesbarkeit deines Codes. Das ist gerade bei Assembler enorm wichtig, weil das Zeug gerne mal ungeheuer große Ausmaße annimmt, wenn man viel in Assembler programmiert.


    Außerdem: Es ist cool, dass du versuchst Assembler zu lernen, und es wird dir vermutlich viel bringen, v.a. wenn du das Spiel analysieren willst, wirst du da nicht drum rum kommen, weiter so! Es sei nur gesagt, dass es auch andere Programmiersprachen gibt, z.B. C/C++, die ebenfalls in ein vom GBA verstandenes Assembler kompiliert werden können, und wesentlich leichter zu lesen/schreiben sind. Beispielsweise könntest du in C so beide Typen eines Pokémon lesen, und hättest sogar noch starke Typisierung:


    Code
    1. //gets the type of a species, gets the secondary type iff second is true
    2. enum PokemonType get_pokemon_type(u16 species, bool second) {
    3. if(second)
    4. return pokemon_base_stats[species].type[1]
    5. else
    6. return pokemon_base_stats[species].type[0]
    7. }

    Man muss natürlich dem Compiler sagen, was z.B. eine PokemonSpecies ist, oder wie die pokemon_base_stats aussehen, aber wenn das erledigt ist, ist der Code um einiges einfacher zu lesen, als Assemblercode.


    Ich habe mir noch die Mühe gemacht und das in Godbolt reingefriemelt, damit man sehen kann wie der C Compiler damit umgehen kann: https://godbolt.org/z/4vg768 - Wie man sehen kann, macht der Compiler Sachen, auf die man als Mensch gerade am Anfang nicht so schnell kommen würde, z.B. vermeidet er die mul Instruktion um Zeit zu sparen, er berechnet zuerst 7s und multipliziert das dann mit 4, um auf 28s zu kommen, dazu verwendet er binäre Shift Operationen. (Was am Anfang vllt nicht unbedingt ganz so trivial ist)


    ~Sturmvogel


    Let the old ways live and prosper in the hearts of our young


  • Ach ja, wenn durch Konstanten teilst (bzw Modul nimmst), kannst du das auch durch Multiplikation lösen (ist aufm GBA wesentlich performanter als ne Software Division zu machen, geschweige denn SWI Overhead).
    r0 durch eine Konstante 24 zu teilen, sieht dann etwa so aus:

    Willst du daraus ein Modulo bauen, kannst du noch folgendes anfügen:

    Code
    1. mul r1, r0, r3
    2. sub r1, r3, r1
    3. @ Modulo-Ergebnis befindet sich in r1

    Der "magische" Wert oben bei ldr r1, =XY berechnet sich für die Division durch C folgendermaßen:

    Wert = 0x100000000 / C + 1


    Im Endeffekt ists natürlich immer schöner Modulo/Division zu vermeiden, aber das ist dann ja ne andere Sache.

    Du möchtest mich mal treffen? Dann sag hallo und besuche mich an der FAU in Erlangen.