This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
asm_stylesheet [2015/10/25 13:10] – [One Event Per Line] black_falcon | asm_stylesheet [2016/09/04 15:07] (current) – removed black_falcon | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | |||
- | ====== Super Metroid Assembly Style Guide ====== | ||
- | |||
- | **A guide that helps you correctly writing and organizing your ASM code to optimize your workflow and keep you motivated! :) | ||
- | Please note that this is __NO ASM TUTORIAL!__. \\ | ||
- | You have to know how ASM code works and how it is stored in the game's ROM in order to make use of this guide. | ||
- | Also don't take the example code I used too seriously, it's just for educational purposes, so it doesn' | ||
- | |||
- | |||
- | ====== TABLE OF CONTENTS ====== | ||
- | |||
- | |||
- | Introduction | ||
- | |||
- | 1. Getting the right setup **done** | ||
- | - Recommended Tools | ||
- | - Setting up N++ | ||
- | |||
- | 2. Basic code stucturization **done** | ||
- | - One event per line | ||
- | - Branches | ||
- | - Folding Blocks Of Code | ||
- | - Commentary | ||
- | |||
- | 3. xkas command structurization **in progress** | ||
- | |||
- | 4. Tips for Optimizing Your Workflow using N++ Macros **pending** | ||
- | - When Disassembling | ||
- | - When Copying a string of bytes from the ROM | ||
- | |||
- | |||
- | ====== INTRODUCTION ====== | ||
- | |||
- | |||
- | |||
- | ASM coding became incredibly popular over the last years. | ||
- | I can very well remember the old vanilla hacks that were amazing long ago at the time when asm wasn't really a thing. | ||
- | Nowadays the amount of patches and asm resources has grown considerably, | ||
- | Naturally, the more ideas are presented in a hack, the more people want to come up with new ideas and code their own thing. | ||
- | The way these people write their code may be very different from each other, resulting sometimes in inefficient or just cluttered looking | ||
- | code that is not really viewer-friendly. | ||
- | |||
- | The goal of this guide is to give ASM coders an idea about how I think organized code should look like and how it helps you optimize it. | ||
- | Especially if you're going to release the source later, so other coders have to understand what's going on from a different perspective, | ||
- | Now you should know that this is by far no " | ||
- | I too am open for suggestions and improvements.\\ | ||
- | In this guide I will as well cover and if necessary expand ways to improve your workflow in regards to Super Metroid ASM coding, | ||
- | including what tools I'm using and how I disassemble large portions of code in a matter of minutes, xkas friendly using the tools listed below. | ||
- | |||
- | Anyway this introduction has been a bit long so // | ||
- | |||
- | ---- | ||
- | |||
- | ====== 1. Getting The Right Setup ====== | ||
- | |||
- | ===== Recommended Tools ===== | ||
- | |||
- | __Essential stuff any ASM coder should have before even reading this guide:__ | ||
- | |||
- | * [[super_metroid: | ||
- | * [[http:// | ||
- | * [[http:// | ||
- | * [[ http:// | ||
- | * Hex Editor - [[http:// | ||
- | * calculator - anything that can calculate and convert between 0x <> 0d <> 0b (you better know what those mean)\\ | ||
- | |||
- | __Tools recommended that once used you don't want to miss anymore:__ | ||
- | |||
- | * [[https:// | ||
- | * [[http:// | ||
- | |||
- | |||
- | __Even more essential stuff and the reason I get out of bed every morning:__ | ||
- | |||
- | * Coffee | ||
- | |||
- | __Other things:__ | ||
- | |||
- | * a brain - well, not quite as essential maybe, some people don't have one, so you don't necessarily need it | ||
- | |||
- | ---- | ||
- | ===== Setting up Notepad++ ===== | ||
- | |||
- | N++ is one of, if not the most versatile and popular code viewers out there. It supports lots of different programming languages such as batch or C++. | ||
- | If the language you need is not supported, you can simply define it nice and easy using the custom language feature. | ||
- | Lucky for you I already made one for SNES ASM so you don't have to bother. | ||
- | To add the ASM language stylesheet, open up N++ and go to Language > User-defined (at the very bottom) | ||
- | Alternatively you can simply click the toolbar icon for it (it's a window with a lightning drawn into it) | ||
- | |||
- | {{: | ||
- | |||
- | In the opened window, click ' | ||
- | |||
- | {{: | ||
- | |||
- | Now the default theme has a white background, which is a pain in the eyes (that' | ||
- | To change that, click '' | ||
- | If you're still not happy with the slight blueish background or whatever color your theme' | ||
- | under '' | ||
- | Try to mess with it a bit, and when you're comfortable, | ||
- | |||
- | ---- | ||
- | ====== 2. Basic code structure ====== | ||
- | |||
- | ===== One Event Per Line ===== | ||
- | |||
- | The following example shows how I used to write code when I just got started: | ||
- | |||
- | LDA # | ||
- | STA $099C ;code to run | ||
- | LDA # | ||
- | STA $07B5 ; | ||
- | LDA !value ; | ||
- | ASL a ; 2 ;doorout pointer will be my custom code (!doorout) 2 + DOOROUT | ||
- | ADC $07B5 | ||
- | TAX | ||
- | LDA $8F0000, | ||
- | STA $078D ; | ||
- | LDA #$7777 | ||
- | STA !flag2 | ||
- | LDA !Istay | ||
- | STA !nextinst | ||
- | LDA #$000A ;09 = execute $099C | ||
- | ;STA $0998 ;also initiate room transition | ||
- | RTS | ||
- | |||
- | ... and this is a good example of how **not** to write your code. You can see it takes up lots of lines and overall is not very nice to look at. | ||
- | So instead of using one opcode + argument per line, try to use **one event/ | ||
- | |||
- | Now what's a statement? | ||
- | ---- | ||
- | **A statement is a combination of opcodes and arguments, which creates either a check, performs an operation or causes any kind of change to a value and things such as RAM adresses and hardware registers.** | ||
- | ---- | ||
- | |||
- | Most statements consist of three steps: //reading >> calculating/ | ||
- | |||
- | an example statement would be an addition operation: | ||
- | |||
- | LDA $09C2 ;reading | ||
- | CLC | ||
- | ADC # | ||
- | STA $09C2 ; | ||
- | |||
- | Now this can be organized to have the operation on a single line using colons as separators: | ||
- | |||
- | LDA $09C2 : CLC : ADC #$0001 : STA $09C2 | ||
- | |||
- | "What, you can actually do that? | ||
- | |||
- | Unorganized check statement(also referred to as conditional branching): | ||
- | |||
- | LDA $09C2 ; | ||
- | AND # | ||
- | CMP # | ||
- | BEQ Branch ;/ | ||
- | |||
- | Organized check: | ||
- | |||
- | LDA $09C2 : AND #$FF00 : CMP #$0E00 : BEQ Branch | ||
- | |||
- | Now what if there' | ||
- | |||
- | LDA $09C2 | ||
- | CMP #$0E00 | ||
- | BEQ Branch | ||
- | LDA $09C2 | ||
- | CLC | ||
- | ADC #$0001 | ||
- | STA $09C2 | ||
- | Branch: | ||
- | LDA #$0E00 | ||
- | STA $09C2 | ||
- | |||
- | Easy, just remember one statement per line: | ||
- | |||
- | LDA $09C2 : CMP #$0E00 : BEQ Branch | ||
- | LDA $09C2 : CLC : ADC #$0001 : STA $09C2 | ||
- | Branch: | ||
- | |||
- | Notice that the branch labels can be put into the same line right before the code without looking cluttered.\\ | ||
- | Though please **avoid** merging multiple statements all into one line like this: | ||
- | |||
- | LDA $09C2 : CMP #$0E00 : BEQ Branch : LDA $09C2 : CLC : ADC #$0001 : STA $09C2 | ||
- | Branch: LDA #$0E00 : STA $09C2 | ||
- | |||
- | Other examples of statements/ | ||
- | |||
- | * Loading an immediate value followed by a jump to a subroutine that uses it (and optionally gives output based on it): | ||
- | |||
- | LDA #$0001 : JSL $8081DC : BCS Branch | ||
- | |||
- | * Loops: | ||
- | |||
- | LDX #$000A | ||
- | back: LDA $12 : CLC : ADC $07A5 : STA $12 | ||
- | DEX : CPX #$0000 : BPL back | ||
- | |||
- | Three statements make up this loop: Initializing X, calculating/ | ||
- | Now there are some special cases of statements, especially when it comes to checking several bits in a respective order like button inputs or items: | ||
- | |||
- | LDA $09A2 | ||
- | BIT #$0020 | ||
- | BNE GRAV | ||
- | BIT #$0001 | ||
- | BNE VAR | ||
- | BRA OUT | ||
- | |||
- | For this there are multiple ways, one is using tabs/spaces for alignment: | ||
- | |||
- | LDA $09A2 : BIT #$0020 : BNE GRAV | ||
- | BIT #$0001 : BNE VAV | ||
- | BRA OUT | ||
- | |||
- | Another way would be to simply give LDA $09A2 its own line: | ||
- | |||
- | LDA $09A2 | ||
- | BIT #$0020 : BNE GRAV | ||
- | BIT #$0001 : BNE VAV | ||
- | BRA OUT | ||
- | |||
- | Simple return statements ('' | ||
- | |||
- | CLC : RTS | ||
- | SEC : RTL | ||
- | |||
- | Status register changes ('' | ||
- | |||
- | SEP #$30 | ||
- | LDX #$0A : LDA #$04 : STA $0000,x | ||
- | REP #$30 | ||
- | BRA OUT | ||
- | |||
- | **Stack operations** should be put together onto a **single line**, for easy recognition of the order they are in | ||
- | (wrong order of pushing onto/ | ||
- | |||
- | PHX : PHY : PHP | ||
- | REP #$30 | ||
- | [code] | ||
- | PLP : PLY : PLX ;always have to be pulled in the reversed order in what they were pushed | ||
- | RTL | ||
- | |||
- | Block movement organization: | ||
- | |||
- | PHB | ||
- | LDA #$00FF : LDX #$8000 : LDY #$1000 : MVN $897E | ||
- | PLB | ||
- | RTS | ||
- | |||
- | ---- | ||
- | ===== Branches===== | ||
- | |||
- | There are three ways to define a branch in xkas that in the end do the same thing.\\ | ||
- | The first one requires you to know how many bytes an opcode' | ||
- | |||
- | REP #$20 | ||
- | LDA $09A2 : AND #$0001 : BNE $02 | ||
- | BRA $06 | ||
- | LDA #$0020 : TRB $09A2 | ||
- | RTS | ||
- | |||
- | But as you can see, this is quite awkward and would not really make sense, since xkas does calculate the pc offset for you using labels.\\ | ||
- | Also actually seeing the branch destination is always better, right? | ||
- | So the second way would be to simply be using **labels to name the branches**: | ||
- | |||
- | REP #$20 | ||
- | LDA $09A2 : AND #$0001 : BNE VAR | ||
- | BRA OUT | ||
- | VAR: STZ $09A2 | ||
- | OUT: RTS | ||
- | |||
- | Alright, up to this point, that should be common knowledge, even for ASM beginners. | ||
- | Now if you have a ton of code and are tired of naming all your branches, there' | ||
- | Xkas has a nifty feature that lets you use sublabels (+/- signs) for branches, depending on whether the branching direction is positive or negative. | ||
- | |||
- | " | ||
- | A new sublabel group is started immediately after a label is declared automatically. | ||
- | A +/- label can be up to 3 levels deep, e.g. +, ++, +++, -, --, ---. | ||
- | They overwrite their pc offsets immediately after being redefined. | ||
- | Useful for very short loops, when even something like .loop would become redundant in a long routine." | ||
- | |||
- | This is a quote from the xkas help file. | ||
- | (which you should have read! Seriously if you didn' | ||
- | Now what does that mean exactly? | ||
- | |||
- | LDA $12 : CMP #$000A : BEQ Go | ||
- | LDX # | ||
- | Back: LDA $12 : CLC : ADC #$0001 : STA $12 | ||
- | DEX : CPX #$0000 : BPL back | ||
- | Go: RTS | ||
- | |||
- | Let's assume you're using the above kind of loop several times.\\ | ||
- | It still would require you to think of appropriate and unique label names all the time so you don't get things confused. | ||
- | That wouldn' | ||
- | You'd have to write the same label for each branch if your routine checks for a lot of things and branches to " | ||
- | |||
- | |||
- | |||
- | This is the loop example, but this time using sublabels: | ||
- | |||
- | LDA $12 : CMP #$000A : BPL + | ||
- | LDX # | ||
- | - LDA $12 : CLC : ADC #$0001 : STA $12 | ||
- | DEX : CPX #$0000 : BPL - | ||
- | + RTS | ||
- | |||
- | Okay, now I'm perfectly aware that using them in a single loop is kinda lame and doesn' | ||
- | Like this: | ||
- | |||
- | LDA $09C2 : CMP #$0010 : BPL + ;this branch... | ||
- | LDX # | ||
- | - LDA $09C2 : CLC : ADC #$0002 : STA $09C2 | ||
- | DEX : CPX #$0000 : BPL - | ||
- | + LDA $09C6 : CMP #$000A : BEQ + ;...goes to here | ||
- | LDX #$000A | ||
- | - LDA $09C6 : CLC : ADC #$0002 : STA $09C6 | ||
- | DEX : CPX #$0000 : BPL - | ||
- | + LDA $09CA : CMP #$1FFF : BEQ + | ||
- | LDX #$1FFF | ||
- | - LDA $09CA : CLC : ADC #$0005 : STA $09CA ; | ||
- | DEX : CPX #$0000 : BPL - ; | ||
- | + RTS | ||
- | |||
- | Awesome, isn't it? | ||
- | Okay this criss-crossing might look confusing at first, but try to understand where each of the branches go to.\\ | ||
- | Now what does "up to 3 levels deep" mean? | ||
- | It means that there can be multiple branch levels, so you have to use just as much levels for the sublables: | ||
- | |||
- | REP #$20 | ||
- | LDA $09A2 : AND #$0001 : BNE + ;branch 1, level 1 | ||
- | BRA ++ ; | ||
- | + STZ $09A2 ; | ||
- | ++ RTS ; | ||
- | |||
- | Note that branch 2 comes **before** the destination 1 meaning in order to actually assign branch 2 to destination 2, you have to use ++ (level 2) | ||
- | This is regardless of whether destination 2 is located before or after destination 1! | ||
- | |||
- | The same counts for - signs, and for going back in the pc count: | ||
- | |||
- | LDY #$000A | ||
- | -- LDX # | ||
- | - LDA 1000,x : CLC : ADC $8000,y : STA $1000, | ||
- | DEX : DEX : CPX #$0000 : BPL - ;branch 1 | ||
- | DEY : DEY : CPY #$0000 ; BPL -- ; | ||
- | |||
- | Please note that you can't mix the use of labels and sublabels, like this: | ||
- | |||
- | LDY #$000A | ||
- | - LDX # | ||
- | back: LDA 1000,x : CLC : ADC $8000,y : STA $1000, | ||
- | DEX : DEX : CPX #$0000 : BPL back ; | ||
- | DEY : DEY : CPY #$0000 ; BPL - ;branch 2 | ||
- | |||
- | xkas will give out an error, saying negative branch too long, as it isn't able to calculate the location for - due to interference with a normal label. (At least as of version v.06, dunno if this has been fixed already) | ||
- | |||
- | ---- | ||
- | ===== Folding Blocks Of Code ===== | ||
- | |||
- | You can use the brackets/ | ||
- | Xkas will ignore them, so you're free to use them anywhere. | ||
- | |||
- | __Example: | ||
- | |||
- | JSR $A4D6 | ||
- | { ;this line will be the only one left visible if collapsed | ||
- | ASL A : TAX | ||
- | LDY $A4EB, X : BNE BRANCH | ||
- | SEC : RTS | ||
- | BRANCH: | ||
- | CLC : RTS | ||
- | } | ||
- | |||
- | In N++, it'll look like this: | ||
- | |||
- | {{: | ||
- | |||
- | Collapsed: | ||
- | |||
- | {{: | ||
- | |||
- | |||
- | Not really much to say, except you should have as many open brackets as closed ones, else N++ won't collapse them correctly. | ||
- | By default, if you open a asm file in N++, everything is unfold. | ||
- | To fold/ | ||
- | |||
- | |||
- | ===== Commentary ===== | ||
- | |||
- | |||
- | Commentary is very important for getting and keeping a better understanding of what's going on. | ||
- | But it's easy to cross the line and soak your code with unnecessarily detailed comments about things. \\ | ||
- | It helps just about as much as having bareboned code without any extra word, no one's gonna read anything. | ||
- | However since I showed you the concept of one event/ | ||
- | |||
- | This code has too much info, which is only useful for absolute ASM beginners: | ||
- | |||
- | LDA $09A2 ;Load item address | ||
- | AND # | ||
- | CMP # | ||
- | BEQ VAR ;branch if varia is equipped | ||
- | |||
- | Using the one-statement-per-line rule: | ||
- | |||
- | LDA $09A2 : AND #$0001 : CMP #$0001 : BEQ VAR ;check if varia is equipped | ||
- | |||
- | You notice that it doesn' | ||
- | If you don't know how specific opcodes work exactly, feel free to ask, that's what Metconst is for after all!\\ | ||
- | Now again you don't HAVE to use comments, only if you feel things need a short explaination. | ||
- | |||
- | |||
- | |||
- | ====== 3. xkas command structurization (WIP) ====== | ||
- | |||
- | |||
- | In the ASM stylesheet, all the xkas commands are blue by default. | ||
- | Most commonly used commands are '' | ||
- | '' | ||
- | |||
- | __Example: | ||
- | |||
- | { org $808000 ; | ||
- | DW $0000 : DW $0001 : DL $7E8000 : DB $FF | ||
- | } | ||
- | |||
- | __Example using labels:__ | ||
- | |||
- | { org $BF8000 : TABLE: | ||
- | |||
- | DW $0001 : DW $0002 : DW $0004 : DW $0008 | ||
- | DW $0010 : DW $0020 : DW $0040 : DW $0080 | ||
- | DW $0100 : DW $0200 : DW $0400 : DW $0800 | ||
- | DW $1000 : DW $2000 : DW $4000 : DW $8000 | ||
- | } | ||
- | |||
- | __Xkas fill functions organzation: | ||
- | |||
- | org $808000 : padbyte $FF : pad $808010 ; | ||
- | fillbyte $ff : fill 16 | ||
- | |||
- | __Repeatedly used opcodes:__ | ||
- | |||
- | Instead of writing a bunch NOPs and ASLs all over the place like this: | ||
- | |||
- | NOP : NOP : NOP | ||
- | NOP : NOP | ||
- | NOP | ||
- | NOP | ||
- | LDA $07A5 : AND #$00FF : ASL : ASL : ASL : ASL : STA $0AF6 | ||
- | |||
- | ...you can let xkas create pseudo upcodes using # and a number (always decimal): | ||
- | |||
- | NOP # | ||
- | LDA $07A5 : AND #$00FF : ASL #4 : STA $0AF6 | ||
- | |||
- | But be careful when writing to status register ('' | ||
- | |||
- | SEP #$20 | ||
- | LDA #$10 : STA $7ECD20 | ||
- | REP # | ||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | |||
- | ====== 4. Tips for Optimizing Your Workflow using N++ Macros (Pending) ====== | ||
- | |||
- | More to come soon! | ||
- | |||
- | |||
- | |||
- | |||
- | |||