In my view, the single biggest handicap of the 65816 is its proliferation of mode flags, some of which actually change the length of instructions (using the same mnemonic and opcode) and are therefore relevant to the assembler. In the standard syntax promoted by WDC, the same syntax is used for both "long" 16-bit and "short" 8-bit immediate operands, and so the only way the assembler knows which is which is through internal state managed by assembly directives. Keeping track of this state can be very error-prone for the programmer, and the symptoms of the resulting bugs can be very odd indeed.
Another artefact of the traditional syntax is the proliferation of individual mnemonics for simply moving data around - an astonishing number given the small number of registers present. More modern assembly syntaxes use few mnemonics (sometimes just one) and specify the source and destination explicitly. Most of the non-transfer operations are more economical in their use of mnemonics.
A third fault of the traditional syntax is that the programmer cannot always explicitly specify which size of address to use, without relying on non-standard assembler extensions. With the older 6502 family, it was rare for the programmer to need an absolute 16-bit address when referring to zero-page, but the 65816 now has Direct Page which can refer to any 256-byte area in the first 64K, and the Data Bank Register can shift absolute-mode addresses by multiples of 64K; hence $0000 may dynamically refer to a different address than $00 in two different ways, either or both of which may differ from $000000. The standard 65816 extensions to the traditional 6502 syntax do not adequately address this.
The new syntax proposed below departs significantly from tradition to correct the above, aiming to support programmers of the 65816 instead of confusing them and getting in the way. A subset may of course be used on the older members of the 6502 family.
The first distinctive feature is that all directly-accessible registers have names which, if they have mode flags influencing size, differ to distinguish those sizes. This makes the assembler stateless, though disassemblers still inherently need to track (or guess) the CPU's mode to correctly determine instruction sizes. Registers which have only one size, or whose size doesn't affect instruction or transfer size, have only one name:
- Accumulator: A (8-bit), AA (16-bit).
- Indexes: X,Y (8-bit), XX,YY (16-bit).
- Stack Ptr: S (8-bit in emulation mode, 16-bit in native mode).
- Status Register: P (8-bit).
- Direct Page Ptr: D (16-bit).
- Data Bank Register: B (8-bit high).
- Program Bank Register: K (8-bit high).
- Program Counter: * or PC (16-bit)
- Immediate: expr
- Direct Page: {expr}
- Direct Page Index: {expr+X} or {expr+XX} or…
- Absolute: (expr)
- Index: (expr+X)
- Absolute Long: [expr]
- Index Long: [expr+X]
- Direct Page Indirect: ({expr})
- Direct Page Index Indirect: ({expr+X})
- Direct Page Indirect Index: ({expr}+Y)
- Direct Page Indirect Long: [{expr}]
- Direct Page Indirect Index Long: [{expr}+Y]
- Stack Pull: {++S}
- Stack Push: {S--}
- Stack Relative: {S+expr} -- the braces here indicate that the bank address is not taken from B, and the explicit offset is 8-bit.
- Stack Indirect Post-Index: ({S+expr}+Y)
- Branch Relative: *+expr
- Branch to Label: label -- assembler will compute correct relative offset, and promote to long branch if required
- Jump Absolute or Long: expr or label
- Jump Indirect: (expr)
- Jump Index Indirect (expr+X)
- Jump Indirect Long: [expr]
Code: Select all
M move, replaces LDA, LDX, LDY, STA, STX, STY, TAX, TAY, TYA, TXA, TXY, TYX, PHA, PHX, PHY, PHP, PHB, PHD, PHK, PLA, PLX, PLY, PLP, PLB, PLD, TXS, TSX, TSC, TCS, TDC, TCD, PEA, PER, PEI
A add, replaces ADC
S subtract, replaces SBC
I increment, replaces INC, INX, INY
D decrement, replaces DEC, DEX, DEY
C compare, replaces CMP, CPX, CPY
T test bits, replaces BIT
TS test and set, replaces TSB
TC test and clear, replaces TRB
LO logical OR, replaces ORA
LA logical AND, replaces AND
LX logical XOR, replaces EOR
RL rotate left, replaces ROL
RR rotate right, replaces ROR
SL shift left, replaces ASL
SR shift right, replaces LSR
SH swap halves, replaces XBA
CL clear, replaces STZ, CLC, CLD, CLV, CLI, REP
ST set, replaces SEC, SED, SEI, SEP
BPL, BMI, BVC, BVS, BNE, BEQ, BCC, BCS, RTS, RTL, RTI, BRK, COP, NOP, XCE, all perform their traditional duties.
B branch, replaces BRA and BRL
BL branch long, forces BRL
J jump, replaces JMP and JML
JL jump long, forces JML
JS jump subroutine, replaces JSR (does *not* promote to JSL, because it's not transparent to the callee)
JSL jump subroutine long, replaces JSL
MBD move block decrementing, replaces MVP
MBI move block incrementing, replaces MVN
WAIT, STOP replace WAI, STP
WDM William D. Mensch, emitted as a 1-byte instruction to permit skipping a following 1-byte instruction.
NOPL long NOP, is actually WDM as an explicit 2-byte, 2-cycle NOP; the second byte is emitted as a standard 1-byte NOP.
Many of these new mnemonics (especially M!) require one or two registers to be specified explicitly for disambiguation, even though they replace formerly "implicit mode" mnemonics. Where two operands are needed, the destination register is listed first, as is common practice for RISC assembly. For example, "LDA $00" becomes "M A, {$00}" assuming an 8-bit accumulator, or "M AA, {$00}" if 16-bit. Instructions which operate only on the accumulator, however, do not need to declare this fact (eg. "A {$00}" is a sufficient replacement for "ADC $00").
While not mandatory, the assembler may aid programmers by watching for inconsistent use of register names of different sizes without changing mode meanwhile, and emitting warning messages if such mistakes are found. Technically, the register name is only significant to the assembler when immediate operands are used, but the programmer effectively asserts and reminds himself that a particular register mode is assumed through this mechanism. Sophisticated assemblers may trace through direct branches, jumps and subroutine calls to enhance the accuracy of this check. Indirect jumps, including BRK and COP, should be assumed to terminate the consistency check, since they can be re-pointed at any code whatsoever.