Actions

SCHG How-to

Difference between revisions of "Work with Motorola 68000 assembly"

From Sonic Retro

(The ADD command)
m (Text replacement - "<asm>" to "<syntaxhighlight lang="asm">")
 
(15 intermediate revisions by 8 users not shown)
Line 1: Line 1:
''(Guide originally written by [[redhotsonic]])''
+
{{GuideBy|redhotsonic}}
  
Welcome, to my ASM guide. It is important to ALWAYS back-up your hack-work before making changes. Plus, I'm using Sonic 2 disassembly, so this guide is based on Sonic 2. If you're hacking Sonic 1 or something else, you can read on, but the activities will be different.
+
Welcome, to my ASM guide. It is important to ALWAYS back-up your hack/work/disassembly before making changes. This will teach you very basic ASM commands what make you aware what ASM is and what a disassembly is used for.
  
 
In this guide, I'll teach you
 
In this guide, I'll teach you
 
* What ASM means
 
* What ASM means
 +
* What a disassembly is used for
 
* The basics of ASM
 
* The basics of ASM
 
* Learning how ASM works
 
* Learning how ASM works
 
* Editing some basics with ASM
 
* Editing some basics with ASM
* Making a special ring for entering special stages
 
  
This guide is for people who know a little ASM or none at all. I've learnt my ASM skills from [[drx]]'s ASM guide, and from [[SMTP]], [[StephenUK]] and [[Ultima]].
+
 
 +
This guide is for people who know a little ASM or none at all. If you have intermediate knowledge of ASM, then you might find a few tips from this guide, but if you're an expert at ASM, then this guide isn't for you because you know all this already.
 +
 
 +
 
 +
 
 +
 
 +
==What is ASM?==
 +
ASM stands for ASseMbly.  It helps builds your commands into a ROM.  Basically, ASM is instead of Hex (Machine Code); it uses words instead of numbers. That's why people use ASM more than Machine Code.
 +
 
 +
For example, this is hex: 11FC 0010 F766.
 +
 
 +
In ASM, it will look like this: move.b #$10,($FFFFF766).w.
 +
 
 +
It's just easier to read for us in words.
 +
 
 +
 
 +
 
 +
 
 +
==What is a disassembly?==
 +
A disassembly is a ROM (or a game) taken apart in displays all its bare code.  It can have separate/multiple data files but only representing one thing.  For example, the ROM will contain art for a character and codes for an object, etc, all in one ROM image/file.  A disassembler will have 1 file for the characters' art, 1 file (or data) for an objects code, etc.  It also means we can edit or add codes and it will shift the rest of the code and the pointers will be changed accordingly.  Whereas if you tried editing the code in the ROM itself, we'll have a fear of over-writing some code as none of the code will shift.  You can shift it yourself, but then all the pointers will need changing.
 +
 
 +
So it's easier to use a disassembler.  But obviously you cannot play the game disassembled.  So, you have to 'build' it.  Once you're happy with your edits, you can click on the .BAT file called 'BUILD'.  What this will do is assemble all those data files and codes into that final ROM image again. And there you have it. Your game is ready to play!
 +
 
 +
 
 +
 
  
 
==Getting started==
 
==Getting started==
Ok, you need all the basics. Here, from the help of Stephen, is everything packed that you'll need. I will be using Nemesis ASM build, so all references will be from his. If you wish to use a different build, you may do so, but not everything may be correct here. The pack I've given contains Nemesis build from drx's site. You'll also need SonED2 (contained in the pack).
+
If you have a disassembly already, you can skip this part. For the majority of the guide, I am going to be using Sonic 2 as the game reference, and Xenowhirls disassembly as the ASM reference.  A list of disassemblies are here on the [[Disassemblies]] page. Or if you want to use the same game and disassembly I am using for this guide, then you can [[Media:Sonic_2_(Split_and_Text_by_Xenowhirl).zip|download it straight from here]]. Once you've downloaded the disassembly, you'll need to split the game (using a plain Sonic 2 ROM, named s2.bin).  Download a Sonic 2 ROM and name it s2.bin and put it in the same folder as the disassembly.  Then, open the .BAT file called SPLIT. You're disassembly is now ready to use!
(Downloads will be here in guide later)
+
 
  
If you get the build and SonED2 separately, firstly, extract everything from the build into a folder. Put your Sonic 2 ROM file in the same folder (must be a clean S2 ROM) and rename it to S2.BIN. Double click on split.bat and a load of folders will be created. Then, extract all the contents from SonED2 into the same folder into the build (say yes to anything it wants to over-write).
 
Once you have everything, we'll start learning.
 
  
==What is ASM?==
 
ASM stands for ASseMbly. It helps build your ROM. Basically, ASM is instead of Hex (Machine Code); it uses words instead of numbers. That's why people use ASM more than Machine Code.
 
  
 
==The basics commands of ASM==
 
==The basics commands of ASM==
Okay, in your folder, look for the S2.asm file and open it with a word editor (I use Notepad). Look at that, there's so much to it, but we can learn it. Close the file down for now, and let's get learning. I'll teach you some ASM commands. Let's go to the first one.
+
Okay, in your folder, look for the S2.asm file and open it with a word editor (I use Notepad2, but the first notepad will be fine). Look at that, there's so much to it, but we can learn it. Close the file down for now, and let's get learning. I'll teach you some ASM commands. Let's get to the basics.
 +
 
 +
 
 +
===The Basic understanding===
 +
<syntaxhighlight lang="asm">TheMoveCommand:
 +
move.w #2,($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
Let's look at the bit where it says move.w.  Move, literally means move. It moves whatever you want to move to a certain point. But what does the .w mean? Well, this means word. You can also get .b (byte) and .l (longword). A byte is two nibbles (00), a word is 4 nibbles (0000), and a longword is 8 nibbles (00000000). The $FFFFFEB8 is a RAM address; these store bytes that you want and can change. You can consider it as a memory. When moving something to a RAM address, it must always be in brackets with a .w afterwards (well, most of the time this is the case, but this is a beginner's guide, so for now, it's always in brackets ending with .w).
 +
 
 +
 
 +
You see the text 'TheMoveCommand:'? This is called a label, helping branches (that will be explained later).
 +
 
 +
 
 +
Okay, so, move.w means it's moving a word. #2 is the byte we want to move, and must always put # in front of the number, so the Megadrive knows it is a decimal number.  Because it's moving a word, that #2 in a word is #0002 (remember, a word is 4 nibbles).  So #2 in 4 nibbles is #0002.  You can put #0002 if you want, but you can ignore the 0's if they come first, so #2 is fine.  If it was #20 we're moving, you'd then have to put either #20 or #0020.  You can't put #2 because it thinks you mean #0002, it doesn't know you want that 0 after the 2.  So putting #20 will be converted to #0020.  If you want #200, you must either put #0200 or #200, etc.
 +
 
 +
If it was to be a move.b, it would only be moving a byte (two nibbles), so #2 would be #02.  If it was to be a move.l, it would only be moving a longword (eight nibbles), so #2 would be #0000002.  Get the idea?
 +
 
 +
 
 +
So # means it's not a memory and is something else,  for most of this guide, a decimal.
 +
 
 +
So # is decimal, so, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13...
 +
 
 +
If you add a dollar sign so you get this: #$2, it will then be treated as hexademical.  So basically:
 +
 
 +
The #$ means hexadecimal, so, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13...
 +
 
 +
 
 +
The disassembler will always convert it into a hexadecimal number either way when you build.  But #2 converted to hex is still #$2.  If you put #9, it will be converted to hex, which is still #$9.  But if you put #10, then when building, that will change to #$A.  Using the numbers I just put above, can you see why?  So if you put #12, it will be converted to $C.  If you actually want it as 12, then you must put #$12, because that way, you've made it hexadecimal yourself, and when building, it will stay as $12.
 +
 
  
 
===The MOVE command===
 
===The MOVE command===
<asm>move.w #2,($FFFFFEB8).w</asm>
 
  
Let's look at the bit where it says move.w. Move, literally means move. It moves whatever you want to move to a certain point. But what's the .w mean? Well, this means word. You can get .b (byte) and .l (longword). A byte is two nibbles (00), a word is 4 nibbles (0000), and a longword is 8 nibbles (00000000). The $FFFFFEB8 is a RAM address; these store bytes that you want and can change. You can consider it as a memory. When moving something to a RAM address, it must always be a word (I highlighted it blue, in case you do not know what I mean). You see the text in RED saying 'The MOVE command: '? This is called a label, helping branches (that will be explained later). So, this command, what is it doing?
+
Anyway, this command, what is it doing?
 +
 
 +
<syntaxhighlight lang="asm">TheMoveCommand:
 +
move.w #2,($FFFFFEB8).w</syntaxhighlight>
  
<asm>move.w #2,($FFFFFEB8).w</asm>
 
  
Well, let's go through it, move.w means it's moving a word. #2 is the byte we want to move, and must always put # in front of the number, so Genesis knows it is a byte. And the $FFFFFEB8 is the address we want it to go and you must always put a $ in front of the address, so Genesis knows it's an address.. Let's say that at address $FFFFFEB8 it is 00 00. After the command above, it'll become 00 02.
+
The $FFFFFEB8 is the address we want it to go. The $ sign indicates the number is hexadecimal, and memory addresses are always represented that way. Let's say that at address $FFFFFEB8 it is #$0000. After the command above, it'll become #$0002.  So before the command, ($FFFFFEB8).w equalled #$0000.  After the command, ($FFFFFEB8).w equals #$0002.
  
Do you see how it works? No? Let's do another example.
 
  
Pretend $FFFFFEB8 is: 00 02
+
Do you see how it works?  No?  Let's do another example.
 +
 
 +
Pretend $FFFFFEB8 is: #$0002
 +
 
  
 
Let's say we want that 02 to be an 08, we do this:
 
Let's say we want that 02 to be an 08, we do this:
  
<asm>move.w #8,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> move.w #8,($FFFFFEB8).w</syntaxhighlight>
  
Now, $FFFFFEB8 is: 00 08.
+
Now, $FFFFFEB8 is: #$0008.
  
It works. Here's a longword example. It works the exact same way.
 
  
Pretend $FFFFFE10 is: 00 00 00 00
+
It works.  Here's a longword example.  It works the exact same way.
  
<asm>move.l #6,($FFFFFE10).w</asm>
+
Pretend $FFFFFE10 is: #$00000000
  
Now, $FFFFFE10 is: 00 00 00 06
+
<syntaxhighlight lang="asm"> move.l #6,($FFFFFE10).w</syntaxhighlight>
  
Let's make things slightly more difficult. Let's say that $FFFFFFE6 is 02 03 04 05. What would this become if we did this command?
+
Now, $FFFFFE10 is: #$00000006
  
<asm>move.w #4,($FFFFFFE6).w</asm>
 
  
Well, $FFFFFFE6 will become 00 04 04 05. The 00 04 in RED is what you just did. Remembering that .w is a word (4 nibbles, 00 00), and there is 8 nibbles to this address, it only does the first four nibbles. If we did:
+
But remember that RAM addresses are treated as a hexadecimal number all the time. So for this next example, pretend $FFFFFE10 is: #$00000000
<asm>move.b #4,($FFFFFFE6).w</asm>
 
$FFFFFFE6 would become 04 03 04 06, as the 04 in RED is what you just did.
 
  
NOTE: It is not recommended to pick any old RAM address, as it may be being used elsewhere. For a list of addresses, look [[SCHG:Sonic_2#Main_System_Memory_Locations|here.]]
+
<syntaxhighlight lang="asm"> move.l #10,($FFFFFE10).w</syntaxhighlight>
All commands can have comments (it doesn't HAVE to have a comment). This is if you want to remember what this command does. For example:
 
  
<asm>move.b #4,($FFFFFFE6).w ; moves the byte 04 to the address shown</asm>
 
  
If you want to add comments after a command, you MUST put the Semicolon symbol ( ; ) after the command, then put whatever you want.
+
We're moving a decimal number #10 to the RAM, but the RAM will treat it as a hexadecimal, so after this command, $FFFFFE10 will be #$0000000A.  Remembering that the RAM address is hexadecimal, so it would have gone 8, 9, A, B...  NOT 8, 9, 10, 11...
  
There are more MOVE commands, but I wouldn't worry about them for now. Here they are if you ever look back here:
 
  
<center>MOVE
+
If you want that RAM to be 10, then you have to treat the number you're moving as a hexadecimal number.  So pretend $FFFFFE10 is: #$00000000, after this next command:
MOVE
 
  
MOVEQ
+
<syntaxhighlight lang="asm"> move.l #$10,($FFFFFE10).w</syntaxhighlight>
MOVE Quick
 
  
MOVEM
 
MOVE Multiple
 
  
MOVEA
+
$FFFFFE10 will be #$00000010.  This is because you've converted the number to hexadecimal yourself; you moved #$10 to the RAM.  Hope this makes sense!
MOVE Address
+
 
</center>
+
 
 +
 
 +
Let's make things slightly more difficult. Let's say that $FFFFFFE6 is #$02030405.  What would this become if we did this command?
 +
 
 +
<syntaxhighlight lang="asm"> move.w #4,($FFFFFFE6).w</syntaxhighlight>
 +
 
 +
 
 +
Well, $FFFFFFE6 will become #$00040405. The first four nibbles (#0004) is what you just did.  Remembering that the move.w is moving a word (4 nibbles, #0000), and there is 8 nibbles to this RAM address, it only does the first four nibbles. The last 4 nibbles in the RAM address remain unaffected because we only moved a word.  If we did:
 +
 
 +
<syntaxhighlight lang="asm"> move.l #4,($FFFFFFE6).w</syntaxhighlight>
 +
 
 +
$FFFFFFE6 would become #$00000004, because you moved a longword (8 nibbles).
 +
 
 +
 
 +
 
 +
As a sidenote, remember that the smallest unit the MC68000 can address is a byte, so each RAM address refers to a byte. As an example:
 +
 
 +
<syntaxhighlight lang="asm">$FFFFFE00 = #$12
 +
$FFFFFE01 = #$34
 +
$FFFFFE02 = #$56
 +
$FFFFFE03 = #$78</syntaxhighlight>
 +
 
 +
Each address here is being treated as a byte. But, if we decided to use one of these addresses as a word:
 +
 
 +
<syntaxhighlight lang="asm"> move.w #5,($FFFFFE00).w</syntaxhighlight>
 +
 
 +
Then the above four addresses will look like this:
 +
 
 +
<syntaxhighlight lang="asm">$FFFFFE00 = #$00
 +
$FFFFFE01 = #$05
 +
$FFFFFE02 = #$56
 +
$FFFFFE03 = #$78</syntaxhighlight>
 +
 
 +
Note that moving a word to $FFFFFE00 affected both $FFFFFE00 and $FFFFFE01. Additionally, if $FFFFFE00 was interpreted as a longword, then its original value was $12345678, and its new value is $00055678. Since we only wrote a single word, $FFFFFE02 and $FFFFFE03 remain unaffected.
 +
 
 +
 
 +
NOTE: It is not recommended to pick any old RAM address, as it may be being used for specific things.  For a list of addresses and what they are used for in Sonic 2, [[SCHG:Sonic_2#Main_System_Memory_Locations|look here]].
 +
 
 +
 
 +
All commands can have comments (it doesn't HAVE to have a comment).  This is if you want to remember what this command does. For example:
 +
 
 +
<syntaxhighlight lang="asm"> move.b #4,($FFFFFFE6).w ; moves the byte 04 to the address shown</syntaxhighlight>
 +
 
 +
If you want to add comments after a command, you MUST put the Semicolon symbol ( ; ) after the command, then put whatever you want, as long as it stays on the same line.  You can literally put any comment you want:
 +
 
 +
<syntaxhighlight lang="asm"> move.b #4,($FFFFFFE6).w ; moves 4 to the 'I am Super' RAM thingamajig, wazza wazza!</syntaxhighlight>
 +
 
 +
Comments are useful if you want to remember what the command is doing or just to set some notes.
 +
 
 +
 
 +
The MOVE command isn't just restricted to moving numbers to RAM addresses.  You can move a RAM address to another for example:
 +
 
 +
 
 +
<syntaxhighlight lang="asm"> move.w ($FFFFFFE6).w,($FFFFFFE8).w ; copy RAM address to another</syntaxhighlight>
 +
 
 +
 
 +
It's simple.  But you MUST remember, RAM addresses are treated as hexadecimal all the time.  Imagine $FFFFFFE6 is #$9AB6, and $FFFFFFE8 is #$1234.  If you move $FFFFFFE6 to $FFFFFFE8, $FFFFFFE8 will now be #$9AB6.  $FFFFFFE6 will also remain the same as you haven't told anything to move to that.  So basically, you've just copied it.  Another example:
 +
 
 +
<syntaxhighlight lang="asm"> move.l d0,($FFFFFFE8).w ; move data register to RAM address</syntaxhighlight>
 +
 
 +
 
 +
d0 is a data register (hexadecimal again), but we'll explain what it's used for a little bit later on, but for now, imagine d0 is #$01234567.  And $FFFFFFE8 is #$55555555.  So, after this command, $FFFFFFE8 will be #$01234567.  d0 will still stay the same obviously.
 +
 
 +
 
 +
You don't have to move things to RAM address either:
 +
 
 +
<syntaxhighlight lang="asm"> move.w #$2534,d0 ; move this hexadecimal number to d0</syntaxhighlight>
 +
 
 +
d0 will become #$2534 after the above command.
 +
 
 +
 
 +
 
 +
There are more MOVE commands, but I wouldn't worry about them for now as this is only a basic guide.  But here they are and a small description on what they do:
 +
 
 +
<center>moveq / MOVE Quick / Quickly moves a hex number to a data register faster than move.l but can only be used from -127 up to 128 (decimals) and moved to data registers only.
 +
 
 +
 
 +
movem / MOVE Multiple / Can move data to multiple addresses.  An example is it can move data from an address register to d0 and d1 and d2, etc.
 +
 
 +
movea / MOVE Address / Moves registers to other registers</center>
  
 
===The ADD command===
 
===The ADD command===
<asm>add.b #2,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> addi.b #9,($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
This is pretty much the same as the command MOVE, but it adds instead.  You're probably asking, what does that i stand for in addi?  i stand for immediate.  No, it does not mean it will immediately do the command, it means it will add a decimal/hexadecimal number to the destination.  So if you want to add a number to something, it's addi. If you want to add a RAM or register to something, then it's just add.  REMEMBER, RAM addresses are always treated as hexadecimal.
 +
 
 +
Anyway, let's say $FFFFFEB8 is #$01010101 and then we did this command above; it'll become #$0A010101.  It's A because it's hex, remember?  We only added a byte, so it only does the first 2 nibbles.  Do you see how it works? Here's another example.
 +
 
 +
 
 +
Assuming that $FFFFFEB8 is #$01040202, after executing the below command:
 +
 
 +
<syntaxhighlight lang="asm"> addi.w #9,($FFFFFEB8).w ; add the word 09 to $FFFFFEB8</syntaxhighlight>
 +
 
 +
It'll become #$010D0202.  Because we added a word.  9 in a word is #0009, it's only doing the first 4 nibbles in the address, so #$0104 add #0009 is #$010D.  Remembering that the RAM address is hexadecimal, so it would of gone 8, 9, A, B, C, D.  NOT 8, 9, 10, 11, 12, 13.
 +
 
 +
 
 +
Again, it doesn't need to be numbers, you can do:
 +
 
 +
<syntaxhighlight lang="asm"> add.w ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</syntaxhighlight>
 +
 
 +
Now, because we're not adding a number, it's no longer addi and just plain-old add.  So, if $FFFFFFE6 is #$0003 and $FFFFFFE8 is #$0008, after this command, $FFFFFFE8 will be #$000B.  Yes, B, because it's hexadecimal.  So it would of gone 8, 9, A, B.  NOT 8, 9, 10, 11.  Otherwise, it's as simple as that.  It is important for you to know that $FFFFFFE6 will stay as #$0003 seeming as we haven't told it to change.
 +
 
 +
 
 +
Another example:
 +
 
 +
<syntaxhighlight lang="asm"> add.w d0,($FFFFFFE8).w ; add data register to a RAM address</syntaxhighlight>
 +
 
 +
If d0 is #$1234 and $FFFFFFE8 is #$AAAA, after the command, $FFFFFFE8 will be #$BCDE.  d0 will obviously stay as #$1234.
 +
 
 +
 
 +
 
 +
Here is an extra command, the addq command:
 +
 
 +
<syntaxhighlight lang="asm"> addq.b #6,($FFFFFEB8).w ; add 6 quickly to the RAM address</syntaxhighlight>
 +
 
  
This is pretty much the same as the command MOVE, but it adds instead. Example, let's say $FFFFFEB8 is 01 01 01 01 and then we did this command above, it'll become 03 01 01 01. Do you see how it works? One more example.
+
addq is the fastest way to add and can be used on any  destination (pretty much).  But the problem is, it can only be used when adding a number between 1 and 8. So, if you want to add 6 to a RAM address, then addq is better to use than addi. If you want to add 3 to a RAM address, then:
  
<asm>add.w #4,($FFFFFEB8).w ; going to add the word 04 to $FFFFFEB8</asm>
+
<syntaxhighlight lang="asm"> addq.b #3,($FFFFFEB8).w ; add 3 quickly to the RAM address</syntaxhighlight>
  
$FFFFFEB8 is now 01 05 01 01.
 
  
Here is an extra command:
+
But however, if you want to add 9 (or more) to a RAM address, then addi has to be used:
<center>
+
 
ADD
+
<syntaxhighlight lang="asm"> addi.b #9,($FFFFFEB8).w ; add 9 to the RAM address</syntaxhighlight>
ADD
+
 
 +
If you're adding a RAM address to another like so:
 +
 
 +
<syntaxhighlight lang="asm"> add.w ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</syntaxhighlight>
 +
 
 +
it doesn't matter if $FFFFFFE6 is between 1 - 8, you cannot use the addq.  addq is literally only used when adding a number itself to the  destination.  As it's not a number and it's a RAM address, it's not addi either, it's just add.
 +
 
  
ADDQ
 
ADD Quick
 
</center>
 
  
 
===The SUB command===
 
===The SUB command===
<asm>sub.b #3,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> subi.b #$12,($FFFFFEB8).w</syntaxhighlight>
  
 
This is nearly the same as the add command, except we're going the other way. Instead of adding, we're subtracting. I'll only give one example here, as this is pretty straight forward.
 
This is nearly the same as the add command, except we're going the other way. Instead of adding, we're subtracting. I'll only give one example here, as this is pretty straight forward.
  
Pretend $FFFFFEB8 is: 09 09 09 09
+
Pretend $FFFFFEB8 is: #$15161718
 +
 
 +
<syntaxhighlight lang="asm">
 +
subi.b #$12,($FFFFFEB8).w ; going to take the byte $12 from $FFFFFEB8</syntaxhighlight>
 +
 
 +
Now $FFFFFEB8 is: #$03161718
 +
 
 +
 
 +
Again, it doesn't need to be numbers, you can do:
 +
 
 +
<syntaxhighlight lang="asm"> sub.b ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</syntaxhighlight>
 +
 
 +
 
 +
If $FFFFFFE6 is #$6 and $FFFFFFE8 is #$8, after the command, $FFFFFFE8 will be #$2.  $FFFFFFE6 will remain as #$6 obviously.  Remember that for these type of commands, it's sub and not subi.
 +
 
 +
 
 +
There is another command, subq.  It works exactly the same way ass addq, but subtracting instead of adding.  Again, only numbers, and only between 1 - 8.
 +
 
 +
 
 +
 
 +
===Number formats and prefixes===
 +
 
 +
I've already explained the basic two; decimal and hexadecimal.  There are four ways in total to represent a number in ASM:
 +
 
 +
*Decimal (no prefix) - Digits are 0-9. This is what humans are used to reading.
 +
*Binary (%) - Each digit represents one bit, which can be 1 (on) or 0 (off). '%10' in binary is the same as '#2' in decimal.
 +
*Hexadecimal ($) - Each digit is one nibble (4 bits) and ranges from $0-$F. '#$10' in hexadecimal is the same as '#16' in decimal
 +
*Octal (@) - 3 bits. Numbers range from 0-7. This is rarely used if ever. '@10' is the same as '#8'
 +
 
 +
The # is special and basically means the number after it is NOT a memory address. It can be used in conjunction to the above as follows, assuming $FFFFFEB8 is #$0000:
 +
 
 +
<syntaxhighlight lang="asm"> move.w #$FF,($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
$FFFFFEB8 becomes: #$00FF, the #$FF would be the same as inputting #255:
  
<asm>sub.b #3,($FFFFFEB8).w ; going to take the byte 03 from $FFFFFEB8</asm>
+
<syntaxhighlight lang="asm"> move.w #255,($FFFFFEB8).w</syntaxhighlight>
  
Now $FFFFFEB8 is: 06 09 09 09
+
Using either of these commands, ($FFFFFEB8).w will be #$FF.
  
Here is an extra command:
 
<center>
 
SUB
 
SUBtract
 
  
SUBQ
 
SUBtract Quick
 
</center>
 
  
 
==Data and address registers==
 
==Data and address registers==
Data registers are temporarily data holders (like memory again). It will remember a byte you told it to remember. All data registers are long words, and these types of registers exist from d0-d7. Example.
+
Data registers are temporarily data holders (like memory again). It will remember what you told it to remember. All data registers are longwords (whereas RAM addresses were bytes), and these types of registers exist from d0-d7. Example:
 +
 
 +
d0: #$00000000
  
d0: 00 00 00 00
+
<syntaxhighlight lang="asm"> move.b #3,d0 ; going to move 03 to the data register 0</syntaxhighlight>
  
<asm>move.b #3,d0 ; going to move 03 to the data register 0</asm>
+
d0: #$00000003
  
d0: 03 00 00 00
 
  
Until you tell it to change, that'll stay like that. Like said, it doesn't necessarily have to be d0, it can be... I don't know, d4. You can make d0 go to other places too.
+
Until you tell it to change, that'll stay like that. Like said, it doesn't necessarily have to be d0, it can be... I don't know... d4? You can make d0 go to other places too.
 +
 
 +
<syntaxhighlight lang="asm"> move.b d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</syntaxhighlight>
 +
 
 +
d0 will still be #$00000003, but $FFFFFEB8 will be #$03000000.
 +
 
 +
 
 +
If the command was:
 +
 
 +
<syntaxhighlight lang="asm"> move.w d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</syntaxhighlight>
 +
 
 +
d0 will still be #$00000003, but $FFFFFEB8 will be #$00030000.
 +
 
 +
 
 +
If the command was:
 +
 
 +
<syntaxhighlight lang="asm"> move.l d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</syntaxhighlight>
 +
 
 +
d0 will still be #$00000003, but $FFFFFEB8 will be #$00000003.
 +
 
 +
 
 +
 
 +
Address registers are also data holders, but these normally hold pointers, but can store data. These exist from a0 to a7, but avoid using a7 as it is a stack holder, which I will not cover in this basic guide but anyway, don't use it.  We'll cover about the address registers a little bit later.
  
<asm>move.b #d0,($FFFFFEB8) ; going to move the data from d0 to $FFFFFEB8</asm>
 
  
d0 will still be 03 00 00 00, but $FFFFFEB8 will also be 03 00 00 00.
 
  
Address registers are also data holders, but these normally hold pointers, but can store data. These exist from a0 to a7, but avoid using a7 as it is a stack holder. Stack is a reserved data of the RAM for temporary things and I'm not quite sure what, so to be on the safe side, don't use it.
 
  
 
==More ASM commands==
 
==More ASM commands==
===The CMP command:===
+
===The CMP command===
<asm>cmp.w #3,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> cmpi.w #3,($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
Ah, the COMPARE command. This just compares the byte you've put to the address. In this case, the word 03 to $FFFFFEB8. If $FFFFFEB8 is #$00030708, this equals the word #$0003. If $FFFFFEB8 was #$05030000, it would not equal the word #$0003 (because it's #$0503).  Again, the i (immediate) is here.  Same rules apply like the addi/subi.  If it's a number you're comparing, it's cmpi, otherwise, it's cmp.  Example:
 +
 
 +
<syntaxhighlight lang="asm"> cmpi.w #3,d0 ; See if d0 equals #0003 (it's cmpi here)</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="asm"> cmp.w d0,d1 ; See if d0 equals the same as d1 (it's cmp here)</syntaxhighlight>
 +
 
 +
 
 +
There is no such thing as cmpq though.
 +
 
 +
 
 +
 
 +
===The TST command===
 +
<syntaxhighlight lang="asm"> tst.w ($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
tst stands for TeST.  This is a disguise for CMP.  Basically, 'tst' is comparing to see if the destination equals 0.  Basically:
 +
 
 +
<syntaxhighlight lang="asm"> tst.w ($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
Is the EXACT same as:
 +
 
 +
<syntaxhighlight lang="asm"> cmpi.w #0,($FFFFFEB8).w</syntaxhighlight>
 +
 
 +
 
 +
The reason why you want to use tst instead of cmp is when seeing if something equals #0 or not, is because tst is slightly faster.  You can consider this as 'cmpq', but only works on #0.
 +
 
  
Ah, the COMPARE command. This just compares the byte you've put to the address. In this case, the word 03 to $FFFFFEB8. If $FFFFFEB8 is 00 03 07 08, this equals the word 00 03. If $FFFFFEB8 was 05 03 00 00, it would not equal the word 00 03.
 
  
 
===The BRA commands===
 
===The BRA commands===
This, is the BRANCH command. There are many branch commands, but we're going to take it one step at a time. The command BRA stands for BRanch Anyway. This is what you want Genesis to branch (or go) to when you use this command. What do I mean? Well, take a look at this.
+
This, is the BRANCH command. There are many branch commands, but we're going to take it one step at a time. The command BRA stands for BRanch Anyway. This is what you want the MegaDrive to branch (or go) to when you use this command. What do I mean? Well, take a look at this.
  
The CMP:
+
<syntaxhighlight lang="asm">TheCode:
<asm>move.w #3,($FFFFFEB8).w
+
move.w #3,($FFFFFEB8).w
add.w #3,($FFFFFF10).w
+
addi.w #3,($FFFFFF10).w
bra Morecodinghere
+
bra.s Morecodinghere
sub.w #3,($FFFFFEB8).w
+
subi.w #3,($FFFFFEB8).w
  
 
Morecodinghere:  
 
Morecodinghere:  
move.w #3,($FFFFFEB8).w
+
move.w #3,($FFFFFEBA).w
rts</asm>
+
rts</syntaxhighlight>
  
You're probably thinking, 'What the hell?' Do not panic. Let's go through this. First, it's saying it's moving the word 00 03 to $FFFFFEB8. Then it's adding the word 00 03 to $FFFFFF10. Here is when our new command, BRA, comes in. It's saying to branch (go to) 'Morecodinghere'. As you can see, there is a label called Morecodinghere. So, when it sees this, it goes to this label. So, the next thing it does, it is moving 00 03 to $FFFFFEB8.
+
You're probably thinking, 'What the hell?' Do not panic. Let's go through this. First, it's saying it's moving the word #0003 to $FFFFFEB8. Then it's adding the word #0003 to $FFFFFF10. Here is when our new command, BRA, comes in. It's saying to branch (go to) 'Morecodinghere'. As you can see, there is a label called 'Morecodinghere'. So, when it sees this, it goes to this label. Remember me mentioning about labels earlier?  This is what labels are useful for!  So now, we're starting from the label 'Morecodinghere'.  So, the next thing it does, it is moving #0003 to $FFFFFEBA.
  
The sub.w command was ignored. This is because it got branched. If you took the BRA command out, it would do that SUB command.
+
The subi.w command was ignored. This is because it got branched. If you took the BRA command out, it would do that SUBI command.
  
 
In the Morecodinghere label, you can see underneath its MOVE command, is a new command:
 
In the Morecodinghere label, you can see underneath its MOVE command, is a new command:
  
===The RTS command===
 
This means 'Return To Subroutine'. So, in that code, it's telling to go back to the label, 'The CMP:'. It is important to put RTS. If you do not, it will carry on.
 
  
<asm>Morecodinghere:
 
move.w #3,($FFFFFEB8).w
 
rts
 
Sumrandomcode:
 
add.w #3,($FFFFFEB8).w</asm>
 
  
Because of that RTS command, the label 'Sumrandomcode' is being ignored. If the RTS command was not there, the 'Sumrandomcode' label would take place.
+
===The RTS and BSR command===
 +
RTS means 'ReTurn from Subroutine'. So, it's telling to go back to where this subroutine was first executed.  Let's look at this example:
 +
 
 +
<syntaxhighlight lang="asm">Somecodes:
 +
move.w #3,($FFFFFEB8).w
 +
addi.w #3,($FFFFFF10).w
 +
bsr.s Morecodinghere2 ; *
 +
subi.w #3,($FFFFFEB8).w
 +
rts
 +
 
 +
Morecodinghere2:
 +
move.w #3,($FFFFFEBA).w
 +
rts</syntaxhighlight>
 +
 +
 +
For this example, that bra.s has changed to bsr.s.  BSR stand for 'Branch to SubRotuine'.  This means to branch to a new code, but remember to come back (in simple terms).  So let's follow this code.  First, it moves #0003 to $FFFFFEB8, and then adds #0003 to $FFFFFF10, then the new command, bsr.  This means, go to the label 'Morecodinghere2' and return to me.  So, go to 'Morecodinghere2', next command is to move #0003 to $FFFFFEBA. Next is 'RTS', which is telling you to return from this subroutine.  Basically, where the '*' is in the comments.  So now, you're back there.  Next command is to subtract #0003 from $FFFFFEB8.  Then finally, rts again, and that will return to whatever made the code jump to the label 'Somecodes'.
 +
 
 +
I hope that makes sense.
 +
 
 +
 
 +
So far, you've been introduced to BRA and BSR.  You might have seen me put a .s after these.  The s stands for short.  Try to add .s to the end of each branch whenever possible.  But hence the name short, so it can only branch to short distances.  If you tried making it branch to a label far away, .s might not work.  If so, use .w (which stands for word) like so:
 +
 
 +
<syntaxhighlight lang="asm"> addq.w #3,($FFFFFF10).w
 +
bra.w afarawaylabel
 +
subi.w #$13,($FFFFFEB8).w</syntaxhighlight>
 +
 +
.s and .w are just as quick as each other, but .s is smaller in size, saving you space.  So use .s when you can and if the branch is too long, use .w.  Sometimes, even a .w can't reach, and if so, then you use:
 +
 
 +
 
 +
 
 +
===The JMP and JSR commands===
 +
 
 +
Whereas BSR stood for 'Branch to SubRoutine', JSR stands for 'Jump to SubRoutine' and JMP literaly stands for Jump.  These two commands can jump almost anywhere.  These commands do not need .s or .w at the end.  So, you can use them in the same way:
 +
 
 +
<syntaxhighlight lang="asm"> add1.w #3,($FFFFFF10).w
 +
jmp afarfarfarawaylabel
 +
subi.w #$13,($FFFFFEB8).w</syntaxhighlight>
 +
 +
JMP and JSR uses more space and time to perform than BRA and BSR, so try to use the BRA/BSR.  Remember:
 +
 
 +
bra.s > bra.w > jmp
 +
 
 +
bsr.s > bsr.w > jsr
 +
 
 +
 
 +
Here are two new branch commands:
 +
 
  
Here's two new branch commands
 
  
 
===The BEQ and BNE commands===
 
===The BEQ and BNE commands===
BEQ stands for Branch if EQual. BNE stands for Branch if Not Equal. This is where the compare command comes into use. Take a look at this.
+
BEQ stands for 'Branch if EQual'. BNE stands for 'Branch if Not Equal'. This is where the compare command (cmp/cmpi) comes into use. Take a look at this.
  
<asm>BEQlabel:
+
<syntaxhighlight lang="asm">BEQlabel:
cmp.w #3,($FFFFFEB8).w
+
cmpi.w #3,($FFFFFEB8).w
beq Itequaled
+
beq.s Itequaled
sub.b #2,($FFFFFEB8).w
+
subi.b #$B,($FFFFFEB8).w
 +
rts
  
 
Itequaled:
 
Itequaled:
add.b #2,($FFFFFEB8).w
+
addq.b #2,($FFFFFEB8).w
rts</asm>
+
rts</syntaxhighlight>
  
Let's go through this. First we're comparing if $FFFFFEB8 equals 00 03. Then we've got our BEQ command. Basically, it is saying if $FFFFFEB8 DID equal 00 03, then branch to the label 'Itequaled'. Now, because it branched, the sub.b command is ignored and it goes to the label 'Itequaled'. If $FFFFFEB8 did NOT equal 00 03, it will carry on and do the sub.b command.
+
Let's go through this. First we're comparing if $FFFFFEB8 equals #0003. Then we've got our BEQ command. Basically, it is saying if $FFFFFEB8 DID equal #0003, then branch to the label 'Itequaled'. Now, because it branched, the subi.b command is ignored and it goes to the label 'Itequaled'. If $FFFFFEB8 did NOT equal #0003, it will carry on and do the subi.b command instead.
  
<asm>BNElabel:
+
<syntaxhighlight lang="asm">BNElabel:
cmp.w #3,($FFFFFEB8).w
+
cmpi.w #3,($FFFFFEB8).w
bne Itequaled
+
bne.s Itdidnotequal
sub.b #2,($FFFFFEB8).w
+
subi.b #$B,($FFFFFEB8).w
  
Itequaled:  
+
Itdidnotequal:  
add.b #2,($FFFFFEB8).w
+
addq.b #2,($FFFFFEB8).w
rts</asm>
+
rts</syntaxhighlight>
  
Now, look at this. If $FFFFFEB8 did NOT equal 00 03, it WILL branch, as we've used the BNE command. However, if it DID equal 00 03, it will carry on and do the sub.b command. I hope you understand this. Here is a test. For this test, pretend that $FFFFFF10 equals 00 00 00 00.
+
Now, look at this. If $FFFFFEB8 did NOT equal #0003, it WILL branch, as we've used the BNE command. However, if it DID equal #0003, it will NOT branch and will carry on and does the subi.b command. I hope you understand this. Here is a test. For this test, pretend that $FFFFFF10 equals #$00000000.
  
<asm>New label:
+
<syntaxhighlight lang="asm">Newlabel:
move.w #3,($FFFFFF10).w
+
move.w #3,($FFFFFF10).w
add.w #3,($FFFFFF10).w
+
addq.w #3,($FFFFFF10).w
cmp.w #6,($FFFFFF10).w
+
cmpi.w #6,($FFFFFF10).w
bra Morecodinghere
+
beq.s Morecodinghere
sub.w #3,($FFFFFF10).w
+
subq.w #3,($FFFFFF10).w
  
 
Morecodinghere:
 
Morecodinghere:
move.w #3,($FFFFFF10).w
+
move.w #3,($FFFFFF10).w
rts</asm>
+
rts</syntaxhighlight>
 +
 
 +
Here's the question; In 'New Label', does it branch to 'Morecodinghere'? The answer is yes. I'm not going to explain why it does; study it. If you still do not know why, go back to revise.
 +
 
  
Here's the question; In 'New Label', does it branch to 'Morecodinghere'? The answer, is yes. I'm not going to explain why it does; study it. If you still do not know why, go back to revise.
 
  
 
There are more BRANCH commands. Here they are, but I'm not going to explain, as you should get the idea.
 
There are more BRANCH commands. Here they are, but I'm not going to explain, as you should get the idea.
 +
 
<center>
 
<center>
BRA
+
BGE / Branch if Greater or Equal / If it equals, or is greater than the equal amount, branch
BRanch Anyway
 
  
BSR
+
BGT / Branch if Greater Than / If it is greater than the equal amount, branch
Branch to SubRoutine
 
  
BEQ
+
BLE / Branch if Less or Equal / If it equals, or is less than the equal amount, branch
Branch if EQual
 
  
BNE
+
BLT / Branch if Less Than / If it is less than the equal amount, branch</center>
Branch if Not Equal
 
  
BGE
+
I hope this makes sense. There are more branches, but it’s not relevant to the basic guide.
Branch if Greater or Equal
 
  
BGT
 
Branch if Greater Than
 
  
BLE
 
Branch if Less or Equal
 
  
BLT
+
===The LEA command===
Branch if Less Than
+
LEA stands for Load Effective Address. You can load an address to an address register.  This is where a0 to a6 becomes useful (briefly  talked about them earlier). Example:
</center>
+
 
 +
<syntaxhighlight lang="asm"> lea ($FFFFFF10).w,a4</syntaxhighlight>
 +
 
 +
Now $FFFFFF10 will be in address register a4.  Or another way of putting it, a4 contains $FFFFFF10.  This can be useful if you want to do a lot of calculations to one RAM address.  Example:
 +
 
 +
<syntaxhighlight lang="asm"> move.b #3,($FFFFFF10).w
 +
addq.b #2,($FFFFFF10).w
 +
cmpi.b #1,($FFFFFF10).w
 +
beq.s Gotolabelifequaled
 +
addi.b #$43,($FFFFFF10).w
 +
cmpi.b #$48,($FFFFFF10).w
 +
beq.s Anotherlabel
 +
rts</syntaxhighlight>
 +
 
 +
 
 +
This is moving/adding/comparing $FFFFFF10 quite a bit.  While there is no problem with this, there can be a quicker way.  This is when address registers and the LEA command can help.  If we load $FFFFFF10 into a1 for example, we can do this instead:
 +
 
 +
<syntaxhighlight lang="asm"> lea ($FFFFFF10).w,a1
 +
move.b #3,(a1)
 +
addq.b #2,(a1)
 +
cmpi.b #1,(a1)
 +
beq.s Gotolabelifequaled
 +
addi.b #$43,(a1)
 +
cmpi.b #$48,(a1)
 +
beq.s Anotherlabel
 +
rts</syntaxhighlight>
 +
 
 +
Basically, (a1) has taken ($FFFFFF10).w's place.  This is a much quicker way, as ASM can do calculations quicker to an address register than to a RAM address.  The charm is, after these commands, $FFFFFF10 will still have these calculations applied to it.  So at the end, both (a1) and ($FFFFFF10).w will equal #$48.
 +
 
 +
 
 +
If however, you want to do these commands above, but you don't want $FFFFFF10 to have any changed effects, then data registers are the answer:
  
I hope this makes sense. There are more branches, but I do not understand them myself.
+
<syntaxhighlight lang="asm"> move.b ($FFFFFF10).w,d0
 +
move.b #3,d0
 +
addq.b #2,d0
 +
cmpi.b #1,d0
 +
beq.s Gotolabelifequaled
 +
addi.b #$43,d0
 +
cmpi.b #$48,d0
 +
beq.s Anotherlabel
 +
rts</syntaxhighlight>
  
===The JMP command===
 
JMP stands for JuMP. It does exactly the same as BRA, but it can jump to any area in the ROM. You see, Branch can't go anywhere, but it is still more common than JMP. Even though JMP can go anywhere, there is no such thing as 'Jump if equal' or 'Jump if not equal' etc. Only JMP, and JSR (Jump to SubRoutine), which is pretty much the same.
 
  
===The LEA command===
+
So after all this, d0 will equal #$48, but $FFFFFF10 will remain as #0.
I've never used this, but I know what it means. LEA stands for Load Effective Address. You can load an address to somewhere else. Example:
 
  
<asm>lea ($FFFFFF10).w,a4</asm>
 
  
Imagine at $FFFFFF10 was 00 02 00 04 and a4 was 00 00 00 00. After this command, a4 will now be 00 02 00 04. Apparently, this works in the exact same way as MOVEA does, but don't take me word for it.
 
  
 
===The NOP command===
 
===The NOP command===
This command is apparently illegal. This stands for No OPeration. It does nothing, literally. So, what is it used for, I do not know, to slow down the process of where it is? It's been explained that it waits for all processes to finish, whether it's true, I have no idea.
+
This stands for No OPeration. It does nothing, literally. So, what is it used for?  Mainly to slow things down.  There are some technical calculations that take time to complete, and sometimes you don't want it to carry on too quickly.  So using a nop will slow things down by 4 cycles, but that's it.
  
 
===The DC command===
 
===The DC command===
This is to store data in the ROM. What does DC stand for? I'm not actually sure, but you can see this anywhere. Here's an example.
+
This is to store data in the ROM. You can see it for art, mappings and other sorts of data. Here's an example.
 +
 
 +
<syntaxhighlight lang="asm">dc.b 0, 1,$F8, 5, 0, 0, 0, 0,$FF,$F8</syntaxhighlight>
  
<asm>dc.b 0, 1,$F8, 5, 0, 0, 0, 0,$FF,$F8</asm>
+
This is part of the ring's mappings. Looks weird, eh? As it is dc.b, its adding bytes, so here, it is adding 00 01 F8 05 00 00 00 00 FF F8. As you can see, there are $ signs there. This just means that they are hexadecimal, but if putting number 0 - 9, you don't have to put a $.
  
This is part of the ring's mappings. Looks weird, eh? As it is dc.b, its adding bytes, so here, it is adding 00 01 F8 05 00 00 00 00 FF F8. As you can see, there are $ signs there. This just means that there is more than one nibble being inputted it. Why a $ instead of a # sign, I don't know. When you get a number on its own, like 1, when the ROM gets built, a 0 appears in front. So 1 equals 01. Example:
 
 
*dc.b 0, $11, 1 = Correct
 
*dc.b 0, $11, 1 = Correct
 
*dc.b 0, 11, 1 = Incorrect
 
*dc.b 0, 11, 1 = Incorrect
*dc.b 0, $11, 01 = Incorrect
 
  
You can also have dc.w dc.l. If that was dc.w, it'll be putting into the ROM 00 00 00 01 00 F8 00 05 00 00 00 00 00 00 00 00 00 FF 00 F8.
+
You can also have dc.w or dc.l (word and longwords). If that was dc.w, it'll be putting into the ROM 00 00 00 01 00 F8 00 05 00 00 00 00 00 00 00 00 00 FF 00 F8.
  
 
DC can be used for palettes, mappings, pretty much anything.
 
DC can be used for palettes, mappings, pretty much anything.
  
===The CLR command===
 
This means CLeaR. This can clear data (obviously) to any RAM address you want. Let's do an example. Pretend that $FFFFFF10 is 05 05 05 05.
 
<asm>clr.b ($FFFFFF10).w ; Clear the byte from $FFFFFF10</asm>
 
Now, $FFFFFF10 is 00 05 05 05.
 
<asm>clr.w ($FFFFFF10).w ; Clear the word from $FFFFFF10</asm>
 
Now, $FFFFFF10 is 00 00 05 05.
 
<asm>clr.l ($FFFFFF10).w ; Clear the longword from $FFFFFF10</asm>
 
Now, $FFFFFF10 is 00 00 00 00.
 
==Quick Homework==
 
$FFFFFEB8 is 00 00 00 00 and $FFFFFF10 is 01 01 01 01. What will these two address show after this process?
 
  
<asm>Code:
 
add.w #4,($FFFFFEB8).w
 
add.b #3,($FFFFFF10).w
 
cmp.w #3,($FFFFFEB8).w
 
beq Codetwo
 
move.l #9,($FFFFFF10).w
 
sub.w #3,($FFFFFEB8).w
 
  
Codetwo:
+
===The CLR command===
clr.l #4,($FFFFFF10).w
+
This means CLeaR. This can clear data (obviously) to any RAM address you want (can clear data and address registers too). Let's do an example. Pretend that $FFFFFF10 is #$05050505.
rts</asm>
 
Answer: $FFFFFEB8 is 00 01 00 00 and $FFFFFF10 is 00 00 00 09. Did you get that? If you didn't, try looking at it again, or go back to more commands for help.
 
  
==Editing the basics==
+
<syntaxhighlight lang="asm"> clr.b ($FFFFFF10).w ; Clear the byte from $FFFFFF10</syntaxhighlight>
Editing the basic is pimp-squeak. You literally do not need any ASM knowledge at all. If you ever want to edit palettes, mappings, text, arrays, start positions, etc, you can search for them. Open up the file s2.asm and go on Edit > Find. Type "array" (without the quotation marks). Keep looking until you come to Level size array. Underneath, you'll see this:
 
  
<asm>word_C054: dc.w 0,$29A0, 0, $320; 0</asm>
+
Now, $FFFFFF10 is #$00050505.
  
This, is the level size array for Emerald Hill Act 1. They go down in order of Level ID. If you remember what I told you about the command DC, you should know this is 00 00 29 A0 00 00 03 20. The very last 0 is a comment as ; is before it. It's a weird comment. Change the array to what you want. But remember, if you going to change them 0's into two or more nibbles, put a $ in front. Example:
+
<syntaxhighlight lang="asm"> clr.w ($FFFFFF10).w ; Clear the word from $FFFFFF10</syntaxhighlight>
  
* word_C054: dc.w 0,$29A0, 22, $320; 0 = Wrong
+
Now, $FFFFFF10 is #$00000505.
* word_C054: dc.w 0,$29A0, $22, $320; 0 = Right
 
  
If you ever want to change things like this, search for it using the Find tool. Example, you want to edit Sonic's start position. Edit > Find > type "start" (without the quotation marks) and keep finding until you come to Character start location array. Edit away.
+
<syntaxhighlight lang="asm"> clr.l ($FFFFFF10).w ; Clear the longword from $FFFFFF10</syntaxhighlight>
  
Some things can't be done in ASM, like Sonic's palette. ASM tells to load a file. If you go to the folder ART/PALETTES, you'll find a file in there called something like 'Sonic's palette'. You edit that using a hex editor. So if you can't find the stuff you want to edit in ASM, look for the file, if you can't find that, contact me.
+
Now, $FFFFFF10 is #$00000000.
  
===Making the special ring object===
 
Ok, things are going to get complicated here, but if you follow this thoroughly without missing bits, you'll be fine, and it'll be a breeze. Okay, first of all, open up your s2.asm file. Edit > Find > Type "SprPoint:" (without the quotation marks). Here, is the list object. These are pointers on where the code is. The comments are the object number, but not in HEX format. Here's a little guide on the <del>object list</del>.
 
  
Sprite_1637C is an empty coding. So, object 4C is empty. We can use this. Find:
 
  
<asm>dc.l Sprite_1637C ; 76</asm>
+
==Quick Homework==
 +
$FFFFFEB8 is #$00000000 and $FFFFFF10 is #$01010101. What will these two address show after this process?
  
Name it to:
+
<syntaxhighlight lang="asm">Code:
<asm>dc.l Sprite_Specialring ; 76</asm>
+
addq.w #4,($FFFFFEB8).w
 +
addq.b #3,($FFFFFF10).w
 +
cmpi.w #3,($FFFFFEB8).w
 +
beq.s Codetwo
 +
move.l #$A,($FFFFFF10).w
 +
subq.w #3,($FFFFFEB8).w
 +
rts
  
Now, it'll be going to the Specialring pointer, except it doesn't exist. But we can sort that. Find "Red" (without the quotation marks) and the second find should display this:
+
Codetwo:
<asm>; ----------------------------------------------------------------------------
+
clr.l ($FFFFFF10).w
; Sprite
+
rts</syntaxhighlight>
;
+
; Red spring
+
Answer: $FFFFFEB8 is #$00010000 and $FFFFFF10 is #$0000000A. Did you get that? If you didn't, try looking at it again, or go back to more commands for help.
; ----------------------------------------------------------------------------</asm>
 
  
Just before the first line before the word Sprite, insert all this:
 
  
<asm>; ----------------------------------------------------------------------------
 
; Sprite
 
;
 
; Special Ring
 
; ----------------------------------------------------------------------------
 
  
Sprite_Specialring: ;This is when the pointer we changed comes to.
 
moveq #0,d0
 
move.b $24(a0),d0
 
move.w off_SpecialringIndex(pc,d0.w),d1
 
jmp off_SpecialringIndex(pc,d1.w) ;moves us to the next bit of the code (look down)
 
  
;****************************************************************************
+
==Editing the basics==
 +
Editing the basic is pimp-squeak. You literally do not need any ASM knowledge at all. If you ever want to edit palettes, mappings, text, arrays, start positions, etc, you can search for them. Open up the file s2.asm and go on Edit > Find. Type 'word_C054:' (without the quotation marks). You should come to Level size array. Underneath, you'll see this:
  
off_SpecialringIndex:
+
<syntaxhighlight lang="asm">word_C054: dc.w 0,$29A0, 0, $320; 0</syntaxhighlight>
dc.w loc_Specialring_Init-off_SpecialringIndex
 
dc.w loc_Specialring_Frame-off_SpecialringIndex
 
dc.w loc_Specialring_Collision-off_SpecialringIndex
 
dc.w loc_Specialring_Delete-off_SpecialringIndex
 
  
;****************************************************************************
+
This, is the level size array for Emerald Hill Act 1. They go down in order of Level ID. If you remember what I told you about the command DC, you should know this is 00 00 29 A0 00 00 03 20. The very last 0 is a comment as ; is before it. It's a weird comment. Change the array to what you want. But remember, if you going to change them 0's into A or more, put a $ in front. Example:
  
loc_Specialring_Init: ;graphics and such are set up here
+
* word_C054: dc.w 0,$29A0, 22, $320; 0 = Wrong
addq.b #2,$24(a0)
+
* word_C054: dc.w 0,$29A0, $22, $320; 0 = Right
move.w 8(a0),$32(a0)
 
move.l #MapUnc_12382,4(a0)
 
move.w #$26BC,2(a0)
 
jsr sub_16D6E
 
move.b #4,1(a0)
 
move.b #2,$18(a0)
 
move.b #$47,$20(a0)
 
move.b #8,$19(a0)
 
  
loc_Specialring_Frame: ;executed every frame * for animation/drawing
 
move.b #0,$1A(a0)
 
move.w $32(a0),d0
 
jsr loc_1640A
 
rts
 
  
loc_Specialring_Collision: ;executed upon collision with sonic
+
If for example, if you change that $29A0 into $2900, when built, EHZ1 will end a bit quicker!
  
loc_Specialring_Delete: ;delete the object
 
rts
 
;****************************************************************************</asm>
 
  
Oh, my. This looks complicated eh? There are comments there to help explain what which part does. This is what Ultima gave me, as my new object code sucked. Always use this when making new objects, and just change to name Specialring to whatever, but in this case, leave it. The loc_Specialring_Collision: part is where we are going to add our brand new code.
 
  
<asm>loc_Specialring_Collision: ;executed upon collision with sonic
+
Or say you want Sonic's top speed to go a little bit faster when you get speedshoes.  Search for "super_shoes:" and you see
move.w #1,($FFFFFEB9).w ;add one word to $FFFFFEB9</asm>
 
  
This is all we need at the collision, seriously. Now, we need to find the bit when the level fades to go to the next level. Save the ASM file, the go on EDIT > Find > loc_1429C: and you should find this:
+
<syntaxhighlight lang="asm"> move.w #$C00,(Sonic_top_speed).w</syntaxhighlight>
  
<asm>loc_1429C: ; CODE XREF: ROM:00014292 j
+
You can change that number higher if you want him a bit faster or lower for slower!  Simples!
move.w d0,($FFFFFE10).w
 
clr.b ($FFFFFE30).w
 
clr.b ($FFFFFEE0).w
 
move.w #1,($FFFFFE02).w
 
rts</asm>
 
  
Change it to:
 
<asm>loc_1429C: ; CODE XREF: ROM:00014292j
 
clr.b ($FFFFFE30).w
 
clr.b ($FFFFFEE0).w
 
cmpi.w #1,($FFFFFEB9).w ; Does $FFFFFEB9 equal 00 01?
 
bne.s nextlevel ; Branch if not
 
move.b #$10,($FFFFF600).w ; set game mode to Special Stage
 
clr.w ($FFFFFEB9).w
 
rts
 
  
nextlevel:
+
If you ever want to change things like this, search for it using the Find tool. Example, you want to edit Sonic's start position. Edit > Find > type 'start' (without the quotation marks) and keep finding until you come to Character start location array. Edit away.
move.w #1,($FFFFFE02).w ; Set level reset
 
clr.b ($FFFFFEB9).w</asm>
 
Now what happens is just before the level fades, if $FFFFFEB9 equals 00 01, it will go to the special stage instead of the next level. If it does not equal, it will go to the next level. When you go to the special stage or next level, $FFFFFEB9 also gets cleared, so it's ready for the next level when you put another ring in there.
 
  
Now, Find > Edit > "loc_1428C: " (without the quotation marks) and you'll find this:
+
Some things can't be done in ASM, like Sonic's palette. ASM tells to load a file. If you go to the folder ART/PALETTES, you'll find a file in there called something like 'Sonic's palette'. You edit that using a hex editor. So if you can't find the stuff you want to edit in ASM, look for the file.
  
<asm>loc_1428C: ; CODE XREF: ROM:00014286j
 
move.w (a1,d0.w),d0
 
tst.w d0
 
bpl.s loc_1429C
 
move.b #0,(Mstr_Lvl_Trigger).w
 
rts</asm>
 
  
Change to:
+
That's it. Hope you enjoyed the guide and have learnt something today. Now get out there and show us what you're made of!
<asm>loc_1428C: ; CODE XREF: ROM:00014286j
 
move.w (a1,d0.w),d0
 
move.w d0,($FFFFFE10).w
 
tst.w d0
 
bpl.s loc_1429C
 
move.b #0,($FFFFF600).w
 
rts</asm>
 
  
This supports the ROM to go to the next level after the special stage. If followed correctly, this should now work. Save your ASM file, and put an object in EHZ act 1 to test it. Put 4D 00 08 as the object. To build, double-click build.bat and after, S2BUILT.BIN will appear. You may need to fix the checksum before playing. Now test away.
 
  
[[Category:SCHG How-tos|Work with ASM]]
+
[[Category:SCHG How-tos|Work with Motorola 68000 assembly]]

Latest revision as of 21:46, 20 December 2015

(Original guide by redhotsonic)

Welcome, to my ASM guide. It is important to ALWAYS back-up your hack/work/disassembly before making changes. This will teach you very basic ASM commands what make you aware what ASM is and what a disassembly is used for.

In this guide, I'll teach you

  • What ASM means
  • What a disassembly is used for
  • The basics of ASM
  • Learning how ASM works
  • Editing some basics with ASM


This guide is for people who know a little ASM or none at all. If you have intermediate knowledge of ASM, then you might find a few tips from this guide, but if you're an expert at ASM, then this guide isn't for you because you know all this already.



What is ASM?

ASM stands for ASseMbly. It helps builds your commands into a ROM. Basically, ASM is instead of Hex (Machine Code); it uses words instead of numbers. That's why people use ASM more than Machine Code.

For example, this is hex: 11FC 0010 F766.

In ASM, it will look like this: move.b #$10,($FFFFF766).w.

It's just easier to read for us in words.



What is a disassembly?

A disassembly is a ROM (or a game) taken apart in displays all its bare code. It can have separate/multiple data files but only representing one thing. For example, the ROM will contain art for a character and codes for an object, etc, all in one ROM image/file. A disassembler will have 1 file for the characters' art, 1 file (or data) for an objects code, etc. It also means we can edit or add codes and it will shift the rest of the code and the pointers will be changed accordingly. Whereas if you tried editing the code in the ROM itself, we'll have a fear of over-writing some code as none of the code will shift. You can shift it yourself, but then all the pointers will need changing.

So it's easier to use a disassembler. But obviously you cannot play the game disassembled. So, you have to 'build' it. Once you're happy with your edits, you can click on the .BAT file called 'BUILD'. What this will do is assemble all those data files and codes into that final ROM image again. And there you have it. Your game is ready to play!



Getting started

If you have a disassembly already, you can skip this part. For the majority of the guide, I am going to be using Sonic 2 as the game reference, and Xenowhirls disassembly as the ASM reference. A list of disassemblies are here on the Disassemblies page. Or if you want to use the same game and disassembly I am using for this guide, then you can download it straight from here. Once you've downloaded the disassembly, you'll need to split the game (using a plain Sonic 2 ROM, named s2.bin). Download a Sonic 2 ROM and name it s2.bin and put it in the same folder as the disassembly. Then, open the .BAT file called SPLIT. You're disassembly is now ready to use!



The basics commands of ASM

Okay, in your folder, look for the S2.asm file and open it with a word editor (I use Notepad2, but the first notepad will be fine). Look at that, there's so much to it, but we can learn it. Close the file down for now, and let's get learning. I'll teach you some ASM commands. Let's get to the basics.


The Basic understanding

TheMoveCommand:
	move.w	#2,($FFFFFEB8).w

Let's look at the bit where it says move.w. Move, literally means move. It moves whatever you want to move to a certain point. But what does the .w mean? Well, this means word. You can also get .b (byte) and .l (longword). A byte is two nibbles (00), a word is 4 nibbles (0000), and a longword is 8 nibbles (00000000). The $FFFFFEB8 is a RAM address; these store bytes that you want and can change. You can consider it as a memory. When moving something to a RAM address, it must always be in brackets with a .w afterwards (well, most of the time this is the case, but this is a beginner's guide, so for now, it's always in brackets ending with .w).


You see the text 'TheMoveCommand:'? This is called a label, helping branches (that will be explained later).


Okay, so, move.w means it's moving a word. #2 is the byte we want to move, and must always put # in front of the number, so the Megadrive knows it is a decimal number. Because it's moving a word, that #2 in a word is #0002 (remember, a word is 4 nibbles). So #2 in 4 nibbles is #0002. You can put #0002 if you want, but you can ignore the 0's if they come first, so #2 is fine. If it was #20 we're moving, you'd then have to put either #20 or #0020. You can't put #2 because it thinks you mean #0002, it doesn't know you want that 0 after the 2. So putting #20 will be converted to #0020. If you want #200, you must either put #0200 or #200, etc.

If it was to be a move.b, it would only be moving a byte (two nibbles), so #2 would be #02. If it was to be a move.l, it would only be moving a longword (eight nibbles), so #2 would be #0000002. Get the idea?


So # means it's not a memory and is something else, for most of this guide, a decimal.

So # is decimal, so, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13...

If you add a dollar sign so you get this: #$2, it will then be treated as hexademical. So basically:

The #$ means hexadecimal, so, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13...


The disassembler will always convert it into a hexadecimal number either way when you build. But #2 converted to hex is still #$2. If you put #9, it will be converted to hex, which is still #$9. But if you put #10, then when building, that will change to #$A. Using the numbers I just put above, can you see why? So if you put #12, it will be converted to $C. If you actually want it as 12, then you must put #$12, because that way, you've made it hexadecimal yourself, and when building, it will stay as $12.


The MOVE command

Anyway, this command, what is it doing?

TheMoveCommand:
	move.w	#2,($FFFFFEB8).w


The $FFFFFEB8 is the address we want it to go. The $ sign indicates the number is hexadecimal, and memory addresses are always represented that way. Let's say that at address $FFFFFEB8 it is #$0000. After the command above, it'll become #$0002. So before the command, ($FFFFFEB8).w equalled #$0000. After the command, ($FFFFFEB8).w equals #$0002.


Do you see how it works? No? Let's do another example.

Pretend $FFFFFEB8 is: #$0002


Let's say we want that 02 to be an 08, we do this:

	move.w	#8,($FFFFFEB8).w

Now, $FFFFFEB8 is: #$0008.


It works. Here's a longword example. It works the exact same way.

Pretend $FFFFFE10 is: #$00000000

	move.l	#6,($FFFFFE10).w

Now, $FFFFFE10 is: #$00000006


But remember that RAM addresses are treated as a hexadecimal number all the time. So for this next example, pretend $FFFFFE10 is: #$00000000

	move.l	#10,($FFFFFE10).w


We're moving a decimal number #10 to the RAM, but the RAM will treat it as a hexadecimal, so after this command, $FFFFFE10 will be #$0000000A. Remembering that the RAM address is hexadecimal, so it would have gone 8, 9, A, B... NOT 8, 9, 10, 11...


If you want that RAM to be 10, then you have to treat the number you're moving as a hexadecimal number. So pretend $FFFFFE10 is: #$00000000, after this next command:

	move.l	#$10,($FFFFFE10).w


$FFFFFE10 will be #$00000010. This is because you've converted the number to hexadecimal yourself; you moved #$10 to the RAM. Hope this makes sense!


Let's make things slightly more difficult. Let's say that $FFFFFFE6 is #$02030405. What would this become if we did this command?

	move.w	#4,($FFFFFFE6).w


Well, $FFFFFFE6 will become #$00040405. The first four nibbles (#0004) is what you just did. Remembering that the move.w is moving a word (4 nibbles, #0000), and there is 8 nibbles to this RAM address, it only does the first four nibbles. The last 4 nibbles in the RAM address remain unaffected because we only moved a word. If we did:

	move.l #4,($FFFFFFE6).w

$FFFFFFE6 would become #$00000004, because you moved a longword (8 nibbles).


As a sidenote, remember that the smallest unit the MC68000 can address is a byte, so each RAM address refers to a byte. As an example:

$FFFFFE00 = #$12
$FFFFFE01 = #$34
$FFFFFE02 = #$56
$FFFFFE03 = #$78

Each address here is being treated as a byte. But, if we decided to use one of these addresses as a word:

	move.w #5,($FFFFFE00).w

Then the above four addresses will look like this:

$FFFFFE00 = #$00
$FFFFFE01 = #$05
$FFFFFE02 = #$56
$FFFFFE03 = #$78

Note that moving a word to $FFFFFE00 affected both $FFFFFE00 and $FFFFFE01. Additionally, if $FFFFFE00 was interpreted as a longword, then its original value was $12345678, and its new value is $00055678. Since we only wrote a single word, $FFFFFE02 and $FFFFFE03 remain unaffected.


NOTE: It is not recommended to pick any old RAM address, as it may be being used for specific things. For a list of addresses and what they are used for in Sonic 2, look here.


All commands can have comments (it doesn't HAVE to have a comment). This is if you want to remember what this command does. For example:

	move.b	#4,($FFFFFFE6).w	; moves the byte 04 to the address shown

If you want to add comments after a command, you MUST put the Semicolon symbol ( ; ) after the command, then put whatever you want, as long as it stays on the same line. You can literally put any comment you want:

	move.b	#4,($FFFFFFE6).w	; moves 4 to the 'I am Super' RAM thingamajig, wazza wazza!

Comments are useful if you want to remember what the command is doing or just to set some notes.


The MOVE command isn't just restricted to moving numbers to RAM addresses. You can move a RAM address to another for example:


	move.w	($FFFFFFE6).w,($FFFFFFE8).w	; copy RAM address to another


It's simple. But you MUST remember, RAM addresses are treated as hexadecimal all the time. Imagine $FFFFFFE6 is #$9AB6, and $FFFFFFE8 is #$1234. If you move $FFFFFFE6 to $FFFFFFE8, $FFFFFFE8 will now be #$9AB6. $FFFFFFE6 will also remain the same as you haven't told anything to move to that. So basically, you've just copied it. Another example:

	move.l	d0,($FFFFFFE8).w	; move data register to RAM address


d0 is a data register (hexadecimal again), but we'll explain what it's used for a little bit later on, but for now, imagine d0 is #$01234567. And $FFFFFFE8 is #$55555555. So, after this command, $FFFFFFE8 will be #$01234567. d0 will still stay the same obviously.


You don't have to move things to RAM address either:

	move.w	#$2534,d0	; move this hexadecimal number to d0

d0 will become #$2534 after the above command.


There are more MOVE commands, but I wouldn't worry about them for now as this is only a basic guide. But here they are and a small description on what they do:

moveq / MOVE Quick / Quickly moves a hex number to a data register faster than move.l but can only be used from -127 up to 128 (decimals) and moved to data registers only.


movem / MOVE Multiple / Can move data to multiple addresses. An example is it can move data from an address register to d0 and d1 and d2, etc.

movea / MOVE Address / Moves registers to other registers

The ADD command

	addi.b #9,($FFFFFEB8).w

This is pretty much the same as the command MOVE, but it adds instead. You're probably asking, what does that i stand for in addi? i stand for immediate. No, it does not mean it will immediately do the command, it means it will add a decimal/hexadecimal number to the destination. So if you want to add a number to something, it's addi. If you want to add a RAM or register to something, then it's just add. REMEMBER, RAM addresses are always treated as hexadecimal.

Anyway, let's say $FFFFFEB8 is #$01010101 and then we did this command above; it'll become #$0A010101. It's A because it's hex, remember? We only added a byte, so it only does the first 2 nibbles. Do you see how it works? Here's another example.


Assuming that $FFFFFEB8 is #$01040202, after executing the below command:

	addi.w #9,($FFFFFEB8).w	; add the word 09 to $FFFFFEB8

It'll become #$010D0202. Because we added a word. 9 in a word is #0009, it's only doing the first 4 nibbles in the address, so #$0104 add #0009 is #$010D. Remembering that the RAM address is hexadecimal, so it would of gone 8, 9, A, B, C, D. NOT 8, 9, 10, 11, 12, 13.


Again, it doesn't need to be numbers, you can do:

	add.w	($FFFFFFE6).w,($FFFFFFE8).w	; add RAM address to another

Now, because we're not adding a number, it's no longer addi and just plain-old add. So, if $FFFFFFE6 is #$0003 and $FFFFFFE8 is #$0008, after this command, $FFFFFFE8 will be #$000B. Yes, B, because it's hexadecimal. So it would of gone 8, 9, A, B. NOT 8, 9, 10, 11. Otherwise, it's as simple as that. It is important for you to know that $FFFFFFE6 will stay as #$0003 seeming as we haven't told it to change.


Another example:

	add.w	d0,($FFFFFFE8).w	; add data register to a RAM address

If d0 is #$1234 and $FFFFFFE8 is #$AAAA, after the command, $FFFFFFE8 will be #$BCDE. d0 will obviously stay as #$1234.


Here is an extra command, the addq command:

	addq.b #6,($FFFFFEB8).w	; add 6 quickly to the RAM address


addq is the fastest way to add and can be used on any destination (pretty much). But the problem is, it can only be used when adding a number between 1 and 8. So, if you want to add 6 to a RAM address, then addq is better to use than addi. If you want to add 3 to a RAM address, then:

	addq.b #3,($FFFFFEB8).w	; add 3 quickly to the RAM address


But however, if you want to add 9 (or more) to a RAM address, then addi has to be used:

	addi.b #9,($FFFFFEB8).w	; add 9 to the RAM address

If you're adding a RAM address to another like so:

	add.w	($FFFFFFE6).w,($FFFFFFE8).w	; add RAM address to another

it doesn't matter if $FFFFFFE6 is between 1 - 8, you cannot use the addq. addq is literally only used when adding a number itself to the destination. As it's not a number and it's a RAM address, it's not addi either, it's just add.


The SUB command

	subi.b	#$12,($FFFFFEB8).w

This is nearly the same as the add command, except we're going the other way. Instead of adding, we're subtracting. I'll only give one example here, as this is pretty straight forward.

Pretend $FFFFFEB8 is: #$15161718

	subi.b	#$12,($FFFFFEB8).w	; going to take the byte $12 from $FFFFFEB8

Now $FFFFFEB8 is: #$03161718


Again, it doesn't need to be numbers, you can do:

	sub.b	($FFFFFFE6).w,($FFFFFFE8).w	; add RAM address to another


If $FFFFFFE6 is #$6 and $FFFFFFE8 is #$8, after the command, $FFFFFFE8 will be #$2. $FFFFFFE6 will remain as #$6 obviously. Remember that for these type of commands, it's sub and not subi.


There is another command, subq. It works exactly the same way ass addq, but subtracting instead of adding. Again, only numbers, and only between 1 - 8.


Number formats and prefixes

I've already explained the basic two; decimal and hexadecimal. There are four ways in total to represent a number in ASM:

  • Decimal (no prefix) - Digits are 0-9. This is what humans are used to reading.
  • Binary (%) - Each digit represents one bit, which can be 1 (on) or 0 (off). '%10' in binary is the same as '#2' in decimal.
  • Hexadecimal ($) - Each digit is one nibble (4 bits) and ranges from $0-$F. '#$10' in hexadecimal is the same as '#16' in decimal
  • Octal (@) - 3 bits. Numbers range from 0-7. This is rarely used if ever. '@10' is the same as '#8'

The # is special and basically means the number after it is NOT a memory address. It can be used in conjunction to the above as follows, assuming $FFFFFEB8 is #$0000:

	move.w	#$FF,($FFFFFEB8).w

$FFFFFEB8 becomes: #$00FF, the #$FF would be the same as inputting #255:

	move.w	#255,($FFFFFEB8).w

Using either of these commands, ($FFFFFEB8).w will be #$FF.


Data and address registers

Data registers are temporarily data holders (like memory again). It will remember what you told it to remember. All data registers are longwords (whereas RAM addresses were bytes), and these types of registers exist from d0-d7. Example:

d0: #$00000000

	move.b	#3,d0	; going to move 03 to the data register 0

d0: #$00000003


Until you tell it to change, that'll stay like that. Like said, it doesn't necessarily have to be d0, it can be... I don't know... d4? You can make d0 go to other places too.

	move.b	d0,($FFFFFEB8).w	; going to move the data from d0 to $FFFFFEB8

d0 will still be #$00000003, but $FFFFFEB8 will be #$03000000.


If the command was:

	move.w	d0,($FFFFFEB8).w	; going to move the data from d0 to $FFFFFEB8

d0 will still be #$00000003, but $FFFFFEB8 will be #$00030000.


If the command was:

	move.l	d0,($FFFFFEB8).w	; going to move the data from d0 to $FFFFFEB8

d0 will still be #$00000003, but $FFFFFEB8 will be #$00000003.


Address registers are also data holders, but these normally hold pointers, but can store data. These exist from a0 to a7, but avoid using a7 as it is a stack holder, which I will not cover in this basic guide but anyway, don't use it. We'll cover about the address registers a little bit later.



More ASM commands

The CMP command

	cmpi.w	#3,($FFFFFEB8).w

Ah, the COMPARE command. This just compares the byte you've put to the address. In this case, the word 03 to $FFFFFEB8. If $FFFFFEB8 is #$00030708, this equals the word #$0003. If $FFFFFEB8 was #$05030000, it would not equal the word #$0003 (because it's #$0503). Again, the i (immediate) is here. Same rules apply like the addi/subi. If it's a number you're comparing, it's cmpi, otherwise, it's cmp. Example:

	cmpi.w	#3,d0	; See if d0 equals #0003 (it's cmpi here)
	cmp.w	d0,d1	; See if d0 equals the same as d1 (it's cmp here)


There is no such thing as cmpq though.


The TST command

	tst.w	($FFFFFEB8).w

tst stands for TeST. This is a disguise for CMP. Basically, 'tst' is comparing to see if the destination equals 0. Basically:

	tst.w	($FFFFFEB8).w

Is the EXACT same as:

	cmpi.w	#0,($FFFFFEB8).w


The reason why you want to use tst instead of cmp is when seeing if something equals #0 or not, is because tst is slightly faster. You can consider this as 'cmpq', but only works on #0.


The BRA commands

This, is the BRANCH command. There are many branch commands, but we're going to take it one step at a time. The command BRA stands for BRanch Anyway. This is what you want the MegaDrive to branch (or go) to when you use this command. What do I mean? Well, take a look at this.

TheCode:
	move.w	#3,($FFFFFEB8).w
	addi.w	#3,($FFFFFF10).w
	bra.s	Morecodinghere
	subi.w	#3,($FFFFFEB8).w

Morecodinghere: 
	move.w	#3,($FFFFFEBA).w
	rts

You're probably thinking, 'What the hell?' Do not panic. Let's go through this. First, it's saying it's moving the word #0003 to $FFFFFEB8. Then it's adding the word #0003 to $FFFFFF10. Here is when our new command, BRA, comes in. It's saying to branch (go to) 'Morecodinghere'. As you can see, there is a label called 'Morecodinghere'. So, when it sees this, it goes to this label. Remember me mentioning about labels earlier? This is what labels are useful for! So now, we're starting from the label 'Morecodinghere'. So, the next thing it does, it is moving #0003 to $FFFFFEBA.

The subi.w command was ignored. This is because it got branched. If you took the BRA command out, it would do that SUBI command.

In the Morecodinghere label, you can see underneath its MOVE command, is a new command:


The RTS and BSR command

RTS means 'ReTurn from Subroutine'. So, it's telling to go back to where this subroutine was first executed. Let's look at this example:

Somecodes:
	move.w	#3,($FFFFFEB8).w
	addi.w	#3,($FFFFFF10).w
	bsr.s	Morecodinghere2		; *
	subi.w	#3,($FFFFFEB8).w
	rts

Morecodinghere2: 
	move.w	#3,($FFFFFEBA).w
	rts


For this example, that bra.s has changed to bsr.s. BSR stand for 'Branch to SubRotuine'. This means to branch to a new code, but remember to come back (in simple terms). So let's follow this code. First, it moves #0003 to $FFFFFEB8, and then adds #0003 to $FFFFFF10, then the new command, bsr. This means, go to the label 'Morecodinghere2' and return to me. So, go to 'Morecodinghere2', next command is to move #0003 to $FFFFFEBA. Next is 'RTS', which is telling you to return from this subroutine. Basically, where the '*' is in the comments. So now, you're back there. Next command is to subtract #0003 from $FFFFFEB8. Then finally, rts again, and that will return to whatever made the code jump to the label 'Somecodes'.

I hope that makes sense.


So far, you've been introduced to BRA and BSR. You might have seen me put a .s after these. The s stands for short. Try to add .s to the end of each branch whenever possible. But hence the name short, so it can only branch to short distances. If you tried making it branch to a label far away, .s might not work. If so, use .w (which stands for word) like so:

	addq.w	#3,($FFFFFF10).w
	bra.w	afarawaylabel
	subi.w	#$13,($FFFFFEB8).w

.s and .w are just as quick as each other, but .s is smaller in size, saving you space. So use .s when you can and if the branch is too long, use .w. Sometimes, even a .w can't reach, and if so, then you use:


The JMP and JSR commands

Whereas BSR stood for 'Branch to SubRoutine', JSR stands for 'Jump to SubRoutine' and JMP literaly stands for Jump. These two commands can jump almost anywhere. These commands do not need .s or .w at the end. So, you can use them in the same way:

	add1.w	#3,($FFFFFF10).w
	jmp	afarfarfarawaylabel
	subi.w	#$13,($FFFFFEB8).w

JMP and JSR uses more space and time to perform than BRA and BSR, so try to use the BRA/BSR. Remember:

bra.s > bra.w > jmp

bsr.s > bsr.w > jsr


Here are two new branch commands:


The BEQ and BNE commands

BEQ stands for 'Branch if EQual'. BNE stands for 'Branch if Not Equal'. This is where the compare command (cmp/cmpi) comes into use. Take a look at this.

BEQlabel:
	cmpi.w	#3,($FFFFFEB8).w
	beq.s	Itequaled
	subi.b	#$B,($FFFFFEB8).w
	rts

Itequaled:
	addq.b	#2,($FFFFFEB8).w
	rts

Let's go through this. First we're comparing if $FFFFFEB8 equals #0003. Then we've got our BEQ command. Basically, it is saying if $FFFFFEB8 DID equal #0003, then branch to the label 'Itequaled'. Now, because it branched, the subi.b command is ignored and it goes to the label 'Itequaled'. If $FFFFFEB8 did NOT equal #0003, it will carry on and do the subi.b command instead.

BNElabel:
	cmpi.w	#3,($FFFFFEB8).w
	bne.s	Itdidnotequal
	subi.b	#$B,($FFFFFEB8).w

Itdidnotequal: 
	addq.b	#2,($FFFFFEB8).w
	rts

Now, look at this. If $FFFFFEB8 did NOT equal #0003, it WILL branch, as we've used the BNE command. However, if it DID equal #0003, it will NOT branch and will carry on and does the subi.b command. I hope you understand this. Here is a test. For this test, pretend that $FFFFFF10 equals #$00000000.

Newlabel:
	move.w	#3,($FFFFFF10).w
	addq.w	#3,($FFFFFF10).w
	cmpi.w	#6,($FFFFFF10).w
	beq.s	Morecodinghere
	subq.w	#3,($FFFFFF10).w

Morecodinghere:
	move.w	#3,($FFFFFF10).w
	rts

Here's the question; In 'New Label', does it branch to 'Morecodinghere'? The answer is yes. I'm not going to explain why it does; study it. If you still do not know why, go back to revise.


There are more BRANCH commands. Here they are, but I'm not going to explain, as you should get the idea.

BGE / Branch if Greater or Equal / If it equals, or is greater than the equal amount, branch

BGT / Branch if Greater Than / If it is greater than the equal amount, branch

BLE / Branch if Less or Equal / If it equals, or is less than the equal amount, branch

BLT / Branch if Less Than / If it is less than the equal amount, branch

I hope this makes sense. There are more branches, but it’s not relevant to the basic guide.


The LEA command

LEA stands for Load Effective Address. You can load an address to an address register. This is where a0 to a6 becomes useful (briefly talked about them earlier). Example:

	lea	($FFFFFF10).w,a4

Now $FFFFFF10 will be in address register a4. Or another way of putting it, a4 contains $FFFFFF10. This can be useful if you want to do a lot of calculations to one RAM address. Example:

	move.b	#3,($FFFFFF10).w
	addq.b	#2,($FFFFFF10).w
	cmpi.b	#1,($FFFFFF10).w
	beq.s	Gotolabelifequaled
	addi.b	#$43,($FFFFFF10).w
	cmpi.b	#$48,($FFFFFF10).w
	beq.s	Anotherlabel
	rts


This is moving/adding/comparing $FFFFFF10 quite a bit. While there is no problem with this, there can be a quicker way. This is when address registers and the LEA command can help. If we load $FFFFFF10 into a1 for example, we can do this instead:

	lea	($FFFFFF10).w,a1
	move.b	#3,(a1)
	addq.b	#2,(a1)
	cmpi.b	#1,(a1)
	beq.s	Gotolabelifequaled
	addi.b	#$43,(a1)
	cmpi.b	#$48,(a1)
	beq.s	Anotherlabel
	rts

Basically, (a1) has taken ($FFFFFF10).w's place. This is a much quicker way, as ASM can do calculations quicker to an address register than to a RAM address. The charm is, after these commands, $FFFFFF10 will still have these calculations applied to it. So at the end, both (a1) and ($FFFFFF10).w will equal #$48.


If however, you want to do these commands above, but you don't want $FFFFFF10 to have any changed effects, then data registers are the answer:

	move.b	($FFFFFF10).w,d0
	move.b	#3,d0
	addq.b	#2,d0
	cmpi.b	#1,d0
	beq.s	Gotolabelifequaled
	addi.b	#$43,d0
	cmpi.b	#$48,d0
	beq.s	Anotherlabel
	rts


So after all this, d0 will equal #$48, but $FFFFFF10 will remain as #0.


The NOP command

This stands for No OPeration. It does nothing, literally. So, what is it used for? Mainly to slow things down. There are some technical calculations that take time to complete, and sometimes you don't want it to carry on too quickly. So using a nop will slow things down by 4 cycles, but that's it.

The DC command

This is to store data in the ROM. You can see it for art, mappings and other sorts of data. Here's an example.

dc.b 0, 1,$F8, 5, 0, 0, 0, 0,$FF,$F8

This is part of the ring's mappings. Looks weird, eh? As it is dc.b, its adding bytes, so here, it is adding 00 01 F8 05 00 00 00 00 FF F8. As you can see, there are $ signs there. This just means that they are hexadecimal, but if putting number 0 - 9, you don't have to put a $.

  • dc.b 0, $11, 1 = Correct
  • dc.b 0, 11, 1 = Incorrect

You can also have dc.w or dc.l (word and longwords). If that was dc.w, it'll be putting into the ROM 00 00 00 01 00 F8 00 05 00 00 00 00 00 00 00 00 00 FF 00 F8.

DC can be used for palettes, mappings, pretty much anything.


The CLR command

This means CLeaR. This can clear data (obviously) to any RAM address you want (can clear data and address registers too). Let's do an example. Pretend that $FFFFFF10 is #$05050505.

	clr.b	($FFFFFF10).w	; Clear the byte from $FFFFFF10

Now, $FFFFFF10 is #$00050505.

	clr.w	($FFFFFF10).w	; Clear the word from $FFFFFF10

Now, $FFFFFF10 is #$00000505.

	clr.l	($FFFFFF10).w	; Clear the longword from $FFFFFF10

Now, $FFFFFF10 is #$00000000.


Quick Homework

$FFFFFEB8 is #$00000000 and $FFFFFF10 is #$01010101. What will these two address show after this process?

Code:
	addq.w	#4,($FFFFFEB8).w
	addq.b	#3,($FFFFFF10).w
	cmpi.w	#3,($FFFFFEB8).w
	beq.s	Codetwo
	move.l	#$A,($FFFFFF10).w
	subq.w	#3,($FFFFFEB8).w
	rts

Codetwo:
	clr.l	($FFFFFF10).w
	rts

Answer: $FFFFFEB8 is #$00010000 and $FFFFFF10 is #$0000000A. Did you get that? If you didn't, try looking at it again, or go back to more commands for help.



Editing the basics

Editing the basic is pimp-squeak. You literally do not need any ASM knowledge at all. If you ever want to edit palettes, mappings, text, arrays, start positions, etc, you can search for them. Open up the file s2.asm and go on Edit > Find. Type 'word_C054:' (without the quotation marks). You should come to Level size array. Underneath, you'll see this:

word_C054: dc.w 0,$29A0, 0, $320; 0

This, is the level size array for Emerald Hill Act 1. They go down in order of Level ID. If you remember what I told you about the command DC, you should know this is 00 00 29 A0 00 00 03 20. The very last 0 is a comment as ; is before it. It's a weird comment. Change the array to what you want. But remember, if you going to change them 0's into A or more, put a $ in front. Example:

  • word_C054: dc.w 0,$29A0, 22, $320; 0 = Wrong
  • word_C054: dc.w 0,$29A0, $22, $320; 0 = Right


If for example, if you change that $29A0 into $2900, when built, EHZ1 will end a bit quicker!


Or say you want Sonic's top speed to go a little bit faster when you get speedshoes. Search for "super_shoes:" and you see

	move.w	#$C00,(Sonic_top_speed).w

You can change that number higher if you want him a bit faster or lower for slower! Simples!


If you ever want to change things like this, search for it using the Find tool. Example, you want to edit Sonic's start position. Edit > Find > type 'start' (without the quotation marks) and keep finding until you come to Character start location array. Edit away.

Some things can't be done in ASM, like Sonic's palette. ASM tells to load a file. If you go to the folder ART/PALETTES, you'll find a file in there called something like 'Sonic's palette'. You edit that using a hex editor. So if you can't find the stuff you want to edit in ASM, look for the file.


That's it. Hope you enjoyed the guide and have learnt something today. Now get out there and show us what you're made of!