Beiträge von Tarek701

geschrieben am 27.08.2011 12:48:00 in Super Mario 64 Hacking
( Link )
Ich habe damals als ich jünger war(mit 9 oder 10 Jahren) habe ich zunächst mal mich mit der Nintendo 64 Konsole befasst. Als ich damals ein Thema gefunden habe, in den acmln board's.
VL-Tone hat mit seinen Leuten(HyperHacker usw.) einen Super Mario 64 Level Editor programmiert. Er bezeichnet ihn als: "Toads Tool 64"
Der Link:
http://homepage.mac.com/qubedstudios/ToadsTool64.htm

frauber(a.k.a Messiaen) ist ein genialer ASM Hacker. Das Nintendo 64 hat natürlich den MIPS 4300i prozessor. Dessen Assembler ist wesentlich schwerer wie dieser des SNES.
Vielleict kenn der eine oder andere Super Mario 64 und hackt es sogar.
Hier ein Video von frauber:


Ich finde das genial.

Übrigens, wenn einer sich für den Assembler von Nintendo 64 wirklich interessiert. Es gibt verschiedene Tutorials im Internet(leider aber nur in Englisch,außer eins!):
http://www.weblearn.hs-bremen.de/risse/RST/docs/MIPS/mips-isa.pdf

Nur ein's ist wichtig. Im SNES Assembler gibt es da ein paar entspannungsmöglichkeiten(wie die tabelle $db usw.) diese gibt es im N64 ASM nicht. Außerdem kann man z.B Animationen nicht mehr wirklich machen, da man jetzt mit XYZ Koordinaten arbeitet. Was das ganze erschwert.

BTW, hier ist das Offizielle Forum(SM64 Hacking):
http://jul.rustedlogic.net/forum.php?id=41
Einige Sachen sind hier wirklich schon out of date, denn VL-Tone ist immernoch in einer Art Deadline.
geschrieben am 31.08.2011 15:41:07 in Super Mario 64 Hacking
( Link )
Ich glaub das nicht O_O
Dieses Forum braucht unbedingt mehr Mitglieder.
geschrieben am 31.08.2011 17:09:46 in Super Mario 64 Hacking
( Link )
Zitat von Urtegurke:
So ists ja nicht. Es ist einfach dass hier mehr SMW-Hacker sind als sonst welche. SM64-Hacken soll auch sehr zeitaufwendig sein, was ich persönlich zum Beispiel nicht machen kann, weil ich eben die Zeit nicht habe. Ich würde gern mal was probieren, aber auch nichts anderes vernachlässigen.


Nun man muss ja nicht direkt mit ASM anfangen. Schließlich hat frauber rausgefunden wie man einen C code schreibt und ihn dann in SM64 implentiert.
geschrieben am 03.09.2011 11:41:37 in Super Mario 64 Hacking
( Link )
Zitat von Ricicir:

Das ist ein guter 64 Hack

Korrekt. Derzeit ist ASM, C nicht angesagt. Hauptsächlich editieren die nur etwas im Hex Editor. Kaum einer von denen weiß, was frauber herausgefunden hat.
geschrieben am 13.07.2014 12:59:09 in SM64 Hacking - MIPS R4300i Assembly Tutorial
( Link )
Vielleicht werden einige, die bereits irgendwie sich im Bereich Informatik ausbilden eventuell auf den Prozessor stoßen(Kp, habe von einigen gehört, dass sie sich mit diesen beschäftigen mussten) weil die meisten scheinen sich mit diesem zu beschäftigen, weil er doch recht simpel ist und das Verstehen der Pipelines auch relativ einfach ist. Dadurch das es keine Pipeline-Sperren gibt, ergeben sich daraus mehrere Vorteile.

Was man halt immer bei MIPS wissen muss ist, dass es keine direkte Addressierungsmöglichkeit gibt wie beim 65C816:

Code

LDA $19 ; Lädt Wert von Adresse 7E:0019. Allerdings gilt das nicht, wenn die Bank 7F ist. Dann würde der Wert von 7F:0019 in Akkumulator geladen werden. -> Direct Addressing.
LDA $0019 ; Lädt Wert von Adresse 7E:0019. Bei Bank gilt das gleiche. -> Absolute Addressing.
LDA $7E0019 ; Lädt Wert von Adresse 7E:0019 in Akkumulator. -> Long Addressing.


Leider ist es nicht so beim MIPS. Bei MIPS muss man das in zwei Prozessen durchlaufen, also sogenanntes "Base Addressing", bei dem das Register als eine Art Pointer zum Operanden agiert:

Code

LUI T0, $809C ; Oberer Adressenteil. T0 ist unser Register und T0 := $809C0000.
LW T1, $5C9D(T0) ; Lade Wert von $809C5C9D in T1. T0 wurde nicht verändert und ist immernoch T0 := $809C0000.


Gleiches geht auch mit SW (Store Word)
Code

LUI T0, $809C ; Lade unseren oberen Adressenteil in T0.
ADDIU T1, T1, $001C ; Unseren Wert in T1 laden bzw. addieren. T1 := $0000001C. ADDIU = ADD Immediate Unsigned
SW T1, $5C9D(T0) ; Speichert Wert von T1 an Adresse $809C5C9D. T0 bleibt immernoch $809C0000.


LUI = Load Upper Immediate
LW = Load Word

Und nun zum Tutorial:
http://dl.smwcentral.net/6947/mips-tut.zip

Wird natürlich noch erweitert. Ich habe das ganze auch mit ein paar Farben ausgeschmückt:
Befehl wie LW, LUI, ADDIU, etc. ist orange. Register sind rot und Werte wie $809C oder $5C9D sind blau.

MfG,
Tarek701.
geschrieben am 07.10.2014 16:15:49 in SM64 Hacking - CajeASM v7.0 (Stable)
zuletzt bearbeitet von Tarek701 am 18.04.2015 16:44:16.
( Link )
CajeASM - A MIPS R4300i Assembler
CajeASM ist ein N64 MIPS Assembler, der für alle N64 ROMs funktioniert (big-endian, little-endian und byteswapped/middle-endian). Der Assembler kommt mit einer Frontend und ist daher sehr einfach und leicht zu gebrauchen, einfach nur ROM und ASM datei auswählen und assemblieren lassen.

Download Link:
http://origami64.net/attachment.php?aid=63

Hier wird gezeigt (für die, die es wirklich nicht verstehen) wie man eine ASM Datei auf die ROM assembled.

Tutorials:
https://www.youtube.com/watch?v=DnFxpRwT0Gc - How to assemble an ASM file to a SM64 ROM.

Wenn du MIPS ASM beherrschst, kannst du richtig cooles Zeug machen.

Beispiele:

FLUDD by Kazeshin(aka Kaze):

(Full Video: FLUDD)

Shop Guy by Tarek701/Cajetan (aka me):

(Full Video + Download Link: Shop Guy v0.2)

More Objects Patch by Kazeshin(aka Kaze):

(Full Video + Download Link: More Objects Patch

MfG,
Tarek701.
geschrieben am 10.10.2014 12:04:35 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
Zitat von RPG Hacker:
Ich verstehe zwar nicht so viel von N64 Assembler, aber du hast dir auf jeden Fall schonmal meinen Respekt verdient, dass du es überhaupt so weit gebracht und eine erste Version veröffentlicht hast!
Ich hoffe mal, es finden sich Leute, die das Ding nutzen können und davon profitieren.


Danke. Also an sich ist es nicht schwer, Adressierungen sind nur halt anders und man hat sowas wie direct addressing, absolute oder long addressing nicht. Stattdessen ist das dann Register+Base Pointern.

Wenn ich also 'ne Adresse laden will muss ich einfach schreiben:
LUI T1, $8033 ; Oberer Teil der Adresse T1 := $80330000.[/color]
ORI T1, R0, $3D54 ; Unterer Teil der Adresse, T1 := $80333D54

LUI heißt einfach: "Load upper immediate", und "upper" bedeutet ja auch, dass hier die obere Adressenhälfte (die ersten vier bytes von links) angesprochen wird.
ORI ist, wie man erahnen kann, einfach nur OR. Man benutzt es aber hier, da es die bytes nach hinten (also die letzten vier bytes) shiftet und dann kriegt man auch die Adresse raus.

T1 ist ein Register bzw. das "temporary register 1" das für temporäre Werte verwendet wird. R0 ist auch ein Register, ist aber immer null. Man verwendet es sicherheitshalber um Fehler zu vermeiden, kann nämlich passieren, das wenn man gleiche Register ORI'd, dass dann irgendein Fehler entsteht und der Wert nicht mehr stimmt. Deshalb benutzt man immer R0.

Will ich jetzt einen Wert von einer Adresse, mache ich das gleiche mit LUI nochmal, nur das ich danach nicht ORI benutze sondern den Befehl LW, LB oder LH. (LW = Load Word (ladet 'nen 32-bit wert), LB = Load Byte (ladet 'nen 8-bit wert) oder LH = Load Halfword (Ladet 'nen 16-bit wert))

LUI T1, $8033 ; Obere Adressenhälfte. T1 := $80330000
LB T2, $3D54(T1) ; Lade Wert von Adresse $80333D54 in T2. T1 bleibt UNVERÄNDERT -> T1 ist immernoch: $80330000.

In dem Falle wird jetzt ein Byte-Wert von $80333D54 in T2 geladen. Wenn also bei $80333D54 steht: 0D 02 00 A2 D1 [...] wird nur das "0D" genommen und lädt es in T2. -> T2 := $0000000D. Bei Halfword hätte er jetzt $0D02 und bei 'nem Word hätte er $0D0200A2 geladen. Ich glaube aber, dass hier irgendwas noch mit Big-Endian und Little-Endian war, so das das bei manchen ROMs auch umgekehrt geshiftet wird. Kann also auch sein, dass $0D0200A2 vertauscht sind und man dann $A200020D hat. Mario 64 ist auf jedenfall Little-Endian.

Kannst auch einen Wert speichern, dafür gibts dann SW, SH, SB. Also Store word, Store Halfword oder Store Byte. Prozess ist der Gleiche. SB hat dabei 'nen Register wo der Wert reingeladen wird und die Adresse verknüpft mit dem Register, dass die obere Adressenhälfte in sich trägt:

LUI T0, $8033 ; Obere Adressenhälfte. T0 := $80330000
ORI T1, R0, $000A ; Unser Byte: $0A -> Wert.
SB T1, $3D54(T0) ; Speichert den Wert (LSB -> Least Significant Byte, $0A) aus T1 an Adresse $80333D54. T0 bleibt wieder unverändert und ist immernoch $80330000

Hier wird das Register "T1" als der zu speichernde Wert verwendet und wieder Pointer+Base Syntax zur Adresse und anschließend wird das Byte dort gespeichert. Nach diesem Code würde statt 0D 02 00 A2 D1 jetzt 0A 02 00 A2 D1 stehen.
geschrieben am 10.10.2014 16:07:48 in Super Mario 64 Hacking
( Link )
Zitat von Brobju:
Holla!
Zwei Fragen: - Kann jemand von euch Custom Music in SM64 einfügen?
- Ein wenig Off-Topic, aber hat jemand von euch den Download Link für die originalen Paper Mario 64 Texturen (brauch sie für meinen
Hack^^


Antwort kommt vielleicht jetzt total spät, aber was solls.
Mit Skelux's Level Importer kann man auch custom Musik einfügen und auch die Instrumente ändern. Zunächst müsst ihr eure MIDI-Sequenz in .XML umwandeln (ich benutze MuseScore) und dann mit dem Importer bei "Music Tab" auf "create sequence" klicken. Dort ladet ihr dann eure XML datei und anschließend werden die Channels angezeigt, mit den jeweiligen Instrumenten. Wenn ihr fertig seid, müsst ihr auf "Create m64 sequence" drücken. Diese .m64 Datei müsst ihr dann importieren. Problem ist halt nur, dass ihr bei den Instrumenten immer rumprobieren müsst (oder die Instrumente kennen müsst) bis die Musik harmoniert und sich perfekt anhört. Manchmal gehts auch einfach so nicht, weil die MIDI-Sequenz vielleicht zu komplex ist, zu viele Channels hat, etc.

Btw, auf SMWCentral gibt es inzwischen ein "SM64 Hacking" Unterforum; Jul.Rustedlogics.net ist ziemlich outdated und dort herrscht tote Hose.
geschrieben am 11.10.2014 15:05:31 in SM64 Hacking - Wie in C Coden?
( Link )
Jo, habe vor einer Weile das herausgefunden, dass man in SM64 in C coden kann und dann entsprechende Hacks dafür erstellen kann. Zunächst einmal müsste ich dann aber klären was "Behaviors" und "Behavior Scripts" sind. Also, im game hat jedes Objekt wie z.B der pinke Bob-Omb in Bob-Omb Battlefield eine Model ID (also der "pinke Bob-Omb" an sich) und ein Behavior(Behavior = Verhalten -> Was er machen soll). In dem Behavior werden Animation, Aktionen und Kollision geregelt. Nun hatten aber die Entwickler von SM64 höchstwahrscheinlich einen Skript Interpreter geschrieben, mit dem sie schnell und einfach Behaviors laden konnten. Das sind "Behavior Scripts". Allerdings haben wir natürlich nicht mehr diese in "Sprachform" sondern in Hex-Format. Dennoch wird man merken, dass die Hexformate in der Behaviorliste allgemein gültig sind. Ich hole jetzt mal den Pink-Bob Omb als Beispiel aus der Liste von VL-Tone:

21CFDC/0031DC 00 04 00 00 <- 0x00 markiert meist Start. Bei "Solid Objects" muss 00 04 auf 00 09 gesetzt werden.
21CFE0/0031E0 11 01 24 49 <- Ist ein OR command.
21CFE4/0031E4 27 26 00 00 08 02 39 6C <- Ist die Animation, Animation ist auf adresse 0x0802396C
21CFEC/0031EC 2F 00 00 00 00 80 00 00 <- Interaction, in dem Falle: Solid.
21CFF4/0031F4 1E 00 00 00 <- "DROP TO GROUND" setzt das Objekt genau auf den Boden.
21CFF8/0031F8 23 00 00 00 00 64 00 3C <- Das ist eine sogenannte "Collision Sphere" heißt also, ne kugelartige Collision. X = 64 und Y = 3C.
21D000/003200 28 00 00 00 <- Animiert Objekt (muss mit 0x27 zusammen gemacht werden)
21D004/003204 10 1D 00 00 <- Ein parameter, in dem Falle für das Behavior.
21D008/003208 2D 00 00 00 <- Deklariert Ursprungsposition.
21D00C/00320C 0C 00 00 00 80 2E 76 AC <- Ist eine Funktion, die sich auf 0x802E76AC (RAM offset) befindet. Hier werden die Aktionen vom Pink Bob-Omb geladen. Also das eigentliche "Behavior"
21D014/003214 08 00 00 00 <- 0x08 bedeutet immer: Anfang eines Loopes. Alles was nun folgt wird "IMMER" ausgeführt.
21D018/003218 10 05 00 00 <- Ausnahmefall -> Für 0x2F command.
21D01C/00321C 0C 00 00 00 80 2E 7C 4C <- Nochmal eine Funktion.
21D024/003224 09 00 00 00 <- Loop Ende.

Man sieht also, das sind Behavior scripts. Die Syntax zu verstehen ist recht einfach. Wir basteln mal schnell ein eigenes Behavior, wir machen es auch nicht überaus kompliziert wie oben. Ich benutze gerne für NPCs, die ich in Zukunft machen will immer unused Behaviors, also Behaviors, die im Game nie benutzt wurden sind und bei Verwendung auch so gut wie keine Funktion bieten können.

Dieses Hier:
ROM Addr: 0021ACD0 Hex Behav: 13000ED0
>>>>>>>>>>Unused Behavior?
21ACD0/000ED0 00 04 00 00
21ACD4/000ED4 11 01 00 01
21ACD8/000ED8 30 00 00 00 00 1E FE 70 FF CE 03 E8 03 E8 00 C8 00 00 00 00
21ACEC/000EEC 10 05 00 00
21ACF0/000EF0 23 00 00 00 00 28 00 28
21ACF8/000EF8 08 00 00 00
21ACFC/000EFC 0C 00 00 00 80 2A E8 5C
21AD04/000F04 09 00 00 00

Wir wollen, dass unser NPC eine Collision Sphere besitzt und die Animation von Fly Guy hat (was uns später zwingt, dass Objekt mit Model ID = "Fly Guy" und diesem Behavior zu setzen. Bei anderen Objekten sieht das dann etwas "komisch" aus). Dafür ersetzen wir einfach das riesige 0x30 command mit einem 0x27, 0x28 und 0x2F command:

21ACD0/000ED0 00 04 00 00
21ACD4/000ED4 11 01 00 01
21ACD8/000ED8 27 26 00 00 08 01 1A 64
21ACE0/000EC0 28 00 00 00
21ACE4/000EC4 2F 00 00 00 00 80 00 00
21ACEC/000EEC 10 05 00 00
21ACF0/000EF0 23 00 00 00 00 28 00 28
21ACF8/000EF8 08 00 00 00
21ACFC/000EFC 0C 00 00 00 80 2A E8 5C
21AD04/000F04 09 00 00 00

Nochmal zur Erklärung:
0x27 Command -> Animation.
Syntax:

[27] [26] [00 00] [08] [01 1A 64]

0x27: Animation command
[1] - Befehl
[2]*4+88:= Adresse
object->address:=
(special case: 26 for animation)
[3-4] Ignoriert (Daher immer 00)
[5] Bank Nummer
[6-8] Adresse der Animation in der Bank.

[2F] [00 00 00] [00 80 00 00]

0x2F: Interaction Command (Gibt auch noch kürzeren Command, der genau das Gleiche macht)
[1] - Befehl
[2-4] - Ignoriert
[5-8] - Interaction (in unserem Falle 80 00 00 für "Solid")

[23] [00 00 00] [00 28] [00 28]

0x23: Collision Sphere (Bildet um das Objekt eine "kugelförmige", sphärenartige Kollision)
[1] - Befehl (0x23)
[2-4] - Ignoriert
[5-6] - X und Z Sphäre.
[7-8] - Y Sphäre.

So, unser Behavior ist jetzt Solid und haben diesem jetzt eine Kollision gegeben. Später wenn wir unsere eigene Funktion geschrieben haben, müssen wir auch das 0x0C command ändern.

Lektion 1 : Wie code ich in C für SM64 Hacking?
Zunächst einmal brauchen wir die entsprechenden Compiler. Ich nehme immer MinGW. Also downloadet euch zuerst MinGW und installiert alles was für C nötig ist. Es gibt einen package manager bei dem ihr auswählen könnt, was installiert werden soll. Dort solltet ihr für C alles auswählen. Wenn ihr euch nicht sicher seid, installiert einfach gleich das ganze Ding.
Als Nächstes müsst ihr euch MSYS 1.0.10 runterladen (nicht MSYS 1.0.11, die funktioniert nämlich nicht richtig!) und installieren. Wir brauchen es später um in den Ordner zu springen und unsere Files zu "maken".

Jetzt fehlt noch das sogenannte "N64Toolchain", welches ihr hier runterladen könnt:
https://gzrt.googlecode.com/files/n64tree-win32-v1.0.7z

Darin befinden sich die BINs zum kompilieren in MIPS Binary, etc.
Macht euch einen schönen Ordner, den ihr, wie auch immer, nach eurem Belieben benennt und extrahiert dort euer Toolchain rein.

Nun schaut in den Ordner "include", dort sehen wir die n64.h die die Datentypen wie u8, u16, u32, usw. definiert. Und wir sehen auch, dass die einzelnen Knöpfe des controllers auch Begriffe haben. Aber wir haben immer noch keine SM64 Header, die Funktionen des Spiels enthält.

Ich habe mir die Header schon viel weiter aufgebaut, deshalb poste ich sie hier. Geht hin und erstellt in include eine "explode.h" Datei und fügt folgendes ein:

Spoiler anzeigen
/* Mario 64 header file rev1 by messiaen.
**
** The information contained in this file is a compilation from many sources,
** especially Cellar Dweller's, Nagra's, Yoshielectron's notes along with
** original research by me. This is to be used with a C compiler targetting MIPS.
**
** You can a find tutorial on setting up a N64 MIPS-GCC Toolchain on Windows
** at http://code.google.com/p/gzrt/wiki/Nint ... chainSetup (ZZT32 we lurve you)
**
** If you are using an *nix environment, check this Wiki for info about the
** appropriate MIPS-R4300i binutils:
**
** http://en.wikibooks.org/wiki/N64_Programming
** 0C 10 14 00 81 CE 0E 94 24 01 00 03 10 00 00 40 00 00 00 00
829D8
** You can contact me at the Jul SM64 Hacking forum @ http://jul.rustedlogic.net/
*/

//#include "math.h"

/* Constants - struct pointers */
#define M64_CURR_OBJ_PTR 0x80361160 /* Pointer to object being currectly processed */
#define M64_FIRST_OBJ_STRUCT 0x8033D488 /* Pointer to the first object (out of 240) in the circular linked list */
#define M64_MARIO_STRUCT 0x8033B170 /* you can read it from 0x8032d93c */
#define M64_MARIO_OBJ_PTR 0x80361158 /* Pointer to Mario OBJ struct in RAM */
#define M64_LEVEL_STRUCT 0x8033B90c
#define M64_OS_MSG_QUEUE 0x8033AF78 /* OS Message Queue pointer */
#define M64_4X4_MATRIX_PTR 0x80220CC0 /* Pointer to 4x4 matrix resulting from calculation */

/* Constants - misc pointers */
#define M64_DISPLAY_STATS_FLAGS 0x8032b26a
#define M64_SEGMENT_TABLE 0x8033b400
#define M64_CURRENT_LEVEL_ID 0x8032ddf8 /* u16 */
#define M64_GEO_LAYOUT_PTR_TABLE 0x8032ddc4 /* Pointer to pointer */
#define DEBUG_FLAG1 0x8032d598

/* graph flags */
#define BILLBOARD 4
#define INVISIBLE 0 /* recheck */
#define VISIBLE 1 /* recheck */

//library function:

extern int sprintf ( char * str, const char * format, ... );

/* Functions */
extern int CreateMessageBox(u16 flags, u16 rotate_to_mario, u16 type_of_dialog, u16 message_id);
/* CreateMessageBox */
/* return value = 0x00 -> dialog is happening */
/* 0x01 -> dialog is over (choice #1) */
/* 0x02 -> dialog is over (choice #2) */
/* 0x03 -> normal dialog is over */
/* type of dialog = 0xA1 -> save related (wing blocks?) */
/* 0xA2 -> regular dialog */
/* 0xA3 -> two choices */
/* You may want to set an wrapper function for */
/* CreateMessageBox (check yoshi.c for an example) */

extern int CreateStar(float x, float y, float z); /* returns pointer for spawned object */
extern void CopyObjParams(u32 *dest, u32 *source); /* copies X,Y,Z + rotation from another object */
extern void CopyObjPosition(u32 *dest, u32 *source);
extern void CopyObjRotation(u32 *dest, u32 *source);
extern void CopyObjScaling(u32 *dest, u32 *source);
extern int DeactivateObject(u32 obj_pointer); /* kills current object */
extern float DistanceFromObject(u32 object1, u32 object2); /* usually object 1 = (*Obj) and object 2 = Mario */
extern void DmaCopy(u32 dst, u32 bottom, u32 top);
extern void Copy32BitTrip(u32 dst, u32 src);
extern void ExplodeObject(u32 obj_ptr);
extern void PlaySound(u32 argument);
extern void HideObject(); /* hides current object by ORing 0x01 at offset 0x02 */
extern void UnHideObject(); /* ORs 0x10*/
extern int RotateTorwardsMario(int current_rotation, int rotation_speed, int arg2);
extern void ScaleObject(float global_scaling_factor);
extern void ScaleXYZ(u32 obj_pointer, float x, float y, float z);
extern void SetModel(u16 model_ID); /* change how the object looks */
extern int SetObjAnimation(u16 animation_index);
extern int ShakeScreen(u16 argument); /* argument = 1 to 4 (?) */
extern int SpawnObj(u32 obj_pointer, u16 model_id, u32 behavior); /* returns pointer for spawned object */
extern int SpawnObjAdv(u32 obj_pointer, float x, float y, float z, u32 obj_parentptr, u16 model_id, u32 behavior);
extern int CheckObjBehavior(u32 behavior_segmented_pointer); /* return 1 if behavior == arg, else 0 */
extern int CheckObjBehavior2(u32 obj_pointer, u32 behavior_segmented_pointer); /* return 1 if behavior == arg, else 0 */
extern void SetObjBehavior(u32 obj_pointer, u32 behavior_segmented_pointer); /* 0x802a14c4 */
extern int IsMarioStepping(); /* returns 1 if Mario is on TOP of a solid object, else 0 */
extern void ProcessCollision(); /* 0x803938cc, usually called from behaviors */
extern int SetMarioAction(u32 mario_struct_pointer, u32 action, u32 unk_arg); /* to Do: check return values */


extern int ProcessGeoLayout(u32 *dest, u32 segmented_address); /* 0x8037e0b4 */
extern void guFrustum(u32 m, float l, float r, float b, float t, float n, float f, float scale); /* Creates a frustum (perspective projection) projection matrix (fixed point) */
extern void guPerspective(u32 m, u16 perspNorm, float fovy, float aspect, float near, float far, float scale);


/* Music Related */
extern int SetMusic(u32 layer, u16 song_index, u32 a2); /* possible layers = 0 (main bgmusic), 1 (other musics) or 2 (sfx) */
extern int SetInstrument(u32 *chan_ptr, u8 instrument_index);

/* these functions need to be tested further */
extern void CreateTextBox(u16 msg_ID);
extern int PrintText(u32 x_pos, char *text, u32 fade); /* used in Credits. a2 = a float value ? */
extern void PrintRegularText(u32 x, u32 y, char *table_text_pointer); /* needs to be tested */
extern int StopMario(u16 arg); /* 1 = stop mario 2 = do nothing??

/* print functions */
extern void PrintInt (u16 x, u16 y, char* text, u32 value);
extern void PrintStr(u16 x, u16 y, char* text);
extern void PrintRegularText(u32 x, u32 y, char *table_text_pointer); /* needs to be tested more, used at credits?*/
extern void PrintXY(u16 x, u16 y, char* text);

/* memory functions */
extern int SegmentedToVirtual(u32 segmented_pointer); /* returns RAM pointer of a segmented address */
extern int GetSegmentBase(int segment);
extern int SetSegmentBase(int segment, void *base); /* sets segment pointer table */
extern u8 *DynamicIndexCopy(u32 index, u32 begin, u32 end, u32 what);
//extern void DecompressMIO0(void *src, void *dst);

/* N64 OS Functions */
extern int osEepromRead(u32 mg, u8 address, u8 *buffer); // ret = 0 -> normal termination, ret = -1 address out of range. Reads data from EEPROM.
extern int osEepromLongRead(u32 mg, u8 address, u8 *buffer, int nbytes);
extern int osEepromWrite(u32 mg, u8 address, u8 *buffer); // ret = 0 -> normal termination, ret = -1 address out of range. Writes data to EEPROM.
extern int osEepromLongWrite(u32 mg, u8 address, u8 *buffer, int nbytes);
extern int osEepromProbe(u32 mq); // 0x00 = no EEPROM loaded. 0x01 = EEPROM_TYPE_4K. 0x02 = EEPROM_TYPE_16K.

// EEPROM Checksum: 0x802792C0. BEQ R0, R0, 0x80279BB4 (80279BAC)

/* math stuff */
extern float sqrtf(float x);
extern float sinf(float x);
extern float cosf(float x);

/* =============================== */

typedef struct anim /* unfinishied */
{
u16 framecount; /* 0x08 */
u32 pointer; /* 0x0c */
u32 pointer2; /* 0x10 */
} Animation;

typedef struct anim2
{
u32 *AnimationDMATable; // 0xd1 items, each item 8 bytes in lenght
u32 Current_DMA; // not sure
u32 TargetAnimationPtr; // 0x80060030 - gets copied to MarioObj->animation (that's where's animation data is DMAed)
u32 padding;
} MarioAnimation;

extern int SetMarioAnimation(MarioAnimation *AnimStruct, u16 index); /* returns 1 if animation has changed, 0 if its the same as before) */



typedef struct Music2
{
u32 _0x00;
u32 _0x04;
u32 _0x08;
u32 _0x0c;
u32 _0x10;
u32 _0x14;
u32 _0x18;
u32 _0x1c;
f32 volume; /* 1 = 0x7f - 0x20 */
f32 _0x024_maybe_pan;
f32 _0x028;
f32 pitch_transposition;
u32 _0x30;
u32 _0x34;
u32 _0x38;
u32 instrument; /* pointer to instrument */
/* stuff missing here */
u32 Vibrato; /* 0x70 */

} ChannelStruct;

typedef struct Music1
{
u32 _0x00;
u32 _0x04;
u16 _0x08;
u16 tempo; /* 0x0a */
u32 _0x0c;
u32 _0x10;
u32 pointer_seq_head;
f32 volume;
u32 _0x1c;
f32 _0x20;
f32 _0x24;
u32 _0x28;
ChannelStruct *Channel[15];
u32 _0x7c_sequence_pointer;
} MusicController; /* Layer 0 = 0x80222618 */

typedef struct collision_triangle
{
u16 collision_type; /* check collision.txt */
u16 _0x02;
u8 flag;
u8 _0x05;
s16 ymin;
s16 ymax;
s16 vertex1_x, vertex1_y, vertex1_z; /* 0x0a */
s16 vertex2_x, vertex2_y, vertex2_z; /* 0x10 */
s16 vertex3_x, vertex3_y, vertex3_z; /* 0x16 */
float normal_x;
float normal_y;
float normal_z;
float negdot;
u32 _0x2c; /* unused? */
} CollisionTriangle;

typedef struct pad_struct /* from nagra */
{
s16 stick_x;
s16 stick_y;
float x;
float y;
float z;
u16 currentButton; /* 0x10 */
u16 previousButton;
u32 *statusData; /* 0x14 */
u32 *controllerData; /* 0x18 */
} Pad;

typedef struct camera_struct /* mario->camera (0x8033C520) */
{
u32 mario_action; /* copied from mario->action */
float x; /* also copied from Mario struct */
float y;
float z;
s16 _0x10_mario_0x2c; /* 0x10 */
u16 rotation; /* again copied from Mario struct */
s16 _0x14_mario_0x30;
s16 _0x16_mario_0x32;
u32 _0x18;
u16 _0x1c;
u16 camera_setting; /* 0x06 = door opening 0x09 = triggers initial peach animation */

/* incomplete, many other members left ?? */
} Camera;

typedef struct level_struct /* 0x8033B90c, from Cellar Dweller's notes */
{
s16 _0x00;
s16 terrain_type;
u32 geo_layout_ptr;
u32 collision_ptr;
u32 _0x0c;
u32 mini_objects_ptr; /* 0x10 pointer to an array of objects defined by command 0x39 */
u32 warp_links_head;
u32 _0x18;
u32 _0x1c;
u32 *objects_head; /* 0x24 objects linked list head */
u32 LevelCameraPointer; /* Level Camera Pointer (generated at run-time) */
u32 _0x28;
u32 _0x2c;
u32 _0x30;
u8 _0x34; /* set by level command 0x30 */
u8 _0x35;
s16 music_param; /* 0x36 */
s16 music_param2; /* 0x38 title screen,etc) */
/* more? */
} Level;

typedef struct object_struct /* Regular objects, Mario also has its own struct like this */
{
u16 graph_node_type; /* 0x00 */
u16 graph_flags;
struct object_struct *prev; /* previous linked list object */
struct object_struct *next; /* next linked list object */
u32 graph_parent;
u32 graph_child; /* 0x10 */
u32 geo_layout_ptr; /* 0x14 */
u32 _0x18;
u32 _0x1c;
float _0x20; /* 0x20 */
float _0x24;
float _0x28;
float x_scaling; /* 0x2c */
float y_scaling; /* 0x30 */
float z_scaling;
u16 _0x38;
u16 _0x3a;
u32 animation; /* 0x3c - current animation */
u16 anim_current_frame; /* 0x40 */
u16 anim_timer; /* timer, animation related? */
u16 anim_current_frame_copy;
u16 _0x46;
u32 _0x48;
u32 _0x4c;
u32 matrix_ptr; /* 0x50 */
float float_0x54;
float float_0x58;
float float_0x5c;
struct object_struct *next_object_ptr; /* 0x60: re-check this */
u32 _0x64;
struct object_struct *next_object_ptr2; /* 0x68: re-check this (child_obj) */
u32 _0x6c;
u32 _0x70; /* 0x70 */
u16 active; /* 0x0000 = inactive, 0x0101 = active */
u16 _0x76; /* collision flag according to YE */
struct object_struct *collided_obj_ptr; /* according to YE, pointer to object collided with */
u32 _0x7c;
u32 _0x80; /* 0x80 */
u32 _0x84;
u32 _0x88;
u32 obj_flags;
u32 _0x90; /* 0x90 */
u32 _0x94;
u32 _0x98;
u32 _0x9c;
float x_pos; /* 0xa0 */
float y_pos;
float z_pos;
float x_speed; /* x increment? */
float y_speed; /* 0xb0 */
float z_speed; /* z_increment? */
float speed;
u32 _0xbc;
u32 _0xc0; /* 0xc0 */
u32 x_rotation; /* 0xc4 - rotation triplet */
u32 y_rotation; /* 0xc8 */
u32 z_rotation;
u32 x_rotation2; /* rotation copy (collision?) 0xd0 */
u32 y_rotation2; /* 0xd4 */
u32 z_rotation2;
u32 _0xd8;
u32 _0xe0; /* 0xe0 */
float _0xe4; /* gravity related? y_speed - 0xe4 ? */
u32 _0xe8;
u32 _0xec;
u32 _0xf0; /* 0xf0 */
u32 _0xf4; /* obj type for some behaviors (ie, ice bully), for AMPS, radius of rotation */
u32 _0xf8;
u32 _0xfc;
u32 _0x100; /* 0x100 */
u32 _0x104;
u32 _0x108;
u32 _0x10c;
u32 _0x110; /* 0x110 */
u32 _0x114;
u32 _0x118;
u32 _0x11c;
u32 animation_ptr; /* 0x120 = (set by 0x27 26 behavior command) entry for animation? */
u32 _0x124; /* in some behaviors, action related? */
float _0x128;
float _0x12c;
u32 interaction; /* 0x130
00 = Something Solid. Can't grab. Mario walks around, Can jump over.
01 = Crashed when jumping at it, Used by Hoot.
02 = Grabbing
04 = Going through door
08 = Knocks mario back and dissappears. No damage.
10 = Something Solid, Can't grab, Mario walks around, Can't jump over, Seems somewhat thin..
40 = Climbing
*/
u32 _0x134;
u32 _0x138;
u32 _0x13c;
u32 _0x140; /* 0x140 */
u32 behav_param; /* behav param */
u32 _0x148;
u32 action;
u32 _0x150; /* 0x150 = also reset when action changes */
u32 timer; /* always incremented. When action changes, it's set to 0 */
float _0x158;
float distance_from_mario;
u32 _0x160; /* 0x160 */
float _0x164_x;
float _0x168_y;
float _0x16c_z;
float _0x170; /* 0x170 */
float _0x174;
u32 _0x178;
u32 transparency;
u32 damage_to_mario; /* According to YE, "How many segments of damage to do to Mario for objects that cause him harm" */
u32 health; /* Health (ie, for King bob-omb and whomp */
u32 behav_param2; /* re-check */
u32 previous_action; /* used to reset the 0x154 timer */
u32 _0x190; /* 0x190 */
float collision_distance; /* NOTE: if collision_distance < disappear_distance then disappear_distance = collision_distance */
u32 _0x198;
float drawing_distance;
u32 _0x1a0; /* 0x1a0 */
u32 _0x1a4;
u32 _0x1a8;
u32 _0x1ac;
u32 _0x1b0; /* 0x1b0 */
u32 _0x1b4;
u32 _0x1b8;
u32 _0x1bc;
u32 _0x1c0; /* 0x1c0 */
u32 _0x1c4;
u32 _0x1c8;
u32 script_ptr;
u32 stack_index; /* 0x1d0 */
u32 stack;
u32 _0x1d8;
u32 _0x1dc;
u32 _0x1e0; /* 0x1e0 */
u32 _0x1e4;
u32 _0x1e8;
u32 _0x1ec;
u32 _0x1f0; /* 0x1f0 */
u16 _0x1f4;
u16 _0x1f6;
float col_sphere_x;
float col_sphere_y;
float _0x200; /* 0x200 */
float _0x204;
float _0x208;
u32 behavior_script_entry;
u32 _0x210; /* 0x210 */
u32 collide_obj_ptr; /* pointer to another object (collision happening)?.
Can be used to detect if Mario is on top of the object by comparing
value with Mario's pointer */
u32 collision_ptr; /* set by behavior script (0x2A command) */
u32 _0x21c;
u32 _0x220; /* 0x220 */
u32 _0x224;
u32 _0x228;
u32 _0x22c;
u32 _0x230; /* 0x230 */
u32 _0x234;
u32 _0x238;
u32 _0x23c;
u32 _0x240; /* 0x240 */
u32 _0x244;
u32 _0x248;
u32 _0x24c;
u32 _0x250; /* 0x250 */
u32 _0x254;
u32 _0x258;
u32 behav_param_copy_ptr;
} Object;

typedef struct mario_struct /* 8033b170 */
{
u32 status;
u32 flags; /* cap & other flags */
u32 _0x08;
u32 action; /* see Romanian Girl list */
u32 previous_action; /* 0x10 */
u32 _0x14;
u16 _0x18;
u16 _0x1a;
u32 _0x1c;
float _0x20; /* 0x20 */
u16 _0x24; /* rotation related, if bit 1 of status is set, 0x24 is copied to 0x2e */
s16 hitstun; /* hitstun counter (how long Mario stays invencible after getting hit */
u32 _0x28;
s16 _0x2c;
u16 rotation; /* divide it by 180 to get the angle? */
s16 _0x30; /* 0x30 */
s16 _0x32;
u32 _0x34;
u32 _0x38;
float x_pos; /* 0x3c */
float y_pos; /* 0x40 */
float z_pos;
float x_speed;
float y_speed;
float z_speed; /* 0x50. The next four floats are related to speed/acelleration */
float speed;
float _0x58;
float _0x5c;
u32 _0x60; /* 0x60 */
u32 _0x64;
CollisionTriangle *curr_collision_triangle; /* current triangle mario is stepping in */
float _0x6c;
float ground_y; /* 0x70 - ground Y */
u32 _0x74;
u32 _0x78;
u32 _0x7c;
u32 _0x80; /* 0x80 */
u32 _0x84;
Object *MarioObj;
u32 _0x8c_ptr;
u32 Mario_level_command; /* 0x90 = 8033b4b0 = Information read from the Level command that sets Mario*/
Camera *camera;
u32 _0x98_ptr; /* 0x8033B3B0 */
Pad *pad; /* pointer to controller struct controller 1 = 8033AF90 controller2 = 8033AFAC*/
MarioAnimation *MarioAnimationStruct; /* 0x8033B080 */
u32 _0xa4;
s16 coins; /* 0xa8 */
s16 stars; /* 0xaa */
s16 lifes; /* 0xac */
s16 power; /* 0xae */
u16 constant_ground_distance; /* usually 0xBD */
u16 misc_timer; /* on any value other than zero it will decrease until zero (also, drains mario energy?) */
u32 cap_timer;
u32 _0xb8;
float _0xbc;
float _0xc0; /* 0xc0 */
} MarioStruct;


Darin sind alle Funktionsprototypen enthalten, die wir später mit dem Linker an die entsprechenden Adressen des Spiels "verknüpfen". Auch sehen wir dort Structs, wie "MarioStruct" oder "ObjectStruct". Auch diese können wir später im Code ansprechen wie Mario->Coins, wodurch wir auf Mario's derzeitige Münzenanzahl zugreifen können.

Ich stelle euch dann auch gleich noch die levels.h und animations.h zur Verfügung (auch in include reinmachen):

levels.h:
Spoiler anzeigen
#define HAUNTED_HOUSE 0x0004
#define COOL_COOL_MOUNTAIN 0x0005
#define INSIDE_CASTLE 0x0006
#define HAZY_MAZE 0x0007
#define SHIFTING_SAND_LAND 0x0008
#define BOB_OMB_BATTLEFIELD 0x0009
#define SNOWMANS_LAND 0x000A
#define WET_DRY_WORLD 0x000B
#define JOLLY_ROGER_BAY 0x000C
#define TINY_HUGE_ISLAND 0x000D
#define TICK_TOCK_CLOCK 0x000E
#define RAINBOW_RIDE 0x000F
#define CASTLE_GROUNDS 0x0010
#define BOWSER_FIRST_COURSE 0x0011
#define VANISH_CAP 0x0012
#define BOWSERS_FIRE_SEA 0x0013
#define SECRET_AQUARIUM 0x0014
#define BOWSER_THIRD_COURSE 0x0015
#define LETHAL_LAVA_LAND 0x0016
#define DIRE_DIRE_DOCKS 0x0017
#define WHOMPS_FORTRESS 0x0018
#define PICTURE_AT_END 0x0019
#define CASTLE_COURTYARD 0x001A
#define PEACH_SECRET_SLIDE 0x001B
#define METAL_CAP_COURSE 0x001C
#define WING_CAP 0x001D
#define BOWSER_FIRST_BATTLE 0x001E
#define RAINBOW_CLOUDS 0x001F
#define BOWSER_SECOND_BATTLE 0x0021
#define BOWSER_THIRD_BATTLE 0x0022
#define TALL_TALL_MOUNTAIN 0x0024


und animations.h:
Spoiler anzeigen
/* Yoshi animation indexes */
#define YOSHI_STANDING 0x00
#define YOSHI_WALKING 0x01
#define YOSHI_JUMPING 0x02

/* Koopa-The-Quick */

#define KOOPA_FALLING 0x00
#define KOOPA_ANIM1 0x01
#define KOOPA_ANIM2 0x02
#define KOOPA_RUNNING_FAST 0x03
#define KOOPA_RUNNING_MODERATELY 0x04
#define KOOPA_ANIM5 0x05
#define KOOPA_GETTING_UP 0x06
#define KOOPA_LOOKING_AROUND 0x07
#define KOOPA_GETTING_UP_AND_STANDING 0x08
#define KOOPA_ANIM9 0x09
#define KOOPA_ANIM10 0x0A
#define KOOPA_ANIM11 0x0B
#define KOOPA_JUMPING_FROM_GROUND 0x0C
#define KOOPA_JUMPING_WHILE_STANDING 0x0D

/* Baby Penguin */
#define PENGUIN_WALKING 0x00
#define PENGUIN_SLIDING 0x01
#define PENGUIN_LOOKING_AROUND 0x02
#define PENGUIN_STAND_UP 0x03
#define PENGUIN_WALKING_2 0x04


Nun gehen wir wieder aus DeinOrdner/include wieder raus und nach DeinOrdner. Nun erstellen wir wieder einen neuen Ordner, der unseren ersten C code enthält. Ich nenne ihn hier: "SM64Test". In diesem Ordner erstellen wir eine neue Datei, diesmal mit der Endung .c. Ich nenne unseren Testcode: "HelloWorld.c" Als Nächstes müssen wir in diesem Ordner den Linker und die Makefile einfügen. Zunächst hier den Linker:

Spoiler anzeigen
/* ========================================================================
*
* n64ld.x
*
* GNU Linker script for building an image that is set up for the N64
* but still has the data factored into sections. It is not directly
* runnable, and it contains the debug info if available. It will need
* a 'loader' to perform the final stage of transformation to produce
* a raw image.
*
* Copyright (c) 1999 Ground Zero Development, All rights reserved.
* Developed by Frank Somers
* Modifications by hcs (halleyscometsoftware@hotmail.com)
*
* $Header: /cvsroot/n64dev/n64dev/lib/alt-libn64/n64ld.x,v 1.2 2006/08/11 15:54:11 halleyscometsw Exp $
*
* ========================================================================
*/

OUTPUT_FORMAT ("elf32-bigmips", "elf32-bigmips", "elf32-littlemips")
OUTPUT_ARCH (mips)
EXTERN (_start)
ENTRY (_start)

SECTIONS {
/* Start address of code is 1K up in uncached, unmapped RAM. We have
* to be at least this far up in order to not interfere with the cart
* boot code which is copying it down from the cart
*/

. = 0x80405000 ;

/* Library Functions */
sprintf = 0x8032255c ;

/* Game Functions */
CreateMessageBox = 0x802A4BE4 ;
CreateTextBox = 0x802d8d08 ;
CreateStar = 0x802F2B88 ;
CheckObjBehavior = 0x802a14fc;
CheckObjBehavior2 = 0x802A1554;
SetObjBehavior = 0x802a14c4;
CopyObjParams = 0x8029F0E0 ;
CopyObjPosition = 0x8029F120 ;
CopyObjRotation = 0x8029F148 ;
CopyObjScaling = 0x8029F3A4 ;
DistanceFromObject = 0x8029e27c ;
DeactivateObject = 0x802a0568 ;
DmaCopy = 0x80278504 ;
Copy32BitTrip = 0x80378800 ;
ExplodeObject = 0x802e6af8 ;
GetSegmentBase = 0x80277f20 ;
HideObject = 0x8029F620 ;
UnHideObject = 0x8029f6bc ;
PlaySound = 0x802ca190;
PrintInt = 0x802d62d8 ;
PrintXY = 0x802d66c0 ;
RotateTorwardsMario = 0x8029e530 ;
ScaleObject = 0x8029F430;
ScaleXYZ = 0x8029F3D0;
SegmentedToVirtual = 0x80277f50;
SetModel = 0x802a04c0;
SetSegmentBase = 0x80277ee0 ;
SetObjAnimation = 0x8029f464;
ShakeScreen = 0x802a50fc ;
SpawnObj = 0x8029edcc ;
SpawnObjAdv = 0x8029EF64 ;

SetMarioAction = 0x80252cf4;
SetMarioAnimation = 0x80279084;

IsMarioStepping = 0x802A3CFC ;

DynamicIndexCopy = 0x8027868c ;

ProcessGeoLayout = 0x8037e0b4;


MoveRelated = 0x802a2320 ;

PreMoveObj = 0x802a1308;
MoveObj = 0x802A2348 ;


MoveObj2 = 0x802A2644 ;
UnknownMove = 0x8029E714 ;

_8037a9a8 = 0x8037a9a8;


StopMario = 0x8028bd34 ;
PrintText = 0x802d8844 ;
PrintStr = 0x802d6554 ;
PrintRegularText = 0x802d7e88;

sinf = 0x80325480;
cosf = 0x80325310;
sqrtf = 0x80323a50;

ProcessCollision = 0x803839cc;

/* Music Related */
SetMusic = 0x80320544;
SetInstrument = 0x8031cfd4;

/* OS Functions */
osEepromRead = 0x80329150 ;
osEepromWrite = 0x80328AF0 ;
osEepromLongRead = 0x80324690 ;
osEepromLongWrite = 0x803247D0 ;
osEepromProbe = 0x80324080 ;

guFrustum = 0x80324D74 ;
guPerspective = 0x80325010 ;

//DecompressMIO0 = 0x8027F4E0 ;

/* The text section carries the app code and its relocation addr is
* the first byte of the cart domain in cached, unmapped memory
*/

.text : {
FILL (0)

__text_start = . ;
*(.init)
*(.text)
*(.ctors)
*(.dtors)
*(.rodata)
*(.fini)
__text_end = . ;
}


/* Data section has relocation address at start of RAM in cached,
* unmapped memory, but is loaded just at the end of the text segment,
* and must be copied to the correct location at startup
*/

.data : {
/* Gather all initialised data together. The memory layout
* will place the global initialised data at the lowest addrs.
* The lit8, lit4, sdata and sbss sections have to be placed
* together in that order from low to high addrs with the _gp symbol
* positioned (aligned) at the start of the sdata section.
* We then finish off with the standard bss section
*/

FILL (0xaa)

__data_start = . ;
*(.data)
*(.lit8)
*(.lit4) ;
/* _gp = ALIGN(16) + 0x7ff0 ;*/
/* _gp = . + 0x7ff0; */
. = ALIGN(16);
_gp = . ;
*(.sdata)
__data_end = . ;
/*
__bss_start = . ;
*(.scommon)
*(.sbss)
*(COMMON)
*(.bss)
/* XXX Force 8-byte end alignment and update startup code */

__bss_end = . ;
*/
}

.bss (NOLOAD) : {
__bss_start = . ;
*(.scommon)
*(.sbss)
*(COMMON)
*(.bss)
__bss_end = . ;
end = . ;
}

}


Im Linker sieht man die selben Funktionen aus explode.h, nur das sie dieses Mal Adressen zugewiesen werden, also den Adressen des Spiels. Denn diese Funktionen existieren im Spiel und so können wir sie verknüpfen. Wir brauchen also nur die richtigen Funktionsprototypen zu schreiben (mit den richtigen parametern und den richtigen Rückgabewert (falls es einen gibt)) und diese müssen wir dann über den Linker mit der entsprechenden Adresse verknüpfen. Im Output wird dann einfach über JAL (was Jump and Link bedeutet, und generell benutzt wird um auf Subroutinen zu springen) geregelt. Wenn die Parameter stimmen, dann werden die Argumentregister (bei MIPS ASM) A0-A3 (oder falls mehr vorhanden, dann über den Stack) mit den richtigen Längen geladen. Nun zu dem:

Code
OUTPUT_FORMAT ("elf32-bigmips", "elf32-bigmips", "elf32-littlemips")
OUTPUT_ARCH (mips)
EXTERN (_start)
ENTRY (_start)


OUTPUT_FORMAT und OUTPUT_ARCH sind einfach nur da um auszusagen, dass wir unseren Code in MIPS Binary kompilieren. Bei EXTERN(_start) und ENTRY(_start) wird ein entsprechendes "Main" festgelegt. Deshalb müssen wir später in unserem C code unbedingt "_start" schreiben, ansonsten kriegen wir einen Fehler vom Compiler.

Nun folgt zuletzt die Makefile, die wir benötigen, damit unser Code überhaupt kompiliert wird. Wir müssen dem Compiler ja ebenfalls ein paar Commands geben, was er machen soll.

Spoiler anzeigen
#
# Standard N64 Makefile
#

# Name!
PRJNAME = debug
PARTS = $(PRJNAME).o

# Vars
INC_FLAGS = -I. -I../include
LIB_FLAGS = -L. -T mario64.x
ROOT = ..
N64ROOT = ../root
N64PREFIX = mips64-
N64BIN = ${N64ROOT}/bin

# Flags
NOBUILTIN=-fno-builtin
CFLAGS=-std=gnu99 -nodefaultlibs -march=vr4300 -mtune=vr4300 -mabi=32 ${INC_FLAGS} $(NOBUILTIN) -o3
ASFLAGS = -march=vr4300 -mabi=32
LINK_FLAGS=${LIB_FLAGS} ${LIBS}

# Programs
CC = $(N64BIN)/mips64-gcc
LD = $(N64BIN)/mips64-ld
OBJCOPY = $(N64BIN)/mips64-objcopy
CHEAT = $(N64BIN)/nemucheat
BINCODE = $(N64BIN)/bin2code

# Awwright
$(PRJNAME).bin: $(PRJNAME).elf
$(OBJCOPY) $(PRJNAME).elf $(PRJNAME).bin -O binary

$(PRJNAME).elf: $(PARTS)
$(LD) -o $(PRJNAME).elf $(PARTS) $(LINK_FLAGS)

clean:
rm *.o *.elf *~ *.bin final.txt codes.txt -vf


Wichtig ist jetzt, dass wir bei PRJNAME (ganz am Anfang) den Namen ändern auf HelloWorld, da meine C Datei auch "HelloWorld.c" heißt. Ansonsten wird der Compiler die C Datei nicht finden und ebenfalls einen Fehler ausgeben. Ich möchte nicht unbedingt jetzt auf die einzelnen Flags eingehen, da das einige wahrscheinlich nur eher verwirrt. Aber -march=vr4300 bedeutet, dass unser Code in MIPS R4300 Binary kompiliert werden soll und -mabi=32 bedeutet, dass unsere Register (MIPS Register) die Länge von 32-bit haben.

Nun wo wir fertig sind, schreiben wir nun unseren ersten C code.



Zuerst müssen wir immer sicherstellen, dass wir die n64.h und die explode.h inkludieren, denn sie tragen immerhin die vordefinierten Datentypen und die explode.h die entsprechenden Funktionen. Ohne diese beiden Headers, wäre Code schreiben unmöglich. Dann müssen wir, wie ich bereits sagte, darauf achten, dass unsere Funktion _start heißt und sie sollte generell immer void sein. Ihr müsst in die Klammern nichts reinschreiben, (wird automatisch dann als void gewertet) ich habe es aber nur zu Übersichtszwecken gemacht und um Konfusion zu vermeiden.

Dann müssen wir als Nächstes ein inline ASM Befehl reinschreiben, denn wir müssen dafür sorgen, dass die Pointer (des Global Pointers, kurz GP) richtig geladen werden. Ansonsten crasht der Code das game.

Nun schreiben wir unsere eigentliche erste Funktion,
Code
extern void PrintXY(u16 x, u16 y, char* text);
(Funktionsprototyp in explode.h)

Grundsätzlich ist das dritte Argument immer ein Pointer und in ASM hätte man den Text manuell irgendwo eintragen müssen und anschließend die Pointeradresse in A2 laden müssen. In unserem Falle aber regelt der Compiler das ebenfalls selber und schreibt den Text am Ende des Codes und der Compiler trägt die Adresse automatisch ein. Jetzt, nachdem wir unseren Code geschrieben haben, müssen wir ihn nur noch kompilieren. Allerdings müssen wir noch eine winzige Kleinigkeit im Linker regeln, nämlich unseren Entry-Point im RAM von SM64. Im Linker sollte man unter SECTIONS direkt am Anfang folgendes sehen:

Code
. = 0x80405000 ;


Dies ist unser Entry-Point im RAM. Wir lassen es mal so stehen, weil das gerade so passt.

Nun kompilieren wir unseren Code. Dafür öffnen wir jetzt MSYS und machen ein "cd" zu unseren HelloWorld.c Code. Ich habe mein Zeugs immer direkt auf C:\ gespeichert, weil mir das lange cd'en auf die Nerven geht. Der Vorgang sieht dann wie folgt aus:



Kriegt ihr sowas raus, dann sollte alles funktioniert haben. Wenn ihr jetzt nochmal in euren Ordner schaut, seht ihr das auch neue Dateien entstanden sind:


In diesem Falle interessiert uns aber die HelloWorld.bin Datei (nee, ist keine Snes9x ROM), denn da steckt unser MIPS Binary drin. Wir wollen nun unseren C code im Spiel testen, dafür müssen wir aber jetzt die normale SM64 ROM (die 8MB groß ist) extenden auf 24MB und anschließend noch auf 65MB expanden.

Ich denke den Extensionprozess kennt der eine oder andere schon. Man downloade sich von VL-Tones Seite seinen Extender:
http://qubedstudios.rustedlogic.net/M64ROMExtender1.3b.zip

Und extende seine "Super Mario 64 (U) [!].z64" Datei, die 8MB groß sein sollte. Der Output wird dann im selben Ordner sein und "Super Mario 64 (U) [!].ext.z64" heißen. Wenn ihr fertig seid, müsst ihr anschließend die ROM noch expanden auf 65MB. Besser ihr tut es gleich mit Skelux's Level Importer:

http://www.mediafire.com/download/3o83fyel15hkbas/SM64+Level+Importer+1.9.1S.zip

Runterladen, und ROM damit öffnen. Das Tool fragt dann automatisch, ob du die ROM auf 65MB expanden willst. Anschließend kannst du auch dann gleich den PPF Patch patchen lassen, der für den Obj-import notwendig ist. Ist alles fertig, kannst du das Tool wieder schließen. Deine "Super Mario 64 (U) [!].ext.z64" sollte jetzt von 24MB auf 65MB angestiegen sein. Wir haben es geschafft. Um den Code nun ins Game zu patchen, müssen wir erstmal errechnen wo in der ROM sich die entsprechende RAM-Stelle befindet, denn 0x80405000 ist eindeutig eine RAM-Adresse und wir müssen erst errechnen, wo die entsprechende ROM Adresse ist. Die Lösung habe ich bereits:

Man nehme das Präfix "80" weg und man hat 0x405000 übrig. Man addiere 0xE00000 und kriegt 0x1205000 raus. Dort muss unser Code also rein. Öffnet also eure 65MB ROM und springt an das 0x1205000 offset. Ihr solltet dann überall 01 sehen:



Nun müsst ihr eure HelloWorld.bin im HexEditor öffnen und alles von der HelloWorld.bin kopieren.


Jetzt müsst ihr bei der ROM einfach nur an Stelle 0x1205000 alles reinpasten. Allerdings achtet beim (falls jemand den gleichen Hex-Editor benutzt wie ich) HxD Editor, dass ihr NICHT Ctrl+V drückt. Denn dies "pastet" wortwörtlich den .bin rein und vergrößert die ROM bzw. verschiebt dann andere wichtige Stellen. Bei HxD solltet ihr also beim "überschreiben" immer Ctrl+B drücken. Am Schluss sieht das dann so aus:



Euer Code ist nun drin. Speichert ab. ABER! Wir sind noch nicht fertig. Wir müssen jetzt nochmal an unser Behavior (an Adresse: 0x21ACD0) zu unserem 0x0C command, was sich bei Adresse: 0x21ACFC befindet. Dort ändert ihr:

0C 00 00 00 80 2A E8 5C um in: 0C 00 00 00 80 40 50 00

Na, fällt euch was auf? Genau, im behavior command müssen wir die RAM-Adresse eintragen, an dem sich unser Code (während der Laufzeit) befinden wird. Damit der Code natürlich im Game ausgeführt wird, müssen wir erstmal einen Fly Guy mit dem Custom Behavior setzen. Das geht natürlich mit dem allbekannten Toad's Tool 64, den SM64 Level Editor. Auch hier nehmen wir wieder Skelux's Version (er hatte von VL-Tone den Source bekommen und Skelux hat ein bisschen was verbessert): http://www.mediafire.com/download/9712q0cms901nvr/Toad%27s+Tool+64+v0.6.2S.zip Falls jemand einen Mac besitzt, dann das runterladen: http://www.mediafire.com/download/9rdnblj57opbl43/Toad%27s+Tool+64+v0.6.2S+%28Mac%29.zip

Also öffnet euer Toad's Tool 64 und ladet eure ROM. Ich ersetze den Schmetterling jetzt durch "Fly Guy". Falls das Objekt nicht vorhanden sein sollte in dem Level deiner Wahl, nicht schlimm. Dann müsst ihr halt nur ein Objekt wählen und Model ID auf Fly Guy und das entsprechende Behavior setzen. So, ich habe jetzt den Schmetterling in Fly Guy umgewandelt:



Aber (wieder einmal) sind wir noch nicht fertig. Wir müssen jetzt nur noch das Behavior ändern. In der unteren Leiste solltet ihr "Behav" sehen, was für Behavior steht. Dort kommt unser Behavior rein. Da unser custom Behavior nicht in dieser Liste steht, müssen wir es manuell eintippen. Dabei haltet ihr Alt gedrückt und drückt mit der Maus dann in die Behav Textbox rein. Wir tragen nicht 21ACD0 ein, sondern die segmented Adresse, in unseren Falle: "000ED0" solltet ihr keine Hexadezimalansicht haben (es sollte vor jeder Zahl "h" stehen) dann klickt einfach die "Hexadecimal" Checkbox an. Nun Alt+Klicken und dann eintragen:



Jetzt sind wir fertig und speichern ab. Nun öffnet eure ROM in PJ64. Das Resultat sollte dann so aussehen:



Der Code wird, wie gesagt, nur dann ausgeführt wenn das entsprechende Objekt mit dem Custom Behavior geplaced wird. Logisch, das wenn man es doppelt placed, das der code dann auch doppelt ausgeführt wird. Allerdings lässt sich das mit ein paar C Tricks leicht umgehen, bspw. das der Code erst dann ausgeführt wird, wenn man Taste B in der Nähe vom Fly Guy drückt, etc.

Nun wisst ihr, wie man Behaviors zusammenbastelt und C code schreibt und anschließend in die ROM patcht. Natürlich könntet ihr auch euren Code direkt durch Mario's behavior ausführen lassen, allerdings wird der Code (logischerweise) in jedem Level und überall ausgeführt. Daher nutze ich Mario's behavior eher dann, wenn ich was RPG mäßiges mache oder irgendeine Anzeige für Mario mache. Auch sollte man hier Mario's Behavior nicht überschreiben, sondern stattdessen eine Hook in einer bereits vorhandenen Funktion schreiben, sodass Mario's normale Routine ausgeführt wird zusammen mit deinem Code von Adresse 0x80405000. Ich werde auch noch ein paar C tutorials machen, was man nun mit C coden so alles machen kann.
geschrieben am 21.10.2014 16:42:59 in Super Mario 64 Hacking
( Link )
Zitat von StarLand:
Zitat von Tarek701:
Zitat von Ricicir:

Das ist ein guter 64 Hack

Korrekt. Derzeit ist ASM, C nicht angesagt. Hauptsächlich editieren die nur etwas im Hex Editor. Kaum einer von denen weiß, was frauber herausgefunden hat.


Das entsprieht nicht der Wahrheit.
Sie Programmieren in C/C++ und Assembler sogar Disassembly wenden die an.
Ahja wenn dann wird dir Skelux die Frage beantworten können.


Bitte schau dir nochmal das Datum an, wann ich den Beitrag verfasst habe. Das war immerhin schon 3 Jahre her. Also bitte.

http://www.smwhacking.de/forum/viewtopic.php?f=31&t=1087 - Hier mein Tutorial zu C Coding, falls Interesse besteht.

Ich habe auch, weiterhin, Recht. Die einzigen, die wirklich in MIPS ASM programmieren können und wirklich Leistungen in SM64 damit erzielt haben, waren Kaze, shyguy, skelux, yoshielectron und ich. Kann natürlich sein, dass es mehr gibt, jedoch wird das nicht bekräftigt, da eben die Beweise fehlen. Welche Frage soll denn Skelux mir beantworten können? Skelux codet in ASM, genau wie ich und ein paar andere. In C++ wird nicht programmiert, zumindest nicht für SM64. Wenn dann wäre das C. Allerdings eignet sich C Coden in den meisten Fällen nicht, weil die kompilierte Output oft zu groß ist. Eher wäre es sinnvoll für neue Behavior zu erstellen. Das wars dann aber auch. Also, ist weiterhin nicht angesagt, weil jeder zu faul ist um es zu lernen. Also, immer schön informieren.

MfG,
Tarek701.
geschrieben am 31.10.2014 20:20:13 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
v3.23 ist draußen!
Hab jetzt alle Befehle von MIPS drin, Labels gehen, .ORG, .ASCIIZ direktive sind auch jetzt drin. Und natürlich alle floating-point Befehle (Das war sehr anstrengend)

MfG,
Tarek701.
geschrieben am 01.11.2014 4:38:00 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
Zitat von RPG Hacker:
Good guy Tarek!
Was ist .ASCIIZ? Zum Einfügen von Text oder sowas?


Es gibt zwei ASCII Direktiven, einmal .ascii und .asciiz. Beide erlauben es ASCII-Strings in die ROM einzufügen. Unterschied ist nur, dass .asciiz den Strint mit einem \0 (Terminator-String, "00" Byte) abschließt. Wenn ich in MIPS bspw. den Hello World Code schreibe, wird .asciiz empfohlen, da MIPS dann genau an dieser Stelle aufhört zu lesen. Würde man .ascii verwenden, so würde MIPS beim Auslesen von Pointern (z.B Pointer zu Text) einfach weiterlesen und dann unschöne Ergebnisse hervorrufen.

Inzwischen hat mein Programm auch eine Front-End (GUI):
geschrieben am 03.11.2014 15:44:03 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
CajeASM v3.30 ist draußen.

Updates:
Man kann jetzt Variablen/Defines reinmachen. Bspw.:

[Var1]: 0xA

ADDIU T0, T1, @Var1
NOP

Wird umgewandelt in:

ADDIU T0, T1, 0x000A

Auch sind jetzt Dezimalwerte und Binärwerte drin. Wenn ma also schreibt, ADDIU T0, T1, #12 wird das als Dezimalwert gewertet und entsprechend in Hex umgewandelt zu ADDIU T0, T1, 0xC

Download-Link:
http://www.smwcentral.net/?p=section&a=details&id=9277
geschrieben am 09.12.2014 18:38:12 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
Neue CajeASM Version draußen! Ist zwar schon 'ne Weile da, aber wird jetzt geshared:

Änderungen/Neuerungen:
- Variablen können jetzt auch Binär- und Dezimalwerte enthalten
- LUI Instruktionen funktionieren jetzt auch mit Variablen
- Mit hex { hex werte hier } kann nun direkt Hex-Werte in die ROM eingefügt werden.
- Code wurde bisschen verbessert, Geschwindigkeitsboost. Und Labels und Variablen werden nun besser
dargesellt.
- Konsole schließt sich nicht mehr sofort. So kann man vorher noch schauen, ob man irgendwo 'nen Syntax
Error hat.
- Dezimalwerte und Binärwerte funktionierten vorher nicht richtig, wurde gefixt.
- Kommentare wurden vorher nicht in Variablen oder Labels ignoriert, was dazu führte, dass der Code nicht mehr
weiterschrieb.
- CajeASM Dokumentation
- Neue Pseudo-Befehle, BGT (Größer Als), BLT (Kleiner Als), BGE(Größer als oder gleich), BLE (Kleiner Als oder
gleich), BAL (Branch Link), B (Branch, direct)
- rn64crc (Checksum Recalculator) war vorher fucked up und hat daher die Prüfsumme nicht neu berechnet, was
dazu führte, dass die ROM evtl. nicht mehr startete.

http://www.smwcentral.net/?p=section&a=details&id=9277
geschrieben am 03.01.2015 14:27:59 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
So, CajeASM v5.1 ist draußen. Ist sehr vieles verändert wurden und den Großteil nochmal neugeschrieben. Es gibt jetzt eine .include direktive, labels müssen nicht mehr mit "!" Token gepräfixt werden und die Variablen/Defines laden jetzt entsprechend des Befehls entweder nur die obere Hälfte der 32-Bit Adresse (bspw. LUI) oder die untere Hälfte der Adresse (bspw. ORI, ADDI/ADDIU, LH, SH, LB, SB, LW, SW, etc.).

Bsp.:
[Var1]: 0x8034B218

LUI T0, @Var1
LH T1, @Var1(T0)

Würde später so übersetzt werden:
LUI T0, 0x8034
LH T1, 0xB218(T0)

Das würde jetzt den derzeitigen Münzwert von Mario von Adresse 0x8033B218 in T1 laden. (0x8033B218 weil 0xB218 größer als 0x7FFF ist. Wenn ich eine 16-Bit Adresse, die negativ ist, auf 32-bit verlängere, will MIPS logischerweise die negative Zahl erhalten und deshalb sieht die Zahl im 32-Bit Bereich so aus: 0xFFFFB218. Nun wird dieser Wert zu 0x8034 hinzuaddiert, und wir kriegen 0x8033B218. Deshalb müssen wir zur oberen Hälfte immer eins hinzufügen, ansonsten würde er nämlich (falls wir ursprünglich 0x8033 geschrieben hätten) fälschlicherweise den Wert aus 0x8032B218 laden)

Englische Changelog:
[ADD]: .include directive, which let's you allow to include other asm files to your main asm file. It also imports labels and variables/defines.
[ADD]: Defines are now handled properly when load with LUI. LUI only loads the upper half of the define, while all other lower half instructions like ADDIU, ORI, etc. load the lower half of the define.
[FIX]: Comments won't get messed up anymore.
[DEL]: Labels no longer need to be prefixed with a "!" token.
[ADD]: New Pseudo-Instructions: BEQI, BNEI, BGTI, BLEI, BGEI, BLTI.
[ADD]: A complete rewritten and up-to date CajeASM Manual.
geschrieben am 20.02.2015 16:46:32 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
ENDLICH. CajeASM v6.0 ist draußen. War bisher das nervigste von allem, vorallem wegen Vektoren und Skalaren.

Hier Update Liste (auf englisch)

  • [ADD]: Added RSP ASM Instruction Set.

  • [FIX]: Error when using more than 3 or 4 labels.

  • [FIX]: Some instructions not recognizing lowercased registers.

  • [FIX]: A lot of BitField errors (blame me)

  • [FIX]: Read/Write Error when including an asm file which includes asm files too which also include asm files. (lol)

  • [FIX]: Unclosed FileStreams; Sometimes caused some code not to be written. (blame me^2)

  • [FIX]: Counter error; didn't properly count the value out of range errors.

  • [FIX]: CajeASM sometimes didn't properly open rn64crc and chksum64 and so didn't fix the checksum.

  • [IMP]: Improved speed of CajeASM and use Streams properly now to save RAM. (Fastz)


Download-Link:
http://sm64-hacks.square7.ch/CajeASM%20v6.0.zip
geschrieben am 05.04.2015 23:23:38 in SM64 Hacking - CajeASM v7.0 (Stable)
( Link )
UPDATE(04/05/2015):
CajeASM v7.0 ist draußen. Habe das komplette tool neugeschrieben, verdammt viel verbessert und CajeASM supportet jetzt auch little-endian und byteswapped/middle-endian ROMs.

Download-Link:

http://origami64.net/attachment.php?aid=52

UPDATES:

  • [IMPR]: Recoded and improved errors in code, writes errors now to a log file. (Logs/log.txt) Label and define errors are saved in two separate log files in Logs folder.

  • [IMPR]: CajeASM assembles 12-20 secs faster than before. Hyperspeed fucky fuck funky.

  • [ADD]: CajeASM is now able to assemble ASM code to big-endian (.z64), little-endian (.v64) and byteswapped/middle-endian (.n64).

  • [ADD]: Added new directives:

    • .incbin "binfile.bin" -> Let's you include binary files into your ASM code.

    • .byte/.halfword/.word/.float -> Let's you insert numeric values into your code.

    • .align alignment, (optional) fill -> Aligns your data/code to a byte boundary, optionally filling the skipped bytes with 'fill'.

    • .skip n, fill -> skips 'n' bytes, optionally filling them with 'fill'.

  • [FIX]: Fixed decimal, binary value conversion.

  • [FIX]: Fixed LI instruction to not use ADDI/ADDIU anymore for values which are in 16-bit range.

  • [FIX]: Fixed crashes caused if the immediate value didn't have a prefix or was too short.

  • [FIX]: Re-added missing instruction SYSCALL.

  • [FIX]: Rewritten label/define list code. This time it should work better and more efficient now and prevent mystical label errors.

  • [ADD]: New command-line options, new command-line argument parser.

  • [IMPR]: If no destination register is specified in pseudo-branch instructions, then on default AT register is used.

  • [FIX]: Fixed MTC2/MFC2 (RSP) instructions not reading the element. (ex.: mtc2 t0, v3[2])

  • [FIX]: Fixed vector load/store instructions not reading the address properly.

  • [ADD]: New CajeASM v7.0+ Manual. Better, explains all pseudo-stuff step-for-step and shows how they look like when translated to real MIPS code.

  • [FIX]: ROM and ASM Files close properly now.

  • [FIX]: include directive for ASM files didn't work properly before and didn't import all defines.

  • [FIX]: hex {} parser/lexer error and confusing it with labels. Has been finally fixed.

  • [FIX]: CajeASM v6.03 crashed when labels were called three times.

  • [DEL+REPL]: (internal) Deleted some old encoding algorithm (originally this was supposed to be a small, quick reader (encoding) for ASM code, fucked up crap which never worked in the end. This was replaced with an algorithm somewhat similiar to ( w(v') >= t + 1) )

  • [FIX]: Closing CajeASM console-command line by pressing "X" caused that rn64crc.exe and chksum64.exe didn't open.

  • [FIX]: CajeASM update checker sometimes didn't read the URL.

  • [FIX]: Fixed prefixes like "0X" not being recognized (forgot to uppercase that one in my check code)

  • [FIX]: Fixed inefficient use of my BitFields (some fields were left filled with bits of preceding instructions, sometimes causing horribly wrong instruction encodings)

  • [FIX]: Fixed instruction reader and code which passes the bits to it's fields, especially the FPU instructions were sometimes not written properly.

  • [FIX]: CajeASM GUI no longer crashes if file is in use (actually it shouldn't even crash before, but it still did for some others. )

  • [FIX]: BEQI/BNEI and BGEI/BLEI/BLTI/BGTI not being correctly translated. Fixed this finally.

  • [FIX]: Lexer errors sometimes crashed CajeASM, especially LI instructions once again.

  • [FIX]: Result of MIPS instructions (Appending BitFields) sometimes were out of range (leading zero's) causing errors.

  • [FIX]: Fixed CajeASM GUI being not properly getting the filepath sometimes (when space was used in filenames).

  • [FIX]: include directive for ASM files sometimes didn't assemble some instructions (forgot to point to visitor, blame me)

  • [FIX]: CajeASM didn't read RSP instructions properly sometimes (especially the scalar modes)

  • [IMPR]: Re-coded reader for scalar modes for RSP instructions (0q, 1q, 0h, 1h, 2h, 3h, 0w-7w)

  • [FIX]: Removed "URL not found" spam. (caused when link wasn't available or when some crap was wrong with the host)

  • [FIX]: Fixed crash when attempting to open a zero byte file.

  • [FIX]: Fixed various memory corruption issues (random crashing)

  • [FIX]: Fixed parser not recognizing RSP instruction VSUB.

  • [FIX]: Fixed endless loop if define didn't exist.

  • [FIX]: Fixed B instruction not being translated correctly to real MIPS ASM instruction.

  • [IMPR]: CajeASM GUI and CajeASM console-application merged to one.