blargg wrote:
This is assuming you manually allocate zero-page, rather than just use .zeropage and let the assembler do it. For a list of addresses, I do like your idea of an "address list" mode.
I was once among the many 6502 programmers who were in the habit of defining zero page usage with EQUs rather than DS (uninitialized storage, it just advances the ORG address) with an ORG on the zero page. Bad habit or not, the former is pretty common.
However, for zero page locations that are input/output parameters of ROM routines it makes sense to use EQUs, e.g.
Code:
ROMSUB1PARM1 = $FD
ROMSUB1PARM2 = $FE
ROMSUB1 = $F000
ROMSUB2PARM1 = $FB
ROMSUB2 = $F123
blargg wrote:
So for the stack-pointer-in-direct-page-register example, you'd just do LDA <5 to use direct-page addressing mode.
That's less typing, but it still has to be done numerous times. Starting from the "Example Implementing strtok()" section in the wiki (just after the tsc-tcd concept is introducted), by my quick count, there are 9 instances (LDA, CMP, etc. but not STA/STX/STY) where you'd have to do this. All but one block of assembly code (which only contains two JSRs and several .word directives) from that point in the article contains at least one instance. And these are intentionally short and simple examples. Most procedures you write are going to have at least one instance.
Another example is that address-address can be either a constant or an address, e.g.
Code:
LDX #0
LOOP LDA STR,X
JSR SUB
INX
CPX #STR_END-STR ; address - address = constant
BCC LOOP
RTS
STR .byte "some string"
STR_END
vs.:
Code:
DP_BASE = $1000 ; address
VAR1 = DP_BASE ; address
VAR2 = DP_BASE+2 ; address
SUB1
;
; Set up VAR1 for later
; The D register doesn't point at DP_BASE yet
;
ASL
STA VAR1 ; uses abs (or long) addressing here
RTS
SUB2
;
; Set the D register to DP_BASE (e.g. for speed)
;
LDA #DP_BASE
TCD
;
; Start by copying VAR1 to VAR2
;
LDA VAR1-DP_BASE ; address - address = dp address
STA VAR2-DP_BASE
That's not the only way to do either of those things, but I think both examples are reasonable approaches.
kc5tja wrote:
Therefore, maybe it's high time that the 6502 and 65816, perhaps the most non-orthogonal architectures on the face of the planet today, just drop the idea of "addressing modes" all-together, and instead switch to explicit mnemonics for everything. Truely, this would make writing assemblers a *LOT* easier.
You can make a case for either. I would argue against putting the zp/abs (or zp/abs/long on the 65816) distinction into the instruction mnemonic, even if the ,X ,Y (,X) (),Y etc. are. It's not as big a deal on the 65816, but on the 6502/65C02, if someone's example is written for abs and I want to use zp (or vice versa), I'd prefer to just make the single change of LABEL = $1000 to LABEL = $00 rather than edit numerous instructions. Yeah, this could be handled with a boatload of macros (one for each instruction) that check if the argument (label) is > $FF, for those variables that can be either abs or zp. But that seems kinda one step forward, one step back to me.
Speaking of macros, they can be simpler without the distinction in the instruction, e.g.
Code:
MOV2A MAC ; 6502/65C02 (.n are the macro parameters)
LDA .1
STA .2
LDA 1+.1
STA 1+.2
EOM
This can handle any combination of abs, zp, abs,X, zp,X, and abs,Y. When the distinction is in the instruction, with really good macro capability, the macro could be invoked with something like MOV2A AX VAR1 AX VAR2 and then the macro would append the AX to the LDA and STA to form LDAAX and STAAX, but that's still a more complicated macro.
The assembler only has to be written once. I'm not sure that making the assembler itself easy to write is all that important.
kc5tja wrote:
Code:
ldxiw 16 ; LDXIW = LDX #nnnn
nextBit: ; LDXIB = LDX #nn
lsrd multiplier ; it'd do away with .a8/.i16 etc.
Some assemblers avoid the .a16/.a8 nonsense by using # to indicate 8-bit immediate data and ## to indicate 16-bit immediate data, e.g.
Code:
LDA #0 ; assembles A9 00
LDA ##0 ; assembles A9 00 00
GARTHWILSON wrote:
That's why my Forth assembler is that way. It made it much easier.
(snip)
The whole assembler is small enough to keep in memory all the time.
Um, 6502 assemblers usually are pretty simple in Forth in any case.
I wrote an ugly 6502 prefix assembler (I should've just made it postfix and I should've implemented labels differently) which is both a simplification and an expansion of the FIG-Forth 6502 assembler. There are colon definitions for JMP and JSR, and only 4 defining words: one for addressing modes, one for implied instructions, one for relative branch instructions, and one for the remaining instructions. It includes branch range checking, and ORG capability, i.e. ORG need not equal HERE (it was intended as a cross assembler for the NMOS 6502). It doesn't include forced zp or abs, though that would be really simple to add (probably only a few dozen bytes or so). It only takes 775 bytes of dictionary space and 690 bytes of head space (heads are separated in this particular indirect-threaded Forth). A significant percentage of it consists of using the defining words to define the instructions (ADC, AND, etc.) It's not as simple as what you're doing, but as poorly designed as it is, it was still pretty easy to do.