Register | Login
Views: 19364387
Main | Memberlist | Active users | ACS | Commons | Calendar | Online users
Ranks | FAQ | Color Chart | Photo album | IRC Chat
11-02-05 12:59 PM
1 user currently in Super Mario World hacking: labmaster | 3 guests
Acmlm's Board - I2 Archive - Super Mario World hacking - Learning SMW ASM [ASM howto] | |
Pages: 1 2 3 4 5Add to favorites | "RSS" Feed | Next newer thread | Next older thread
User Post
Glyph Phoenix

Level: 39

Posts: 305/745
EXP: 385876
For next: 18895

Since: 11-07-04

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 05:36 AM Link | Quote
Here are some useful ASM resources:

MarcTheMer's SNES asm tutorial

SPASM ASM reference - This is a great .hlp file. It contains all the opcodes alphabetically organized.

Hex editors - You'll need one of these.

Escherial's SMW Memory Map - A great combination of RAM addresses

==Conversion==

Before you start, you should know how the Memory Map and ASM pointers work. Example:

Coins = 7E0DBF in the Memory Map. This is the preferred way to list a ram address.

Coins = BF 0D when you're pointing with ASM.

As you can see, the difference is simple: remove the 7E, which is often implied in ASM coding, and rearrange the two pointer bytes.

So 7E0DBF becomes BF0D.

==Starting out==

Super Mario World, if you haven't opened it up with a hex editor, is made up of hexadecimal numbers. These start at 0, and end at FF. Hexadecimal is weird. Look at how weird it is here.

Now, many numbers are just regular numbers. If you have Calculator in Windows you can open up the scientific part of it and convert between regular decimal and hexadecimal numbers. Many of them just values, like how many lives Mario starts out with.

Some numbers represent opcodes. These are commands that 65c816 ASM (which Super Mario World runs on) operates though. The help file linked above lists them all. They have two forms; one is a 3-letter pnumonic, the other is machine code. The lettered version can be assembled into machine code.

One of the best ways to learn anything is by doing. This thread is full of ASM traces. Here's one as an example:

x52D8, when Mario loses a life.

Using your hex editor, open up the SMW rom (you may need to edit it with Lunar Magic first so all the addresses are in the right place... not sure about this one) and jump to offset x52D8. You may need the x first, you may need an 0x first, or you may only need the numbers 52D8. Experiment to see exactly how your hex editor works.

At this place in the ROM, you should be reading the numberse CE, BE, and 0D. CE is a decremental opcode, and BE OD is a pointer to Mario's lives in ram. CE BE 0D tells the processor to take 1 from Mario's lives.

Now that you know this opcode and its pointer, you can change it to whatever you want.

Want it so Mario loses a coin instead of a life? Change the pointer BE 0D, Mario's lives in RAM, to BF 0D, Mario's coins. Tada. Mario loses a coin.

Another example? Change CE BE OD, Decrement lives by one, to 9C BF 0D. 9C is the absolute opcode STZ, Set To Zero, so the game goes to that address in RAM and sets it to zero. Tada. Now Mario loses all his coins. It is that simple.

Now, almost all opcodes work like or similar to that. They start with an opcode and are followed by more information pertaining to the opcode. But many opcodes need more than the pointer following it, and for that you need to use the accumulator, the status flags, and the X and Y registers which will be covered later.

At this point you only know one real place in the SMW rom, the 3 byte opcode that decrements your coins by one. There are other places out there you can decode with the 65816 help file or any other opcode list you can find. Check Hyperhacker's data, he listed a lot of nifty offsets in the memory locations thread. You will need to use Lunar Address to convert some of them. Check the section on Jump statements below if you want to know more about that.

The accumulator is this number always available to you. You load data into it using LDA, usually. In hexadecimal if you want to load the next byte you use A9 and if you want to load from a ram address you use AD. By writing A9, "Load number into accumulator" and 05, you load 05 into the accumulator. AD is "Load number from memory into accumulator", so writing AD and BF OD loads how many coins you have into the accumulator.

Now that that number is in the accumulator, you can do all kinds of nifty things with it. One of the most common is storing that number someplace else. This may not seem useful now, but if you create a blocktool block and a .bin file I can show you why it is.

Contents of the .bin file you should make:

A9 00 8D BF OD 60

A9 loads the next number, 00, into the accumulator. It is an immediate opcode, which means it uses the next number for whatever the opcode does, while absolute addressing opcodes would read the next two bytes and go to that space in RAM.

8D stores the accumulator into BF OD, where Mario's coins are located. 8D takes whatever's in your accumulator and since it's an absolute address opcode, it stores it at the space in ram designated by the next 2 bytes.

60 is a "return from subroutine" statement you have to put at the end of every blocktool bin file. More on that later.

Now create a block in blocktool and set its script address to the bin file you made. You have now just created your own block, which sets coins to zero. In Blocktool you should set all of the offset fields to -1 except for Above, Below, and Sides, which you should set to 0. Import the block into LM and set its type to 25.

Alternatively, set every setting to -1 except for Above and set the block's type to one of the question mark blocks. Now in addition to whatever the question mark block did, it will set your coins to zero.

You should be able to edit the BF OD to BE OD and it will set Mario's lives to 1. The reason it is one and not zero is because SMW is programmed funnily. If you change the 00 to 01 it will set Mario's coins to 1. You can modify this simple script using what you know.

Starting with simple stuff like this, you can move onto complex ASM hacks. That's all for now. Content added to this thread would be much appreciated.

==More Blocktool==

You already know how to create a basic blocktool block, but one of the nifty things about Blocktool is how within Blocktool code you can change the way a block acts.

Create a .bin file with the following code:

A0 01 A9 30 8D 93 16 60

A0 is a command that loads the next number into the Y register. The X and Y registers are variables like the accumulator that are used for storing temporary data. In blocktool code, Y is the high byte of the type of block you are coding. If you'll recall, 130 is the name of the concrete block in LM's map16 editor. A0 01 loads the "1" of "130" into the Y register.

Now you store the "30" of "130" at 93 16. That's where you store the low byte of block types. If you'll recall, 8D is "Store accumulator at address". Since your accumulator has been set to 30, this completes the code.

Well, actually, that mandatory 60 "return from subroutine" should complete the code.

So create a custom block and set the script to the one you just made. Set Above, Below, and Sides to 0 and the rest to -1. In LM set the block's type to 25. 25 is a block of blank space.

Now, what does this do? When touched, blocktool code activates. The code tells this block to act like 130. That means that from whatever the block is supposed to do, it instead acts like a concrete block.

But... the block is only set to activate when touched from sides, above, and below by Mario. Therefore all other sprites go through it. This is now a "block only Mario" block. By changing the settings, you can make it a "block everything but Mario" block.

Most likely the greatest thing you can do, however, with Blocktool block types is use it with branch statements. I'll get to that later.

==Branch Statements==

Branch statements are very important. If you know C or C++ or Java or Javascript or virtually any other programming language, it should have branch statements as well. (Usually called IF statements.)

Branch statements work a little differently in ASM. When the branch's requirements are met, the code jumps to a different place. I have made an example, but it is complicated and untested. I repeat, I have not tested this code, so I have no idea whether or not it will mess up your stuff.

My example, this blocktool .bin file code:

AD 86 00 C9 00 F0 03 CE 86 00 60

AD should load the data at 86 00 into the accumulator. 86 00 points to how slippery the level is. C9 is a comparison statement. It compares your accumulator (currently holding the level friction) and the byte that comes directly after it, in this case 00.

When you use comparisons this tells your emulator that whether or not there is a difference between the accumulator (holding the level friction) and the number after the C9 opcode (00).

Now, here's the clincher... F0 03 skips 3 spaces. But only if the zero status flag is set.

This means that if your coins are equal to 00, the code in bold is never read. How is this useful? You'll find out.

The code CE 86 00: CE decrements, and 86 00 is how slippery the level is.

The branch statement before the code is saving you from having the slippery level variable go below zero. Because if it, did we'd have an overflow on our hands and those suck.

60 at the end should finish every blocktool script.

To make sure the block doesn't go nuts and activate every frame that you're touching it, set all options to -1 except for below. In Lunar Magic, set the block type to one of the question mark blocks.

Now you have a block that, along with all the other things the question mark block did, will make the level less slippery and not glitch up when the level friction is equal to zero. At least, it shouldn't glitch up, but it might anyway because I haven't tested it.

== Jump Statements==

The stack is a place where memory is temporarily stored. You push and pull numbers onto it. Once you push a value onto the stack you can take it back off with a pull opcode. A basic understanding of this is useful when you get to JSR and JSL (and RTS and RTL) statements, but not necessary for now.

This was taken and edited from Darkflight's post.

Jump statements tell the processor to move to a new section of code, instead of simply increment a byte. JML is the 24-bit equivalent of JMP, wheras JMP is only 16-bit. JSR and JSL are the same as their respective counterparts, but they also push the current program counter to the stack (again, 16-bit and 24-bit addresses respectively), before jumping to the desired offset. RTS and RTL are the same as the JMP and JML commands, but instead of getting the destination address from code, they get their destination addresses from the stack.

After JSR and JSL push your original position onto the stack, RTS and RTL take that position and use it to go back once the opcode is finished. If you look at the Blocktool code above, you'll see the opcode RTS at the end of every block. This is what you put at the end of your block code to go back to wherever the code was called from.

RTL and RTS are easy to use because they are only one opcode byte; 6B and 60 respectively. Just place that whenever you want to return from your subroutine. But when jumping you will have to manually choose your address and that is quite a pain to do.

You should get Lunar Address for this. By putting the PC address (the one in your hex editor) in the right box, you can use Lunar Address to translate it from a PC to a SNES address.

But when hex editing you have yet another problem to face. If your SNES address is, say 028787 (not a valid address, don't try it), you will need to change it to 878702 for your Jump Long due to the way the SNES reads addresses.

==The Status Flags==
The 65c816 processor uses 8 flags that determine what certain opcodes do.

There is a carry flag, which adds or subtracts one from your ADC and SBC results (more later).

There is a negative flag, which is used by branch opcodes to check whether or not the number in your accumulator is above or below zero, and by comparison opcodes to check whether or not your accumulator is greater or less than the number you're comparing.

The rest have other purposes, which will likely be discussed here in more detail when I remember what they are.

==The X and Y Registers==

These are like Mini-accumulators. When you don't want to change your accumulator and you don't want to bother with addressing a special spot in RAM, just load your value into your X and Y registers. Also, they REALLY come in handy during indexing.

Indexing is when you take a normal storage opcode and make wherever it's supposed to store its location change based on your X or Y register. (Usually X.)

This means instead of manually telling the processor where to store each variable, you can just increase the X register by 1 and using an indexed storage opcode, it'll place your byte at wherever it would normally store it at + 1.

Be careful when you use these two in custom block code, as they take on other purposes. That's why you should push whatever's in your X and Y registers before use, and pull it once you're done. (see below)

==An explanation of the Stack==
Sukasa's explanation:

The Stack, in SNES programming, is a special memory address that is used for various things. Of these, it (in my experience) is mainly used for JSR and JSL statements, and preserving register states. Think of the stack like a book pile, you can add books (pushing to the stack), or remove a book (But only off of the top!). The stack can be helpful for a reson like this:

You have the accumulator set to $55, but need to use it for math. You have no available RAM, what do you do? Simple, you PusH the Accumulator (PHA), and do your math, storing it to it's RAM location. Now, to get the Accumulator back the way it was, you need to PuLl the Accumulator (PLA). in doing this, you put the Accumulator back to $55, AND did your math without losing any data. See how helpful the stack is?

Back to me again:

It's important to push your Y (and maybe X, too, I'm not so clear on that) registers onto the stack if you plan on using X and Y during blocktool code.

5A (PusH Y) pushes your Y register onto the stack and DA pushes X (PusH X), but it'll mess up your return from subroutine blocktool code if you're not careful. Once you're finished, pull Y again with 7A (PulL Y) and X with FZ (PulL X). Between the push and pull, you can do whatever you want with the registers.

That is, unless you want blocktool to change your type of block. That's what it does with the X and Y addresses. It allows you to manipulate them, and then your block. But if you're just using them for indexing, you can seriously mess up your block because you didn't push first and pull after.

==ADC and SBC==

Written by Sukasa, edited by me

ADC
ADC stands for ADd with Carry. It adds a number to the accumulator, and adds an extra 1 if the carry flag is set.

To use ADC for normal addition, you first want to clear the carry flag with the instruction CLC (CLear Carry flag). After that, you simple use the command
ADC ____, such as ADC #$88 or ADC $1A76. In both of these examples, the value after ADC is added to the accumulator, for the first, that's $88, and for the second that's the value at $1A76 in RAM. Let's say that the accumulator was originally $20, and $1A76 is $10. With the carry flag cleared, the results would be $A8 and $30, respectively. However, if the carry flag is set, then the results would have been $A9 and $31, respectively. See the difference?

When using this opcode, be careful that your result is never greater than FF in hex (255 decimal). If this happens, your number will loop around and become 00 in hex.

SBC
SBC stands for SuBtract with Carry. It is the opposite of ADC, and also affects the accumulator. Like ADC, to subtract properly the carry flag must be cleared (Use CLC). So, with A still being $20, and $1A76 still $10, here are the results for SBC #$18 and SBC $1A76 with the carry flag cleared- $08 and $10, respectively. Although you may guess what SBC will output with the carry flag set, I'll mention it anyways- $07 and $0F, respectively.

When using this opcode, be careful that your result is never less than 0. If it is, it'll loop back around and become FF in hex.

==Opcodes==

A9 - LDA - LoaD into Accumulator, immediate

This takes the number right after A9 and loads it into your accumulator.

AD - LDA - LoaD into Accumulator, absolute

Put a 2-byte RAM address right after AD, and it will be loaded into your accumulator.

8D - STA - STore Accumulator to Memory, absolute

Take what you loaded into your accumulator and store it at the place in ram signified by the next 2 bytes.


(edited by Glyph Phoenix on 06-25-05 08:37 PM)
(edited by Glyph Phoenix on 06-26-05 04:12 PM)
(edited by Glyph Phoenix on 06-27-05 05:17 PM)
(edited by Glyph Phoenix on 08-11-05 10:39 PM)
(edited by Glyph Phoenix on 10-07-05 05:08 PM)
(edited by Glyph Phoenix on 10-07-05 05:32 PM)
(edited by Glyph Phoenix on 10-15-05 10:14 PM)
(edited by Glyph Phoenix on 10-17-05 06:59 PM)
(edited by Glyph Phoenix on 10-17-05 07:37 PM)
Rainbow Yoshi

Level: 30

Posts: 293/496
EXP: 159486
For next: 6383

Since: 04-08-05

Since last post: 14 hours
Last activity: 4 hours
Posted on 06-26-05 05:39 AM Link | Quote
I'm sure this should be stickied to stop all those "HOW DO I LEARN ASM and WHERE CAN I FIND A GOOD TUTORIAL" threads people make.
Alastor the Stylish
Hey! I made a cool game! It's called "I poisoned half the food, so if you eat you might die!" Have a taco.


Level: 114

Posts: 6729/7620
EXP: 16258468
For next: 51099

Since: 03-15-04
From: Oregon, US

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 05:42 AM Link | Quote
That'd be stupid, though, because no one reads the sticky threads.
Glyph Phoenix

Level: 39

Posts: 306/745
EXP: 385876
For next: 18895

Since: 11-07-04

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 05:44 AM Link | Quote
No, no, no, Kyouji. Only the people who don't need to read them read the sticky threads. And now we have a solid defense against asm hacker wannabe lamers. Before it was "Er... we don't actually have a tutorial.... try google" and so the lamers could get off without as much as a lock. Now they'll have no such luck.

And wow, that was a fast sticky.
Alastor the Stylish
Hey! I made a cool game! It's called "I poisoned half the food, so if you eat you might die!" Have a taco.


Level: 114

Posts: 6730/7620
EXP: 16258468
For next: 51099

Since: 03-15-04
From: Oregon, US

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 05:45 AM Link | Quote
Smallhacker's the one on. It must've been him. Now I will kill him.
Smallhacker

Green Birdo

SMW Hacking Moderator
Level: 68

Posts: 1815/2273
EXP: 2647223
For next: 81577

Since: 03-15-04
From: Söderhamn, Sweden

Since last post: 10 hours
Last activity: 9 hours
Posted on 06-26-05 05:46 AM Link | Quote
(Stickied)

Suggestions:
Make a small reference list of the most basic opcodes at the end.
Write somewhere that a memory pointer is simply a RAM address with the bytes flipped.
Rainbow Yoshi

Level: 30

Posts: 294/496
EXP: 159486
For next: 6383

Since: 04-08-05

Since last post: 14 hours
Last activity: 4 hours
Posted on 06-26-05 05:49 AM Link | Quote
Hmm why kill Smallhacker? Kill me! Its like building something nobody reads the manuals same here Newbies dont read the stickies.
Glyph Phoenix

Level: 39

Posts: 307/745
EXP: 385876
For next: 18895

Since: 11-07-04

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 05:58 AM Link | Quote
To JJ, Fu, BMF, Smallhacker, and HH:

You guys know this stuff far better than I do, and you'd probably do a better job editing the tutorial with more information than I would. If any of you guys wanted to add information into that first tutorial post or something, it would be much apprecated.

And to anybody else who has something to add, just PM or mention it in this thread and I'll probably end up adding it sometime.
ExKeeper

Bullet Bill
Level: 31

Posts: 364/512
EXP: 180084
For next: 5279

Since: 03-05-05
From: Riiight ^

Since last post: 1 day
Last activity: 6 hours
Posted on 06-26-05 06:38 AM Link | Quote
you should add BMF's hdma tutorial in that first post.

edit: here is a link: http://board.acmlm.org/thread.php?id=955


(edited by smwedit on 06-25-05 09:42 PM)
Glyph Phoenix

Level: 39

Posts: 308/745
EXP: 385876
For next: 18895

Since: 11-07-04

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 07:09 AM Link | Quote
No. Not yet. Maybe when I have resources to explain HDMA and stuff, but for now I think I'll keep it to simple information and then expand the tutorial to encompass complicated stuff like HDMA.

I mean, I haven't even gotten to BRANCH STATEMENTS yet. I'm not just going to toss HDMA into the tutorial.
Alastor the Stylish
Hey! I made a cool game! It's called "I poisoned half the food, so if you eat you might die!" Have a taco.


Level: 114

Posts: 6731/7620
EXP: 16258468
For next: 51099

Since: 03-15-04
From: Oregon, US

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 07:10 AM Link | Quote
Dammit, Glyph, I wanted to say that in a far more abrasive fashion, but now I can't because it wouldn't make sense.
Sukasa

Boomboom
Error 349857348734534: The system experienced an error.
Level: 57

Posts: 981/1981
EXP: 1446921
For next: 39007

Since: 02-06-05
From: *Shrug*

Since last post: 6 days
Last activity: 1 day
Posted on 06-26-05 08:45 AM Link | Quote
All right, in about 10 minutes I will post 65c816.hlp, which is a complete listing of all the opcodes that the 65c816 is compatible with.
Atma X

Bandit
Level: 43

Posts: 795/801
EXP: 553639
For next: 11407

Since: 03-16-04
From: Derrière vous!!!

Since last post: 43 days
Last activity: 14 days
Posted on 06-26-05 09:48 AM Link | Quote
I have to agree with that this thread won't be extreamly useful,... but for other another reason. The reason is that you can't just learn a few instructions and start programming,... because there's many concepts that needs to be learned,... and most people don't seem to understand them. Most people only seem to understand the easier things,... which makes it quite obvious why very few people here have produced anything that's really worth while with their knowledge of programming,... most of the stuff that I've seen done are things that a person who really understands programming, can do with no hassle, and in only a short bit of time.
Well, this thread may help some determined people get started, but it's not going to make miracles happend anytime soon
(If you want to become a good programmer, spend a lot of time thinking critically about the logic [your tolerance for it shouldn't wear out fast, it should be fun],... and if you can't do that, then you're not determined enough to be a excellent programmer )


(edited by Atma X on 06-26-05 12:53 AM)
HyperLamer
<||bass> and this was the soloution i thought of that was guarinteed to piss off the greatest amount of people

Sesshomaru
Tamaranian

Level: 118

Posts: 5264/8210
EXP: 18171887
For next: 211027

Since: 03-15-04
From: Canada, w00t!
LOL FAD

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 10:59 AM Link | Quote
Also note that memory addresses are byte-swapped. For example, CE BE 0D subtracts one from memory address 7E0DBE. (The 7E isn't necessary, but if you need to use an address that doesn't start with 7E, you need to change the instructions a bit.)

You should also keep in mind that you can't just edit code willy-nilly. For simple things like this, it's alright, but say you wanted to put in a longer instruction. You can't just write it overtop of the other bytes there, or you'll be deleting code which will most likely result in crashing the game. To do this you need to add a subroutine, which is a more advanced topic to be discussed later.
Glyph Phoenix

Level: 39

Posts: 310/745
EXP: 385876
For next: 18895

Since: 11-07-04

Since last post: 2 hours
Last activity: 2 hours
Posted on 06-26-05 04:21 PM Link | Quote
I do plan to get to all that eventually, though. How to use "No operation", EA if you have extra space and how to use subroutines and all that jive.
mikepjr

Ninji
Level: 26

Posts: 159/242
EXP: 92006
For next: 10269

Since: 03-15-04
From: houston texas

Since last post: 4 days
Last activity: 1 hour
Posted on 06-26-05 07:53 PM Link | Quote
Ya know, it's funny.

You guys say that no one will read this and yet im still something of a newbe and i am here reading it.

Go fig.
Dark Ludwig

Red Paratroopa
Level: 21

Posts: 45/172
EXP: 45740
For next: 4203

Since: 09-17-04
From: Georgia

Since last post: 9 days
Last activity: 2 days
Posted on 06-27-05 05:44 AM Link | Quote
I wasn't sure what was going on, but I tried making a custom block that moves mario over about 3 blocks, that's about 0x30 pixels. (Close enough for me, even if not perfect.) I decided to load Mario's X position at 7E0094 into the accumulator, add 30 to it, then store the accumulator's value back at that address. Here's what I made:
|---------------------------------|
|AD940069308D940060|
|---------------------------------|
Now for a better way to view it:

I tried to make this easy to read, I hope it is.

AD 94 00 -- | LDA #0094 | (Load accumulator from memory, 7E0094, which is Mario's X postion)
69 30 -- -- | ADC #$30- | (Add 30 to the accumulator's value)
8D 94 00 -- | STA #0094 | (Store accumulator's value at 7E0094)
60 -- -- -- | RTS ----- | (Return from subroutine)


I hit the plus button in edit block data in blocktool, filled in all the fields after ASM file name with a -1, spelled the ASM file name correctly, gave it a number, opened the rom, chose a map16 number for my block, saved it, stuck it in a level via lunar magic, tested it, and it didn't do anything. Where did I go wrong? Most likely in the actual block ASM, because that's my first block I ever made. But still, I might have gone wrong elsewhere, so please fill me in.

Thanks, if anyone helps me.
Smallhacker

Green Birdo

SMW Hacking Moderator
Level: 68

Posts: 1822/2273
EXP: 2647223
For next: 81577

Since: 03-15-04
From: Söderhamn, Sweden

Since last post: 10 hours
Last activity: 9 hours
Posted on 06-27-05 05:56 AM Link | Quote
Originally posted by dark ludwig
filled in all the fields after ASM file name with a -1

Here's the problem. If you fill everything with -1, nothing will happen. If you want it to activated when touched from above, set the "above" text box to 0, if you want it to be activated from below, set the "below" one to 0, if you want both, set both to 0, and so on.
Dark Ludwig

Red Paratroopa
Level: 21

Posts: 46/172
EXP: 45740
For next: 4203

Since: 09-17-04
From: Georgia

Since last post: 9 days
Last activity: 2 days
Posted on 06-27-05 06:00 AM Link | Quote
WOW that was a quick reply

Anyway, thank you! It now works perfectly!
Koneko

Flurry
Level: 21

Posts: 11/256
EXP: 49237
For next: 706

Since: 06-11-05
From: Tartarus, tartarus, tartarus

Since last post: 9 hours
Last activity: 8 hours
Posted on 06-27-05 06:25 AM Link | Quote
*applauds*

Thank you so much for making this... My n00bness is now on the path to freedom...

Apparently, though, noobies WILL read this... good sign, good sign....
Pages: 1 2 3 4 5Add to favorites | "RSS" Feed | Next newer thread | Next older thread
Acmlm's Board - I2 Archive - Super Mario World hacking - Learning SMW ASM [ASM howto] | |


ABII


AcmlmBoard vl.ol (11-01-05)
© 2000-2005 Acmlm, Emuz, et al



Page rendered in 0.016 seconds.