The
# is needed when you want to tell the assembler/6502 to use an immediate value (think "literal number") in the operand itself, rather than "an address (in RAM or ROM) to read/write to/from". It's not a prefix used to represent base of numbers (e.g.
$ for hexadecimal or
% for binary), or "types" of numbers on a per-number or per-variable basis. Better explained in code with comments:
Code:
FOO = $01
BAR = $02
UGH = $0a
DERP = %11111110
HOBER = 26
lda FOO ; equivalent to lda $01 -- reads value from RAM location $01 and puts it into the accumulator -- assembles to a5 01
lda #FOO ; equivalent to lda #$01 -- puts the literal value 1 ($01) into the accumulator -- assembles to a9 01
lda FOO+BAR ; equivalent to lda $01+$02, i.e. lda $03 -- assembles to a5 03
lda #FOO+BAR ; equivalent to lda #$01+$02, i.e. lda #$03 -- assembles to a9 03
lda DERP ; equivalent to lda %11111110 or lda $fe -- assembles to a5 fe
lda #DERP ; equivalent to lda #%11111110 or lda #$fe -- assembles to a9 fe
lda HOBER ; equivalent to lda 26 or lda $1a -- assembles to a5 1a
lda #HOBER ; equivalent to lda #26 or lda #$1a -- assembles to a9 1a
lda HOBER*2 ; equivalent to lda 26*2 or lda 52 or lda $1a*2 or lda $34 -- assembles to a5 34
lda HOBER*100 ; equivalent to lda 26*100 or lda 2600 or lda $1a*100 or lda $0a28 -- assembles to ad 28 0a
lda #HOBER*100 ; equivalent to lda #26*100 or lda #2600 -- will fail to assemble because 2600 is too large; values can only be 0-255 (8-bit)
lda $HOBER ; undetermined -- behaviour may vary per assembler depending on parser; might do lda $26 or might do lda $1a or might throw an error
lda #$HOBER ; undetermined -- behaviour may vary per assembler depending on parser; might do lda #$26 or might do lda #$1a or might throw an error
lda #(UGH+16)*8 ; let's expand variables, use decimal rather than hexadecimal, and break the math down:
; ($0a+16)*8 == (10+16)*8 == 26*8 == 208
; thus this is the equivalent to lda #208 or lda #$d0 -- assembles to a9 d0
lda ((UGH+3)*$1000)+10 ; let's expand variables and break the math down piece by piece, in order of operation, and do some base conversion:
; ($0a+3) == (10+3) == 13 == $0d
; ($0d*$1000) == $d000
; $d000+10 == $d00a
; thus this is the equivalent to lda $d00a -- assembles to ad 0a d0
It's important to understand two things here (#1 may help you):
1. The CPU uses
completely different instructions/opcodes depending on what addressing mode you're using. For example,
lda #$05 uses immediate addressing, which assembles to bytes
a9 05. But
lda $05 would use zero page addressing and assemble to
a5 05 -- note the difference of the first byte! Same goes for absolute addressing, where
lda $1234 would assemble to
ad 34 12.
2. The mathematics you see above is being done
at assemble-time, calculated the assembler and NOT done at run-time by the CPU. There's a world of difference between the two. Doing mathematics on the 6502 at run-time is a substantially more involved process (simple addition/subtraction is easy, multiplying/dividing by 2/4/8/16/32/64/128 is easy, anything else is much more advanced).
Does this help?
One thing that will confuse you: you'll see parenthesis
() used for mathematical order-of-operation, but you'll also see it in instructions like
lda ($16),y. The latter isn't assemble-time mathematics being done by the assembler -- it's real/actual 6502 code using a form of indirect addressing (already discussed). This can add to some confusion as I'm sure you can imagine.
"So how does the assembler know when to use
() for instructions and when to use it for math?" It varies per assembler, and you have to read the assembler's documentation to get a feel for it. Some assemblers like NESASM actually use brackets
[] to represent the instruction-level addressing mode, and uses parenthesis purely for assemble-time mathematics. Other assemblers are smart/intelligent enough to know what you want.
If this paragraph is confusing, then I'll make it simple: in most assemblers you'd say
lda ($16),y or
lda ($16,x) or
jmp ($1234) to do indirect addressing, while in NESASM you'd need to write
lda [$16],y or
lda [$16,x] or
jmp [$1234]. NESASM tends to be "the odd man out" in this respect, and this major difference can cause a lot of problems for both newbies and experienced programmers.