Actions

SCHG How-to

Difference between revisions of "Work with Motorola 68000 assembly"

From Sonic Retro

m (Text replacement - "<asm>" to "<syntaxhighlight lang="asm">")
 
(4 intermediate revisions by 2 users not shown)
Line 47: Line 47:
  
 
===The Basic understanding===
 
===The Basic understanding===
<asm>TheMoveCommand:
+
<syntaxhighlight lang="asm">TheMoveCommand:
move.w #2,($FFFFFEB8).w</asm>
+
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).
 
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).
Line 77: Line 77:
 
Anyway, this command, what is it doing?
 
Anyway, this command, what is it doing?
  
<asm>TheMoveCommand:
+
<syntaxhighlight lang="asm">TheMoveCommand:
move.w #2,($FFFFFEB8).w</asm>
+
move.w #2,($FFFFFEB8).w</syntaxhighlight>
  
  
Line 91: Line 91:
 
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: #$0008.
 
Now, $FFFFFEB8 is: #$0008.
Line 100: Line 100:
 
Pretend $FFFFFE10 is: #$00000000
 
Pretend $FFFFFE10 is: #$00000000
  
<asm> move.l #6,($FFFFFE10).w</asm>
+
<syntaxhighlight lang="asm"> move.l #6,($FFFFFE10).w</syntaxhighlight>
  
 
Now, $FFFFFE10 is: #$00000006
 
Now, $FFFFFE10 is: #$00000006
Line 107: Line 107:
 
But remember that RAM addresses are treated as a hexadecimal number all the time.  So for this next example, pretend $FFFFFE10 is: #$00000000
 
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.l #10,($FFFFFE10).w</asm>
+
<syntaxhighlight lang="asm"> move.l #10,($FFFFFE10).w</syntaxhighlight>
  
  
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 of gone 8, 9, A, B...  NOT 8, 9, 10, 11...
+
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:
 
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:
  
<asm> move.l #$10,($FFFFFE10).w</asm>
+
<syntaxhighlight lang="asm"> move.l #$10,($FFFFFE10).w</syntaxhighlight>
  
  
Line 124: Line 124:
 
Let's make things slightly more difficult. Let's say that $FFFFFFE6 is #$02030405.  What would this become if we did this command?
 
Let's make things slightly more difficult. Let's say that $FFFFFFE6 is #$02030405.  What would this become if we did this command?
  
<asm> move.w #4,($FFFFFFE6).w</asm>
+
<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:
 
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:
  
<asm> move.l #4,($FFFFFFE6).w</asm>
+
<syntaxhighlight lang="asm"> move.l #4,($FFFFFFE6).w</syntaxhighlight>
  
 
$FFFFFFE6 would become #$00000004, because you moved a longword (8 nibbles).
 
$FFFFFFE6 would become #$00000004, because you moved a longword (8 nibbles).
Line 135: Line 135:
  
  
A note.  RAM address are technically a byte long. Let's say:
+
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:
  
<asm>$FFFFFE00 = #$12
+
<syntaxhighlight lang="asm">$FFFFFE00 = #$12
 
$FFFFFE01 = #$34
 
$FFFFFE01 = #$34
 
$FFFFFE02 = #$56
 
$FFFFFE02 = #$56
$FFFFFE03 = #$78</asm>
+
$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:
  
Each RAM here is being treated as a byte. But if we suddenly did this:
+
<syntaxhighlight lang="asm"> move.w #5,($FFFFFE00).w</syntaxhighlight>
  
<asm> move.w #5,($FFFFFF00).w</asm>
+
Then the above four addresses will look like this:
  
Then it will become like this:
+
<syntaxhighlight lang="asm">$FFFFFE00 = #$00
 
 
<asm>$FFFFFE00 = #$00
 
 
$FFFFFE01 = #$05
 
$FFFFFE01 = #$05
 
$FFFFFE02 = #$56
 
$FFFFFE02 = #$56
$FFFFFE03 = #$78</asm>
+
$FFFFFE03 = #$78</syntaxhighlight>
  
Same way as before.  As a longword, $FFFFFE00 technically at first was #$12345678.  After moving a word #0004 to $FFFFFE00, $FFFFFE00 became #$00055678. But as RAM addresses are a byte long, $FFFFFE01 got interfered with. Because we only moved a word, $FFFFFE02 and $FFFFFE03 remain unaffected.
+
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.
  
  
Line 162: Line 161:
 
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:
 
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>
+
<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:
 
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:
  
<asm> move.b #4,($FFFFFFE6).w ; moves 4 to the 'I am Super' RAM thingamajig, wazza wazza!</asm>
+
<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.
 
Comments are useful if you want to remember what the command is doing or just to set some notes.
Line 174: Line 173:
  
  
<asm> move.w ($FFFFFFE6).w,($FFFFFFE8).w ; copy RAM address to another</asm>
+
<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:
 
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:
  
<asm> move.l d0,($FFFFFFE8).w ; move data register to RAM address</asm>
+
<syntaxhighlight lang="asm"> move.l d0,($FFFFFFE8).w ; move data register to RAM address</syntaxhighlight>
  
  
Line 187: Line 186:
 
You don't have to move things to RAM address either:
 
You don't have to move things to RAM address either:
  
<asm> move.w #$2534,d0 ; move this hexadecimal number to d0</asm>
+
<syntaxhighlight lang="asm"> move.w #$2534,d0 ; move this hexadecimal number to d0</syntaxhighlight>
  
 
d0 will become #$2534 after the above command.
 
d0 will become #$2534 after the above command.
Line 201: Line 200:
  
 
movea / MOVE Address / Moves registers to other registers</center>
 
movea / MOVE Address / Moves registers to other registers</center>
 
 
 
  
 
===The ADD command===
 
===The ADD command===
<asm> addi.b #9,($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.
 
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.
Line 215: Line 211:
 
Assuming that $FFFFFEB8 is #$01040202, after executing the below command:
 
Assuming that $FFFFFEB8 is #$01040202, after executing the below command:
  
<asm> addi.w #9,($FFFFFEB8).w ; add the word 09 to $FFFFFEB8</asm>
+
<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.
 
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.
Line 222: Line 218:
 
Again, it doesn't need to be numbers, you can do:
 
Again, it doesn't need to be numbers, you can do:
  
<asm> add.w ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</asm>
+
<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.
 
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.
Line 229: Line 225:
 
Another example:
 
Another example:
  
<asm> add.w d0,($FFFFFFE8).w ; add data register to a RAM address</asm>
+
<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.
 
If d0 is #$1234 and $FFFFFFE8 is #$AAAA, after the command, $FFFFFFE8 will be #$BCDE.  d0 will obviously stay as #$1234.
Line 237: Line 233:
 
Here is an extra command, the addq command:
 
Here is an extra command, the addq command:
  
<asm> addq.b #6,($FFFFFEB8).w ; add 6 quickly to the RAM address</asm>
+
<syntaxhighlight lang="asm"> addq.b #6,($FFFFFEB8).w ; add 6 quickly to the RAM address</syntaxhighlight>
  
  
 
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 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> addq.b #3,($FFFFFEB8).w ; add 3 quickly to the RAM address</asm>
+
<syntaxhighlight lang="asm"> addq.b #3,($FFFFFEB8).w ; add 3 quickly to the RAM address</syntaxhighlight>
  
  
 
But however, if you want to add 9 (or more) to a RAM address, then addi has to be used:
 
But however, if you want to add 9 (or more) to a RAM address, then addi has to be used:
  
<asm> addi.b #9,($FFFFFEB8).w ; add 9 to the RAM address</asm>
+
<syntaxhighlight lang="asm"> addi.b #9,($FFFFFEB8).w ; add 9 to the RAM address</syntaxhighlight>
  
 
If you're adding a RAM address to another like so:
 
If you're adding a RAM address to another like so:
  
<asm> add.w ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</asm>
+
<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.
 
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.
Line 258: Line 254:
  
 
===The SUB command===
 
===The SUB command===
<asm> subi.b #$12,($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.
Line 264: Line 260:
 
Pretend $FFFFFEB8 is: #$15161718
 
Pretend $FFFFFEB8 is: #$15161718
  
<asm>
+
<syntaxhighlight lang="asm">
subi.b #$12,($FFFFFEB8).w ; going to take the byte $12 from $FFFFFEB8</asm>
+
subi.b #$12,($FFFFFEB8).w ; going to take the byte $12 from $FFFFFEB8</syntaxhighlight>
  
 
Now $FFFFFEB8 is: #$03161718
 
Now $FFFFFEB8 is: #$03161718
Line 272: Line 268:
 
Again, it doesn't need to be numbers, you can do:
 
Again, it doesn't need to be numbers, you can do:
  
<asm> sub.b ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</asm>
+
<syntaxhighlight lang="asm"> sub.b ($FFFFFFE6).w,($FFFFFFE8).w ; add RAM address to another</syntaxhighlight>
  
  
Line 293: Line 289:
 
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:
 
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:
  
<asm> move.w #$FF,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> move.w #$FF,($FFFFFEB8).w</syntaxhighlight>
  
 
$FFFFFEB8 becomes: #$00FF, the #$FF would be the same as inputting #255:
 
$FFFFFEB8 becomes: #$00FF, the #$FF would be the same as inputting #255:
  
<asm> move.w #255,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> move.w #255,($FFFFFEB8).w</syntaxhighlight>
  
 
Using either of these commands, ($FFFFFEB8).w will be #$FF.
 
Using either of these commands, ($FFFFFEB8).w will be #$FF.
Line 308: Line 304:
 
d0: #$00000000
 
d0: #$00000000
  
<asm> move.b #3,d0 ; going to move 03 to the data register 0</asm>
+
<syntaxhighlight lang="asm"> move.b #3,d0 ; going to move 03 to the data register 0</syntaxhighlight>
  
 
d0: #$00000003
 
d0: #$00000003
Line 315: Line 311:
 
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.
  
<asm> move.b d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</asm>
+
<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.
 
d0 will still be #$00000003, but $FFFFFEB8 will be #$03000000.
Line 322: Line 318:
 
If the command was:
 
If the command was:
  
<asm> move.w d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</asm>
+
<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.
 
d0 will still be #$00000003, but $FFFFFEB8 will be #$00030000.
Line 329: Line 325:
 
If the command was:
 
If the command was:
  
<asm> move.l d0,($FFFFFEB8).w ; going to move the data from d0 to $FFFFFEB8</asm>
+
<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.
 
d0 will still be #$00000003, but $FFFFFEB8 will be #$00000003.
Line 342: Line 338:
 
==More ASM commands==
 
==More ASM commands==
 
===The CMP command===
 
===The CMP command===
<asm> cmpi.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:
 
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:
  
<asm> cmpi.w #3,d0 ; See if d0 equals #0003 (it's cmpi here)</asm>
+
<syntaxhighlight lang="asm"> cmpi.w #3,d0 ; See if d0 equals #0003 (it's cmpi here)</syntaxhighlight>
  
<asm> cmp.w d0,d1 ; See if d0 equals the same as d1 (it's cmp here)</asm>
+
<syntaxhighlight lang="asm"> cmp.w d0,d1 ; See if d0 equals the same as d1 (it's cmp here)</syntaxhighlight>
  
  
Line 356: Line 352:
  
 
===The TST command===
 
===The TST command===
<asm> tst.w ($FFFFFEB8).w</asm>
+
<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:
 
tst stands for TeST.  This is a disguise for CMP.  Basically, 'tst' is comparing to see if the destination equals 0.  Basically:
  
<asm> tst.w ($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> tst.w ($FFFFFEB8).w</syntaxhighlight>
  
 
Is the EXACT same as:
 
Is the EXACT same as:
  
<asm> cmpi.w #0,($FFFFFEB8).w</asm>
+
<syntaxhighlight lang="asm"> cmpi.w #0,($FFFFFEB8).w</syntaxhighlight>
  
  
Line 374: Line 370:
 
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.
 
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.
  
<asm>TheCode:
+
<syntaxhighlight lang="asm">TheCode:
 
move.w #3,($FFFFFEB8).w
 
move.w #3,($FFFFFEB8).w
 
addi.w #3,($FFFFFF10).w
 
addi.w #3,($FFFFFF10).w
Line 382: Line 378:
 
Morecodinghere:  
 
Morecodinghere:  
 
move.w #3,($FFFFFEBA).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 #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.
 
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.
Line 395: Line 391:
 
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:
 
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:
  
<asm>Somecodes:
+
<syntaxhighlight lang="asm">Somecodes:
 
move.w #3,($FFFFFEB8).w
 
move.w #3,($FFFFFEB8).w
 
addi.w #3,($FFFFFF10).w
 
addi.w #3,($FFFFFF10).w
Line 404: Line 400:
 
Morecodinghere2:  
 
Morecodinghere2:  
 
move.w #3,($FFFFFEBA).w
 
move.w #3,($FFFFFEBA).w
rts</asm>
+
rts</syntaxhighlight>
 
 
 
 
Line 414: Line 410:
 
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:
 
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:
  
<asm> addq.w #3,($FFFFFF10).w
+
<syntaxhighlight lang="asm"> addq.w #3,($FFFFFF10).w
 
bra.w afarawaylabel
 
bra.w afarawaylabel
subi.w #$13,($FFFFFEB8).w</asm>
+
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:
 
.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:
Line 426: Line 422:
 
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:
 
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:
  
<asm> add1.w #3,($FFFFFF10).w
+
<syntaxhighlight lang="asm"> add1.w #3,($FFFFFF10).w
 
jmp afarfarfarawaylabel
 
jmp afarfarfarawaylabel
subi.w #$13,($FFFFFEB8).w</asm>
+
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:
 
JMP and JSR uses more space and time to perform than BRA and BSR, so try to use the BRA/BSR.  Remember:
Line 444: Line 440:
 
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.
 
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:
 
cmpi.w #3,($FFFFFEB8).w
 
cmpi.w #3,($FFFFFEB8).w
 
beq.s Itequaled
 
beq.s Itequaled
Line 452: Line 448:
 
Itequaled:
 
Itequaled:
 
addq.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 #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.
 
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:
 
cmpi.w #3,($FFFFFEB8).w
 
cmpi.w #3,($FFFFFEB8).w
 
bne.s Itdidnotequal
 
bne.s Itdidnotequal
Line 463: Line 459:
 
Itdidnotequal:  
 
Itdidnotequal:  
 
addq.b #2,($FFFFFEB8).w
 
addq.b #2,($FFFFFEB8).w
rts</asm>
+
rts</syntaxhighlight>
  
 
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.
 
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>Newlabel:
+
<syntaxhighlight lang="asm">Newlabel:
 
move.w #3,($FFFFFF10).w
 
move.w #3,($FFFFFF10).w
 
addq.w #3,($FFFFFF10).w
 
addq.w #3,($FFFFFF10).w
Line 476: Line 472:
 
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.
Line 500: Line 496:
 
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 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:
  
<asm> lea ($FFFFFF10).w,a4</asm>
+
<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:
 
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:
  
<asm> move.b #3,($FFFFFF10).w
+
<syntaxhighlight lang="asm"> move.b #3,($FFFFFF10).w
 
addq.b #2,($FFFFFF10).w
 
addq.b #2,($FFFFFF10).w
 
cmpi.b #1,($FFFFFF10).w
 
cmpi.b #1,($FFFFFF10).w
Line 511: Line 507:
 
cmpi.b #$48,($FFFFFF10).w
 
cmpi.b #$48,($FFFFFF10).w
 
beq.s Anotherlabel
 
beq.s Anotherlabel
rts</asm>
+
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:
 
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:
  
<asm> lea ($FFFFFF10).w,a1
+
<syntaxhighlight lang="asm"> lea ($FFFFFF10).w,a1
 
move.b #3,(a1)
 
move.b #3,(a1)
 
addq.b #2,(a1)
 
addq.b #2,(a1)
Line 524: Line 520:
 
cmpi.b #$48,(a1)
 
cmpi.b #$48,(a1)
 
beq.s Anotherlabel
 
beq.s Anotherlabel
rts</asm>
+
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.
 
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.
Line 531: Line 527:
 
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:
 
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:
  
<asm> move.b ($FFFFFF10).w,d0
+
<syntaxhighlight lang="asm"> move.b ($FFFFFF10).w,d0
 
move.b #3,d0
 
move.b #3,d0
 
addq.b #2,d0
 
addq.b #2,d0
Line 539: Line 535:
 
cmpi.b #$48,d0
 
cmpi.b #$48,d0
 
beq.s Anotherlabel
 
beq.s Anotherlabel
rts</asm>
+
rts</syntaxhighlight>
  
  
Line 552: Line 548:
 
This is to store data in the ROM. You can see it for art, mappings and other sorts of data. 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.
  
<asm>dc.b 0, 1,$F8, 5, 0, 0, 0, 0,$FF,$F8</asm>
+
<syntaxhighlight lang="asm">dc.b 0, 1,$F8, 5, 0, 0, 0, 0,$FF,$F8</syntaxhighlight>
  
 
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 they are hexadecimal, but if putting number 0 - 9, you don't have to put a $.
Line 568: Line 564:
 
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.
 
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.
  
<asm> clr.b ($FFFFFF10).w ; Clear the byte from $FFFFFF10</asm>
+
<syntaxhighlight lang="asm"> clr.b ($FFFFFF10).w ; Clear the byte from $FFFFFF10</syntaxhighlight>
  
 
Now, $FFFFFF10 is #$00050505.
 
Now, $FFFFFF10 is #$00050505.
  
<asm> clr.w ($FFFFFF10).w ; Clear the word from $FFFFFF10</asm>
+
<syntaxhighlight lang="asm"> clr.w ($FFFFFF10).w ; Clear the word from $FFFFFF10</syntaxhighlight>
  
 
Now, $FFFFFF10 is #$00000505.
 
Now, $FFFFFF10 is #$00000505.
  
<asm> clr.l ($FFFFFF10).w ; Clear the longword from $FFFFFF10</asm>
+
<syntaxhighlight lang="asm"> clr.l ($FFFFFF10).w ; Clear the longword from $FFFFFF10</syntaxhighlight>
  
 
Now, $FFFFFF10 is #$00000000.
 
Now, $FFFFFF10 is #$00000000.
Line 585: Line 581:
 
$FFFFFEB8 is #$00000000 and $FFFFFF10 is #$01010101. What will these two address show after this process?
 
$FFFFFEB8 is #$00000000 and $FFFFFF10 is #$01010101. What will these two address show after this process?
  
<asm>Code:
+
<syntaxhighlight lang="asm">Code:
 
addq.w #4,($FFFFFEB8).w
 
addq.w #4,($FFFFFEB8).w
 
addq.b #3,($FFFFFF10).w
 
addq.b #3,($FFFFFF10).w
Line 596: Line 592:
 
Codetwo:
 
Codetwo:
 
clr.l ($FFFFFF10).w
 
clr.l ($FFFFFF10).w
rts</asm>
+
rts</syntaxhighlight>
 
 
 
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.
 
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.
Line 606: Line 602:
 
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:
 
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:
  
<asm>word_C054: dc.w 0,$29A0, 0, $320; 0</asm>
+
<syntaxhighlight lang="asm">word_C054: dc.w 0,$29A0, 0, $320; 0</syntaxhighlight>
  
 
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:
 
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:
Line 620: Line 616:
 
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
 
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
  
<asm> move.w #$C00,(Sonic_top_speed).w</asm>
+
<syntaxhighlight lang="asm"> move.w #$C00,(Sonic_top_speed).w</syntaxhighlight>
  
 
You can change that number higher if you want him a bit faster or lower for slower!  Simples!
 
You can change that number higher if you want him a bit faster or lower for slower!  Simples!

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!