Labeling memory addresses will save you a lot of hassle. It’s easier to remember and reference “Object_speed” rather than its physical memory address, which could be something like $0337. How does labeling work, when are labels applied, and what’s up with that “Label already defined” error message?
What is a label?
A label, in the most literal sense of the word, is a classifying name or phrase applied to a thing. In Assembly, this “thing” is a memory address. Each memory address in the NES holds a byte (or eight bits, hence the NES is called an 8-bit system) of data. This data can either be a variable or (part of) an instruction. Without labeling addresses, you’ll have to manually figure out which memory address holds the data you need or the instruction you want to execute. To make matters even worse, during development, these memory addresses may change, as extra instructions and data are being added inside the source code, shifting things in memory.
A snippet of code, viewed in the Mesen Debugger, showing unlabeled memory addresses and instructions
By giving these memory addresses a label, you can reference these addresses a lot easier, and on top of that, the memory addresses are more or less dynamic during development, which means you don’t have to update those when you’re writing your game’s code.
How do labels work?
Note: NESmaker uses ASM6 as its assembly code compiler; this tutorial is written with this in mind. Other compilers may have different syntaxes for labels, although the general idea is the same.
Applying a label to a memory address is as simple as typing the name of the label you want, optionally (but preferably) followed by a colon. A small example from the NESmaker source code, with its label-less counterpart next to it:
$C2A2 LoadSpritePal_NMI: $C2A2 LDA sprPal,x LDA $0625,X $C2A5 STA $2007 STA $2007 $C2A8 INX INX $C2A9 CPX #$10 CPX #$10 $C2AB BNE LoadSpritePal_NMI BNE $C2A2
In the above example, LoadSpritePal_NMI
is the label. The last line, BNE LoadSpritePal_NMI
, allows the script to branch out to the memory address associated with that label. You may say LoadSpritePal_NMI
is an alias for memory address $C2A2
. As you can see in the example above, labels use no memory in ROM, but rather get replaced with the actual address upon compilation. Pop question: can you find another label in the above snippet?
Label naming conventions
You can use any name or value for your labels, although obviously you don’t want to use instructions as labels. For instance, LDA:
is a really bad idea for a label name. Ideally, you want to name your labels according to what they stand for. So the label Object_speed
should be used for the variable where object speeds are stored. Base rule: use common sense when naming things. Another important thing to keep in mind is that you can’t use label names more than once – unless they are special labels, mentioned below. If you use a label name more than once, the compiler won’t know which label to use when referencing it, resulting in a “Label already defined” error. So if you ever come across such error message, rename the label, or make it “special.”
Special labels
There are a few special labels in ASM6 though. Whenever you need a label for a branch or jump forward, you can prefix your label with the plus sign. The same goes for situations where you want to branch backward, in which case you can use a minus sign as a prefix instead. The biggest advantage of these labels is that, unlike “normally” named labels, you can use them multiple times. The compiler will branch out to the first next or previous prefixed label it encounters.
Using only a single or couple plus or minus signs is just as fine as well, although those should only be used in instances where it’s clearly obvious what they do. If there is the slightest hint of ambiguity going on, you’re best off giving your label an appropriate name though. A few quick examples:
Reset the game when your life counter hits zero:
LDA myLives ;; Load number of lives into accumulator BNE + ;; If not zero, branch out to the next plus sign JMP RESET ;; Number of lives is zero, so reset the game + ;; Code branches out here if number of lives is not zero
Write sixteen palette byte values to the PPU register:
LDX #$00 ;; Load zero into the X-register -palLoop: ;; The -palLoop label, which the code can branch to LDA sprPal,x ;; Load the X'th byte from sprPal into the accumulator STA $2007 ;; Store its value to the PPU data register INX ;; Add one to the X-register CPX #$10 ;; Check if the X-register's value equals sixteen BNE -palLoop ;; If not, branch back to the -palLoop label
This basically is all you need to know about labels up at this point. If things are unclear, or if you still have some questions regarding labels, please feel free to contact me.