Hello all, this might come across as a noobish question and I apologize if it's been asked before (or I am missing something obvious), but I've noticed that nesasm always uses absolute addressing, even if I want to address the zero page. This happens regardless of whether I use labels or addresses, so both lda $00 and lda label (with label being a variable at $00) result in an AD 00 00 opcode. Also, sty $00, x results in an assembler error, as nesasm interprets $00 as an absolute address.
Now, my question is whether I can do something against it (other than manually writing every line as .db $A5, $00, for lda,zp), or whether it's about time I switched assemblers.
Cheers, -scrimpeh
This is a known difference in syntax between NESASM and other popular 6502 assemblers. To switch to zero page, use explicit zero page addressing: lda <$FF or lda <some_var.
NESASM requires < before a zero page variable due to the fact that it's derived from a PC Engine/TurboGrafx 16 assembler (on that system, Zero Page is in the $2000 area).
My suggestions for alternative assemblers include asm6 (simpler) and ca65 (more complex).
tepples wrote:
This is a known difference in syntax between NESASM and other popular 6502 assemblers. To switch to zero page, use explicit zero page addressing: lda <$FF or lda <some_var.
freem wrote:
NESASM requires < before a zero page variable due to the fact that it's derived from a PC Engine/TurboGrafx 16 assembler (on that system, Zero Page is in the $2000 area).
My suggestions for alternative assemblers include asm6 (simpler) and ca65 (more complex).
Oh wow, this is really simple. Thanks for the quick answers, too!
freem wrote:
asm6 (simpler)
ASM6 on the other hand
never uses absolute addressing for $0000-$00FF. If you happen to need that for timing reasons or something, you'll either have to use .db (for the opcode) and .dw (for the address) or a mirror of ZP ($0800-$08FF, for example).
tokumaru wrote:
freem wrote:
asm6 (simpler)
ASM6 on the other hand
never uses absolute addressing for $0000-$00FF.
good to know, I thought it would do that if you just put $00xx. (guess that's something I should look into for my fork)
Yeah, that's the one annoying ASM6 flaw that affects me in any significant way. It's still my assembler of choice, though.
I don't think I ever needed absolute addressing for $0000-$00FF on the NES, but it happens quite often when I'm programming kernels for Atari 2600 games.
What's the preferred way of handling it?
Assembler directive? (.zopt on/off)
Per instruction? (lda.w $ff, lda >$ff, lda $00ff ...?)
My preferences are unprefixed $00ff (which might not be simple/easy to handle; it's been a bit since I've looked at the code) or an a:$00ff prefix like ca65 supports. The former would be my preferred solution in a perfect world, and the latter would be my preferred solution in the real world
It does make the parsing more tricky, I'd have to add some special cases. 15 = %1111 = $f = $00000f, they all just evaluate to a number. What happens if you want to throw in arithmetic or labels?
Matter of opinion, but this is my take on it, with some actual historical assembler reference documentation to back it up:
lda $00 through
lda $ff (more specifically: a 2-digit address, or an address that's less than 256 in value (in case some crazy decided to do
lda 257)) should get assembled as the zero page version. Addresses $100 or larger should get assembled as 16-bit addresses. For equates or mathematical operations, the equates or math should get expanded first (which has to be done anyways since they're assemble-time and not run-time generated) and the decision based off the calculated result. If someone wants to force the 16-bit equivalent, then they should be able to do either a)
lda $0000 to
lda $00ff, or, b)
lda >$00 to
lda >$ff (use of
> modifier). If you support things like
.w pseudo-ops then that should also force 16-bit equivalents (e.g.
lda.w $ff would assemble to the 16-bit address).
Justification for this stems from the model/methodology implemented by the Apple IIGS ORCA/M assembler.
Here's the manual. The relevant section that describes the above is on PDF pages 335/336 (actual document pages 309/310).
Likewise, the Apple II Merlin 8/16 assembler uses the same model but with a different syntax/modifier/methodology (and fairly risky/scary -- it's accomplished by adding any character to the end of the operand, other than "D" or "L", i.e.
LDAq $ff would use 16-bit addressing, as would
LDA- $ff or
LDAq $ff. I believe they did this to be compatible with other assemblers).
Here's that manual. The relevant section is PDF page 97 (document page 85).
tokumaru wrote:
I don't think I ever needed absolute addressing for $0000-$00FF on the NES, but it happens quite often when I'm programming kernels for Atari 2600 games.
If you happen to need that for timing reasons or something on the 2600, you'll either have to use .db (for the opcode) and .dw (for the address) or a mirror of ZP ($0100-$01FF, for example).
koitsu wrote:
lda $00 through lda $ff (more specifically: a 2-digit address, or an address that's less than 256 in value (in case some crazy decided to do lda 257)) should get assembled as the zero page version. Addresses $100 or larger should get assembled as 16-bit addresses.
Unless the address is a label defined later or imported from another file. For the latter situation, ca65 has
.globalzp vs.
.global.
Quote:
For equates or mathematical operations, the equates or math should get expanded first (which has to be done anyways since they're assemble-time and not run-time generated) and the decision based off the calculated result. If someone wants to force the 16-bit equivalent, then they should be able to do either a) lda $0000 to lda $00ff
$0000 to $00FF expands to $0 to $FF. I'd recommend things like
a: modifiers on an address expression to force its size.
Quote:
lda >$00 to lda >$ff (use of > modifier).
Where I come from,
>x means
(x & $FF00) >> 8, the "high" byte of a value.
We're not supposed to used hardcoded addresses in instructions, so the solution has to be something that works well for variables and expressions too.
being able to turn ZP addressing on and off is nice if for some reason you want this to affect several instructions, but you really need per instruction control in order to write timed code efficiently.
Personally I'd go for the operator suffix (.w), because it doesn't mess with the operand. When I'm coding for the Atari 2600, I may need to write to the same address but using different addressing modes each time depending on how many cycles I want. If the solution was something about how many 0's the address has, would I have to define the same variable twice? Once as $07 and again as $0007, giving them different names? should I add $0000 to it to force absolute addressing? none of that makes any sense. Modifying the operator is much cleaner.
tepples wrote:
If you happen to need that for timing reasons or something on the 2600, you'll either have to use .db (for the opcode) and .dw (for the address) or a mirror of ZP ($0100-$01FF, for example).
Exactly, which is why I suggested the same be done the NES, if necessary. So far I've been using +$100 after variable names in my Atari 2600 programs, but I don't really like this.
I don't think it really requires a special case for the assembler. You can just do it as data:
Code:
.byte $AD ; LDA absolute
.word foo
If needed frequently, it can become a macro very easily.
Yes, this and other workarounds have been suggested. It's not like this is impossible to do, but the code is more readable if you don't have to do things like this, and not having to look up opcodes will save some time too.
Would the preferred solution to be an assembler that optimizes all instructions for zeropage unless the object is denoted with a $ character preceding the object's name/value?
$ is reserved for the hexadecimal constant prefix. I've seen that ca65 optimizes where possible (address is known to be less than $100 before it is used) unless a: is specified.
A modifier is certainly the best solution, because it can be applied to anything (hardcoded addresses, variables and expressions) but I never heard of this syntax before (a:). Is it common or just a CA65 thing? I've seen .w appended to the operator a few times before, so I'm not sure which is more common. When you force an addressing mode like this, you're in fact modifying both the operator (different opcode) and the operand (16 bits instead of 8), so both make sense to me.
In 68000 assembly, instructions use .W and the like to specify the size of the data (8, 16, or 32 bits), not the size of the address.
tepples wrote:
In 68000 assembly, instructions use .W and the like to specify the size of the data (8, 16, or 32 bits), not the size of the address.
If that's the case, then this really isn't the best option for 6502 assembly.
Doesn't ASM6 support macros? It shouldn't be too difficult to write macros for instructions that need to read/write full address or zp addressing.
Just let it known that I personally happen to prefer the way NESASM does it; zero page addressing is marked explicitly.
How about:
Code:
LDA (zeroPageAddress + $800), y
That's not a valid 6502 instruction. The value within () needs to be < $100. There is no indirect indexed LDA that takes a pointer from outside the zero-page. (Only indirect JMP can do this.)
LDA (zp), Y
My bad I meant:
Code:
LDA zeroPageAddress+$800, y
To force opcode $B9.
Well, yes you can use mirrored addresses for ZP on the NES, though I don't think it's a good way to do this.
In NESASM it will be absolute anyway if you don't use the < prefix, so it's not relevant.
On ca65 you can force an absolute address with an a: prefix. I think it's better to use this instead of relying on mirroring.
Code:
LDA a:zeroPageAddress, y
For those who found this thread.
I have a patch barely discussed in this
topicI'd like to get some feedback, and I could fix it to fit community needs.