This shows you the differences between two versions of the page.
Next revision | |||
— | super:asm_lessons [2015/02/28 22:59] – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== ASM Basics Class ====== | ||
+ | [[http:// | ||
+ | Welcome. This is a guide meant to teach 65c816 Assembly to people that already know a thing or two about ROMhacking. If you have some experience hacking, and you want to learn ASM with Super Metroid as a context, this is the guide for you! | ||
+ | |||
+ | You will need: | ||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * Super Metroid ROM (Preferably unheadered) | ||
+ | * Notepad, Wordpad, [[http:// | ||
+ | |||
+ | |||
+ | **NOTE: The lessons on this wiki page are edited versions of the IRC logs, and are to be edited further over time for easier reading and understanding. The original logs are available within the .zip files for each lesson.** | ||
+ | |||
+ | |||
+ | ===== Lesson 1 ===== | ||
+ | [[ http:// | ||
+ | |||
+ | Class run by [[..: | ||
+ | |||
+ | |||
+ | ASM is neither very easy nor very hard. The lazier you are, the harder it gets. Furthermore, | ||
+ | Let's start our first lesson: Learning basics.\\ | ||
+ | |||
+ | |||
+ | ==== Understanding RAM ==== | ||
+ | |||
+ | You can create a lot of new code for some pretty cool things with just simple, basic ASM. First, we must go over just how the RAM works in SM (and basically everything else computer-related). RAM is short for Random Access Memory; it is a section of memory, much like ROM, but it's not ' | ||
+ | |||
+ | In this case, the RAM is bank $7E, which can't be accessed externally (meaning you can't open it with a hex editor). Bank $7E exists only in the hardware, which is unviewable without a system that allows you to view its RAM while running a ROM. | ||
+ | |||
+ | At this point, we'd like you to open [[..: | ||
+ | |||
+ | < | ||
+ | |||
+ | The 2 bytes at $7E:09A2 keep track of every powerup you have equipped, while the 2 at $7E:09A4 keep track of every powerup collected. Why are there 2? Because the game needs to keep track of everything you have picked up, but also needs to check whether or not you actually have that item equipped. | ||
+ | |||
+ | >" | ||
+ | |||
+ | True that... But, those 2 **bytes** are made up of a lot more **bits**. If there are 2 bytes, there are 16 bits. (Note: $ before a number is used to indicate [[wp> | ||
+ | |||
+ | Bit masking is great; it means you can easily assign a single number to any possible combination of items. Say bit 3 is reserved for screw attack, bit 2 is reserved for Morph Ball, bit 1 is reserved for Spring Ball, and bit 0 is reserved for Varia. Then, when you have Morph ball equipped, the value would be 0100 ($04). If you have Morph Ball AND Varia, it would be 0101 ($05). Those four binary bits can be set in all possible combinations with just half of a byte. We have 2 bytes to use, which is 16 binary bits, so there can be a number for every possible on/off or equipped/ | ||
+ | |||
+ | Anyway, if you look a bit further down the RAM map, at $09C2, you'll find Samus' current health. (Note: 0x before numbers is also used to indicate [[wp> | ||
+ | |||
+ | >" | ||
+ | |||
+ | That's correct. Now, if we wanted to create a code that refilled samus' health, we would load the value from $09C4, and store it to $09C2. That's all; that would completely refill Samus' health. The thing about RAM is, it not only stores the status of the game, but it //affects// it. If you write to RAM while the game is running, it will affect the game, because the game also reads from RAM instead of just storing things there. This is the basis of ASM; we edit the values in RAM to create the effects we want in the game. | ||
+ | |||
+ | |||
+ | ==== Writing your first basic ASM ==== | ||
+ | |||
+ | All right, time to start writing basic code! | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | Opened that and had a look? Basically, LDA means LoaD into Accumulator, | ||
+ | |||
+ | Also, we tell the game to load a value in two ways: immediate (direct value) and relative (from an address). For instance, immediate: | ||
+ | |||
+ | < | ||
+ | |||
+ | Notice the prefix '#' | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | I probably should' | ||
+ | |||
+ | Okay, question: how would you restore your missiles instead of energy? Look at the RAM map and try to answer before reading on.\\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | >" | ||
+ | \\ | ||
+ | \\ | ||
+ | This is done exactly the same way if you want to restore missiles, supers, power bombs or reserve tanks. Just LDA/STA the max/current values for those accordingly. All right, time to start explaining how to [[wp> | ||
+ | |||
+ | |||
+ | ==== Compiling with Xkas ==== | ||
+ | |||
+ | Okay, let's write an .asm file! I prefer changing the file type with windows, so please make a new file called Test.asm. Also make a copy of your clean ROM (unheadered is best) and rename it Test.smc. When you're done, open the .asm file. Copy the code from Example 1, paste it into your Test.asm file, and save. Now, if you drag the .asm file over the Xkas.exe, the code should be automatically compiled to the ROM. To test if the code works, use SMILE to place an Air-Fool X-ray tile with BTS 03 somewhere (you should be able to touch it in-game) and quickmet. Do a shinespark or anything else to lose a bit of your health, then touch the block. It should restore your energy; does it work properly? | ||
+ | |||
+ | |||
+ | ==== Homework ==== | ||
+ | |||
+ | All right, now i have your first task. It's simple though, so don't freak out.\\ | ||
+ | See if you can make a block that fully restores your missiles, super missiles, and powerbombs, then sets your energy to 1. | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | |||
+ | |||
+ | ===== Lesson 2 ===== | ||
+ | [[ http:// | ||
+ | |||
+ | Class run by [[..: | ||
+ | |||
+ | Today, I'm just gonna go through the branching opcodes, which are used to make variable code. Got your RAM map open? | ||
+ | |||
+ | |||
+ | ==== Conditional Branching ==== | ||
+ | |||
+ | First, we're gonna learn about conditional branching, which is done by comparing values at addresses with other addresses or immediate values. | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | As you can see, it's the same basic energy-restoring routine from last lesson, but this time it checks to see if Samus' | ||
+ | |||
+ | >" | ||
+ | |||
+ | Almost... BNE actually means Branch if Not Equal; there wouldn' | ||
+ | |||
+ | |||
+ | ==== BRA/JMP vs. JSR/JSL ==== | ||
+ | |||
+ | >" | ||
+ | |||
+ | Sort of. JSR and JSL take the game to a subroutine, which means that upon hitting RTS/RTL, the game jumps back to the instruction straight after the JSR/JSL. BRA works similarly to a JMP; when the game hits RTS/RTL, it doesn' | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | If we follow the JSR SUBROUTINE path, the game enters the routine normally. It checks Samus' energy and compares it to her max energy; if the two are NOT equal, then the routine branches across to refill it, and then ends the routine. Then, game would then jump back to the instruction straight after the original JSR, which is another RTS. Now, let's assume her energy was full. The game compares them, and they' | ||
+ | |||
+ | Now let's follow the JMP SUBROUTINE path. This still takes the game to the next routine, but upon hitting an RTS, it // | ||
+ | |||
+ | |||
+ | ==== INC, DEC, and Counters ==== | ||
+ | |||
+ | Okay, now what I want to show you is how to to refill energy over time, using a timer. I'll show you 2 new instructions: | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | Now things become a bit more complicated. The routine checks to see if Samus has full energy first; if she does, the routine ends and that's it. If she doesn' | ||
+ | |||
+ | |||
+ | ==== Homework ==== | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | This is homework. Restore Samus' energy every 5th frame, at the cost of 1 missile every 20th frame. If you run out of missiles or have full energy, do nothing. | ||
+ | |||
+ | |||
+ | ==== Questions from students ==== | ||
+ | |||
+ | >"I have another question, if you don't mind..." | ||
+ | |||
+ | Sure. | ||
+ | |||
+ | >" | ||
+ | |||
+ | JMP specifies an address. BRA is relative, while JMP is absolute. This is what gives JMP that freedom. If you use BRA, you'd put something like BRA $10 to jump ahead #$10 bytes; if you use JMP, you would JMP $8000 to jump to $8000 in the current bank, or you can specify a bank like JMP $8FFB00. | ||
+ | |||
+ | |||
+ | |||
+ | ===== Lesson 3 ===== | ||
+ | [[ http:// | ||
+ | |||
+ | Class run by [[..: | ||
+ | |||
+ | Okay, this time I'm gonna get into selective bits (useful for checking for specific items, events, etc). | ||
+ | |||
+ | |||
+ | ==== Working with Selective Bits ==== | ||
+ | |||
+ | Basically, each address in the ram is split up into bits. 1 byte contains 8 bits; FF in hex is equal to 11111111 in binary. Single bits are pretty much only used as flags, rather than having all 8/16 work as a whole value or counter. If you look in the RAM map at $09A2 (equipped items), this location is actually just 16 bits (2 bytes) used as flags for each item being equipped. Since an item can only be on or off, each item is assigned a single bit, which is either on or off. Using these bits, EVERY combination of equipped items can be represented with just 2 bytes. | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | Here's an example of checking for a specific item. Again, this is for fool xray BTS $03. This code loads the equipped item bits, then tests for a certain bit (in this case, bit 1, which is used by the Varia suit). The next part confuses most people; BEQ and BNE are used when testing for specific bits, but they are used in reverse. Branch Not Equal will branch if the bit came back true (the bit was SET), whereas Branch EQual will branch if the bit was false (RESET, or 0). Let's assume that Samus has Varia suit (0001), screw attack (0008), and morph (0004) equipped. LDA $09A2 would yield the bits of 0000 0000 0000 1101. 1101 = #$000D. Therefore, the value at $09A2 would be #$000D. Now, if we test for Varia against these equipped item bits: | ||
+ | |||
+ | < | ||
+ | 0000 0000 0000 1101 | ||
+ | 0000 0000 0000 0001 | ||
+ | = | ||
+ | 0000 0000 0000 1100 | ||
+ | </ | ||
+ | |||
+ | Since this " | ||
+ | |||
+ | Now I'm going to get into turning specific bits on or off. Let's say you wanted to make a block that gives you an item, or make a barrier that removes or unequips items. If you only want to take or give a certain item, you'd use the opcodes AND or ORA respectively. AND is used to turn specific bits off, while ORA sets them. If those bits were already off/on, the instruction would do nothing. With these 2 instructions, | ||
+ | |||
+ | < | ||
+ | LDA # | ||
+ | AND # | ||
+ | A = # | ||
+ | </ | ||
+ | |||
+ | In simple form, the result is that only bits that are set in BOTH values will return true, otherwise they will be reset. Another example: | ||
+ | |||
+ | < | ||
+ | LDA #$FF0F | ||
+ | AND #$F0F0 | ||
+ | A = #$F000 | ||
+ | </ | ||
+ | |||
+ | Make sense?\\ | ||
+ | The other instruction, | ||
+ | |||
+ | < | ||
+ | LDA # | ||
+ | ORA # | ||
+ | A = # | ||
+ | </ | ||
+ | < | ||
+ | LDA #$00F0 | ||
+ | ORA #$F000 | ||
+ | A = #$F0F0 | ||
+ | </ | ||
+ | |||
+ | I'll write up some examples. | ||
+ | |||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | That example creates 2 blocks. The first (BTS $03 fool xray) will give and equip the Varia suit when touched. The second (BTS $04), when touched, will unequip Gravity suit. Although, the only problem with equipping/ | ||
+ | |||
+ | Well, i think that covers the simple opcodes for selective bits. | ||
+ | |||
+ | ==== Teacher' | ||
+ | "This was a short one, though there' | ||
+ | Just some notes about the log and asm. | ||
+ | |||
+ | You can use SMILE to find specific bits used by items. It is the " | ||
+ | |||
+ | $ is used to denote a hexidecimal value. ie $F000, $EA, $1034\\ | ||
+ | % is used to denote a binary value. ie %1100110011001100, | ||
+ | # is used to indicate an immediate value, or number value. Without this symbol, any of the above would be read as an address.\\ | ||
+ | |||
+ | AND #$FFFF does nothing\\ | ||
+ | ORA #$0000 does nothing\\ | ||
+ | BEQ will ALWAYS branch after a BIT #$0000\\ | ||
+ | BNE will always branch after a BIT #$FFFF, UNLESS the value being tested against is # | ||
+ | |||
+ | |||
+ | ==== Homework ==== | ||
+ | :!: [[ asm_lessons# | ||
+ | |||
+ | I do realize that this has pretty much zero usefulness in any game, but it's good practice. :-P |