ASM Fragen

geschrieben am 05.08.2012 18:25:24
( Link )
Ich mache das ganze mal lieber hier anstatt im Chat, da mir noch ein paar mehr Fragen eingefallen sind

Also vorweg, da ich mich mal wieder mit ASM beschäftigen wollte, RPG-Hackers Tut aber schon gelesen (ich hoffe auch verstanden) habe, habe ich mich an das Tut. vom Talkhaus gesetzt. Hab es mir jetzt ins Deutsche übersetzt, aber bei meinem Englisch die hälfte der Erklärungen nicht verstanden und ansonsten auch noch 2, 3 Fragen mehr entdeckt, die das Tut. leider nicht beantwortet.

1. Vor dem Blockcode für BTSD steht immer
Code
 JMP MarioBelow : JMP MarioAbove : JMP MarioSide : JMP SpriteV : JMP SpriteH : JMP MarioCape : JMP MarioFireBall

soweit klar. Wenn man das ganze kürzer haben will nur das (Hauptsache oben stehen 7 Sachen)
Code
JMP MarioCode : JMP Return : JMP Return : JMP Return : JMP Return : JMP Return : JMP Return

Das wäre dann aber nur bei Marioberührungen und egal von wo möglich, oder?
Kann ein Block auch aktiviert werden, wenn Mario in ihm drinsteht und sonst nicht?

2.Es gibt nicht nur den Akkumulator (schreibt man den so?) A, sondern auch noch die Register X und Y.
Damit kann man Tabellen wie diese anlegen (Beispiel aus dem Tut.)
Code
SOUNDTABLE dcb $13,$14,$15,$16

LDX $19
LDA SOUNDTABLE,x
STA $1DF9
RTS

Wieso geht das aber nicht mit ganz vielen Wenn-Dann-Befehlen und dann mit LDA und nicht LDX? Wieso nicht auch LDY? Für was brauche ich die Register X und Y also genau wenn ich A nicht benutzen kann? Wann kann ich A nicht benutzen?

3.Wenn ich überprüfen will ob der Spiele eine bestimmte Taste drückt brauche ich "Binarybefehle". Wenn ich dann aber z.B.
Code
 01000001
00000001

So etwas habe, was mach ich dann damit? Wo packe ich es hin und wo finde ich auf der RAM Map die binären Adressen von Kontrollertasten, oder Stelle ich mir das falsch vor?

4.Was ist eigentlich noch PHA, PHX, PHY, PLA, PLX, PLY und was ist noch TAX, TAY, TXY, TYX, TYA, TXA?

Ich hoffe meine Fragen sind nicht nur Müll und bedanke mich schon mal dafür, dass ihr das hier überhaupt gelesen habt
geschrieben am 05.08.2012 18:52:33
( Link )
Zitat von kooooopa:
Ich mache das ganze mal lieber hier anstatt im Chat, da mir noch ein paar mehr Fragen eingefallen sind

Das Forum sollte sowieso erste Wahl sein.

Zitat von kooooopa:
Code
JMP MarioCode : JMP Return : JMP Return : JMP Return : JMP Return : JMP Return : JMP Return

Das wäre dann aber nur bei Marioberührungen und egal von wo möglich, oder?

Nee, der Code würde nur laufen, wenn man den Block von unten berührt.

Zitat von kooooopa:
Kann ein Block auch aktiviert werden, wenn Mario in ihm drinsteht und sonst nicht?

Ja, LM ab 1.70 (?) und BTSD ab 0.44 unterstützen drei neue Offsets:
Code
db $42
JMP MarioBelow : JMP MarioAbove : JMP MarioSide : JMP SpriteVert : JMP SpriteHorz : JMP MarioCape : JMP MarioFireball : JMP TopCorner : JMP HeadInside : JMP BodyInside

Mit denen kannst du zu Code springen, der ausgeführt werden soll, wenn man die Ecken berührt oder mit dem Kopf bzw. Körper drin ist. Vergiss das db $42 am Anfang nicht, sonst werden die nicht berücksichtigt.

Zitat von kooooopa:
Wieso geht das aber nicht mit ganz vielen Wenn-Dann-Befehlen und dann mit LDA und nicht LDX?

Was?

Zitat von kooooopa:
Wieso nicht auch LDY?

Geht auch.

Zitat von kooooopa:
Für was brauche ich die Register X und Y also genau wenn ich A nicht benutzen kann? Wann kann ich A nicht benutzen?

Wenn du indizieren willst. Falls es das Tutorial nicht gut genug erklärt hat, folgendes:
LDA $1337 lädt den Wert aus Adresse $1337, das ist klar. Wenn du aber LDA $1337,x schreibst, spielt das X-Register eine Rolle darin, aus welcher Adresse gelesen wird - der Wert in X wird zu der Zahl hinzugezählt, und aus dieser Adresse wird dann gelesen. Ist der Wert in X gleich 0, wird weiterhin aus Adresse $1337 gelesen ($1337+0). Ist der Wert in X 1, lesen wir aber aus Adresse $1338 ($1337+1). Ist X $10, lesen wir aus $1347, und so weiter. Dasselbe gilt für Y, nur schreibt man da eben LDA $1337,y.
Den Wert in A kann man nicht einfach so dranhängen, dafür gibt es keine Befehle. LDA $1337,a geht nicht.

Zitat von kooooopa:
Wenn ich dann aber z.B.
Code
 01000001
00000001

So etwas habe, was mach ich dann damit? Wo packe ich es hin

Was? Wo hast du das her? Kapier ich nicht.

Zitat von kooooopa:
und wo finde ich auf der RAM Map die binären Adressen von Kontrollertasten, oder Stelle ich mir das falsch vor?

In der Beschreibung steht's:
Code
$7E:0015	1 byte	I/O	Controller data 1. Format: byetUDLR.
b = A or B; y = X or Y; e = select; t = Start; U = up; D = down; L = left, R = right.

Die Buchstabenfolge byetUDLR stellt ein Byte dar (acht Bits). Jeder Buchstabe entspricht einem Button und damit einem Bit. Würde START gedrückt und sonst nichts, wäre der Wert in $15 00010000. Der Buchstabe t steht in der Beschreibung für den START-Knopf, und der steht in dem Byte an der vierten Stelle von Links, also ist das Bit an der viersten Stelle von links gesetzt.
Die Buchstabenvergabe ist übrigens völlig egal, das dient nur der Verdeutlichung.

Zitat von kooooopa:
4.Was ist eigentlich noch PHA, PHX, PHY, PLA, PLX, PLY und was ist noch TAX, TAY, TXY, TYX, TYA, TXA?

Letztere sind einfacher zu erklären: Übertragung des Wertes von einem Register ins andere. TAX kopiert den Wert von A nach X, TAY kopiert von A nach Y und so weiter.

Was erstere angeht, da musst du über den Stack Bescheid wissen. Das ist ein Bereich im RAM, in dem Werte zwischengespeichert werden. Stell ihn dir als Bücherstapel vor: Wenn du etwas auf den Stack legst, platzierst du den neuen Wert immer an oberster Stelle, und wenn du einen Wert herausholst, nimmst du immer nur den, der ganz oben liegt.
PHA ("Push A") speichert einen Wert auf den Stack, PLA ("Pull A") holt ihn wieder zurück. Beispiel:
Code
LDA #$02 ; A ist 02
PHA ; Wert von A auf den Stack speichern
LDA #$FF ; A ist FF
PLA ; Wert vom Stack zurückholen und in A speichern, A ist wieder 02
RTS


Natürlich können auch mehrere Werte zur selben Zeit auf dem Stack abgelegt sein:
Code
LDA #$02 ; A ist 02
PHA ; Wert von A auf den Stack speichern
LDA #$03 ; A ist 03
PHA ; Wert von A auf den Stack speichern
LDA #$04 ; A ist 04
PLA ; Obersten Wert vom Stack in A speichern, A ist 03
PLA ; Obersten Wert vom Stack in A speichern, A ist 02
RTS


Nett ist dabei auch, dass du von einem Register ins andere pushen und pullen kannst:

Code
LDA #$05 ; A ist 05
LDX #$09 ; X ist 09
PHA ; Wert von A auf den Stack speichern
PLX ; Wert vom Stack zurückholen und in X speichern, X ist 05
RTS

(das würde in diesem Fall mit TAX einfacher gehen.)

Achte dabei immer darauf, dass der Stack ausgeglichen ist. Am Ende musst du genauso oft "gepusht" haben wie "gepullt":

Code
LDA #$02
PHA
PHA
PLA
RTS ; das geht nicht


Hoffe, das hilft dir weiter.
geschrieben am 05.08.2012 19:09:02
( Link )
Von wissen erschlagen sein

Ok, danke für das mit den Blöcken, das ist glaube ich das einzige was ich relativ gut verstehe XD

Zitat
Was?

Beispiel aus dem Tut:
Say we wanted a block that when hit, would make a sound effect depending on Mario's powerup. Just for example, i'm going to use the shell sound effects that get higher as the shell hits more enemies.
Code
SOUNDTABLE dcb $13,$14,$15,$16

LDX $19
LDA SOUNDTABLE,x
STA $1DF9
RTS

Geht das nicht auch in dem wir das Powerup laden, schauen ob MArio einen Pilz hat, wenn ja zu Code Pilz springen und den Soundeffekt abspielen, wenn nein schauen ob Mario eine Blume hat, wenn ja....
Dann bräuchte ich doch garkeine Tabelle.

Wenn LDY auch geht, gibt es dann einen Unterschied zwischen LDX und LDY?


Zu dem mit den Binarys hier der Tut Teil:
Today, we are going to learn Binary commands. Now, say we wanted to check if a certain button is pressed. Now, with your current knowledge, you would probably do something like:
Code
LDA $15
CMP #$01
BEQ MYCODE
RTS

MYCODE
(blah)
RTS

Now, this would not work, as this checks if ONLY down is pressed, so if they were holding any other buttons, our code would not work. Instead, we do it a different way. In binary, there are 8 figures, which can be either 1 or 0. SMW uses these as bits, which are either set or not (1 or 0 respectively). The binary > SMW Bits goes like this:
Code
Bit number: 7 6 5 4 3 2 1 0
Binarycode: 1 0 0 0 1 0 1 0

So from this random piece of binary, bits 7, 3 and 1 are set. The rest are not.

Now, instead of using CMP to check if that is set, we use AND. AND works like this.
Code
LDA $15
AND #$01
BNE MYCODE
RTS

MYCODE
(blah)
RTS

Now, say the controller was pressing Y and Down. This would make A amount to 41 (I believe, but the Ram map is unavailable, making this rather difficult). This would set bits 6 and 0. Then, we AND #$01, which is a value that has bit 0 set. AND then works like this:
Code
01000001
00000001
So we get:
00000001


Was bringt mir das nun? Wo pack ich das hin?


Zitat
In der Beschreibung steht's:
CODE: ALLES AUSWÄHLEN
$7E:0015 1 byte I/O Controller data 1. Format: byetUDLR.
b = A or B; y = X or Y; e = select; t = Start; U = up; D = down; L = left, R = right.

Die Buchstabenfolge byetUDLR stellt ein Byte dar (acht Bits). Jeder Buchstabe entspricht einem Button und damit einem Bit. Würde START gedrückt und sonst nichts, wäre der Wert in $15 00010000. Der Buchstabe t steht in der Beschreibung für den START-Knopf, und der steht in dem Byte an der vierten Stelle von Links, also ist das Bit an der viersten Stelle von links gesetzt.
Die Buchstabenvergabe ist übrigens völlig egal, das dient nur der Verdeutlichung.


Sorry, kein Wort verstanden, nur das man es irgendwie aus diesem Code rauslesen kann

Ok, warum man TAX, etc. brauchen könnte leuchtet mir ein, aber wieso brauche ich PHA, etc. gibt es da irgendeine praktische Anwendung?
geschrieben am 05.08.2012 19:54:20
( Link )
Zitat von kooooopa:
Geht das nicht auch in dem wir das Powerup laden, schauen ob MArio einen Pilz hat, wenn ja zu Code Pilz springen und den Soundeffekt abspielen, wenn nein schauen ob Mario eine Blume hat, wenn ja....
Dann bräuchte ich doch garkeine Tabelle.

Ja, das stimmt. Trotzdem sind Tabellen vorzuziehen - sie sparen Platz, sind effizienter und machen den Code weitaus lesbarer. Was ist dir lieber, tausend Wenn-Dann-Befehle oder eine einzige Tabelle mit tausend Werten?

Zitat von kooooopa:
Wenn LDY auch geht, gibt es dann einen Unterschied zwischen LDX und LDY?

Keinen großen, und keinen, um den du dir jetzt Gedanken machen musst. Es gibt nur einige Befehle, die nicht mit Y funktionieren - zum Beispiel würde LDA $000000,x funktionieren, LDA $000000,y gibt es aber nicht.

Zitat von kooooopa:
...

Okay, das mit dem Binärzahlen erkläre ich dir lieber ganz von vorne.

Du weißt, dass ein Byte aus 8 Bits besteht, oder? In Hexadezimal kann man zum Beispiel E6 sagen, das ist eine Zahl, die ein Byte lang ist. Sie besteht aus 8 Bits, und ein Bit ist eine Ziffer, die entweder 0 oder 1 sein kann. Wenn man die Bits einer Zahl ausschreibt, dann nennt man das Binärschreibweise. Die Zahl E6 wäre im Binärsystem 11100110. Dieselbe Zahl, nur verschiedene Schreibweisen. Du kannst sie auch im dir vertrauten Zehnersystem aufschreiben, das wäre 230, aber das tut hier nichts zur Sache.

Auch wenn man in ASM programmiert, sind die Werte, mit denen du arbeitest, meistens 1 Byte (8 bit) lang. Du kannst zum Beispiel schreiben:
Code
LDA #$E6

, das lädt den Wert E6 in A. Du kannst aber auch die Binärschreibweise verwenden:
Code
LDA #%11100110

Das ist exakt dasselbe! Wenn du diese beiden Codes in die ROM einfügst, sind sie genau gleich. Die verschiedenen Schreibweisen sind nur für uns Menschen da, der Maschine ist das egal.
Hexadezimale Zahlen werden durch ein Dollarzeichen ($) angezeigt, Binärzahlen durch ein Prozentzeichen (%). Und merk dir: Welche Schreibweise du wählst, ist egal.( Normalerweise nimmt man aber die Hexadezimalschreibweise, die ist kürzer.)

Stell dir vor, der Wert in Adresse $15 wäre #$34 - das ist dasselbe wie 00110100.

Soviel dazu, jetzt zu den Controller-Adressen. $15 ist eine Adresse, deren Wert davon abhängt, welche Knöpfe man gerade drückt. Drückt man zum Beispiel den START-Knopf und sonst nichts, ist der Wert in Adresse $15 00010000, was dasselbe ist wie #$10.
Und woher weiß man jetzt, welcher Wert für welche Knöpfe steht? Vielleicht hilft dir das hier mehr weiter als die RAM-Map:

00000000 <-- dieses Bit ist 1, wenn A oder B gedrückt werden, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn X oder Y gedrückt werden, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn SELECT gedrückt wird, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn START gedrückt wird, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn der Pfeil nach oben gedrückt wird, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn der Pfeil nach unten gedrückt wird, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn der Pfeil nach links gedrückt wird, und 0, wenn nicht
00000000 <-- dieses Bit ist 1, wenn der Pfeil nach rechts gedrückt wird, und 0, wenn nicht

Verstehst du jetzt, warum der Wert 00010000 ist, wenn START gedrückt wird?

Natürlich kann man auch mehrere Knöpfe gleichzeitig drücken, dann sind eben mehrere Bits 1. Wenn man zum Beispiel gleichzeitig SELECT, nach oben und nach links drückt, ist der Wert in Adresse $15 00101010. Und das ist dasselbe wie #$2A.

Wenn du jetzt überprüfen willst, ob man gleichzeitig SELECT, nach oben und nach links drückt, könntest du folgendes machen:
Code
LDA $15
CMP #$2A
BEQ KnoepfeGedrueckt
[...]
KnoepfeGedrueckt:
[...]


Oder, wenn dir die Binärschreibweise lieber ist:

Code
LDA $15
CMP #%00101010
BEQ KnoepfeGedrueckt
[...]
KnoepfeGedrueckt:
[...]


Wenn man SELECT alleine drückt, ist der Wert in $15 00100000, was dasselbe ist wie #$20. Wenn du überprüfen willst, ob man SELECT drückt, könntest du folgendes tun:

Code
LDA $15
CMP #$20
BEQ KnopfGedrueckt
[...]
KnopfGedrueckt:
[...]


Das würde auch oft funktionieren. Wenn man allerdings zur selben Zeit SELECT und einen anderen Knopf drückt (sagen wir mal SELECT und nach oben), dann würde das im obigen Beispiel nicht als "Knopf gedrückt" zählen. Der Grund: Wenn man SELECT und nach oben drückt, ist der Wert in $15 00101000, und das ist #$28. Der Code würde nicht zu "KnopfGedrueckt" springen, denn du überprüfst ja auf den Wert #$20, nicht #$28.

In den meisten Fällen nimmt man für Knopf-Abfragen AND statt CMP und vertauscht BEQ und BNE. Ein besserer Code würde also so aussehen:

Code
LDA $15
AND #$20
BNE KnopfGedrueckt
[...]
KnopfGedrueckt:
[...]


Dieser Code würde zu "KnopfGedrueckt" springen, wenn man SELECT drückt - egal, welche Knöpfe man sonst noch drückt.

Warum das funktioniert und was AND ist... das würde hier zu lange dauern. Ich hoffe, du hast es jetzt etwas besser verstanden.

Zitat von kooooopa:
Ok, warum man TAX, etc. brauchen könnte leuchtet mir ein, aber wieso brauche ich PHA, etc. gibt es da irgendeine praktische Anwendung?

Natürlich, jede Menge sogar. Wenn du zum Beispiel Code von SMW veränderst, solltest du die Register vorher auf dem Stack speichern und am Ende wieder zurückholen, denn SMW verlässt sich darauf, dass in den Registern bestimmte Werte sind, mit denen es später weiterarbeiten will. Es gibt noch eine Menge mehr Anwendungsmöglichkeiten, aber mir will sonst gerade keine einfallen.
geschrieben am 05.08.2012 20:08:40
( Link )
Ok verstanden, ein richtig fettes [size=1000]DANKE![/size]für dich.
geschrieben am 05.08.2012 20:10:53
( Link )
Eines will ich noch anmerken:

Code
SOUNDTABLE dcb $13,$14,$15,$16

LDX $19
LDA SOUNDTABLE,x
STA $1DF9
RTS


Das ist für Blöcke eine schlechte Idee. Kein Block Tool, das ich kenne, könnte diesen Code so ohne weiteres ausführen, weil die Tabelle oben steht und somit als Code interpretiert wird und über dem LDX $19 nichtmal ein Label steht, zu dem gesprungen werden kann. Besser wäre etwas in dieser Form:

Code
[...]
MarioTouch:
LDX $19
LDA SOUNDTABLE,x
STA $1DF9

Return:
RTS

SOUNDTABLE:
db $13,$14,$15,$16


Tabellen sollten am besten immer unter dem Code stehen, in dem sie verwendet werden. Besonders in Blocks und Sprites.
-Das quadratische Rad neu erfinden-
Mit das quadratische Rad neu erfinden (englisch Reinventing the square wheel) bezeichnet man die Bereitstellung einer schlechten Lösung, wenn eine gute Lösung bereits existiert.

-Slowsort-
Slowsort (von engl. slow: langsam) ist ein langsamer, rekursiver Sortieralgorithmus, der nach dem Prinzip Vervielfache und kapituliere (engl. Multiply and surrender, eine Parodie auf Teile und herrsche) arbeitet.

geschrieben am 06.08.2012 7:49:07
( Link )
Macht es den einen Unterschied, ob vor der Tabelle ein dcb steht, oder ein dbb?
geschrieben am 06.08.2012 10:43:49
( Link )
Ups. Das sollte bei mir eigentlich "db" heißen. Zwischen dcb und db gibt es - soweit ich weiß - keinen Unterschied, aber einige Assembler unterstützen nur dcb und einige nur db. xkas unterstützt, meine ich, nur db.
-Das quadratische Rad neu erfinden-
Mit das quadratische Rad neu erfinden (englisch Reinventing the square wheel) bezeichnet man die Bereitstellung einer schlechten Lösung, wenn eine gute Lösung bereits existiert.

-Slowsort-
Slowsort (von engl. slow: langsam) ist ein langsamer, rekursiver Sortieralgorithmus, der nach dem Prinzip Vervielfache und kapituliere (engl. Multiply and surrender, eine Parodie auf Teile und herrsche) arbeitet.