In the spirit of the last post, here's some "goodies" of mine. They are pseudo-ops that just reduce the number of lines of code, however one should always be cautious because they can possibly emit alot of instructions, so they can create a false impression of program compactness. Also, one may call it heresy that those pseudo-ops are more than 3 characters, and that it may look like another cpu instruction set: no, my goal isn't to implement another processor in a 6502, it is just to save a bit of typing while having some fun with ca65's macro system.
push/pop:Code:
.macro push a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15
.ifnblank a0
.if .xmatch({a0},p) .or .xmatch({a0},P)
php
.else
.if .match({a0},x)
txa
.elseif .match({a0},y)
tya
.elseif .match(.left(1,{a0}),=)
lda #>(.right(.tcount({a0})-1,{a0}))
pha
lda #<(.right(.tcount({a0})-1,{a0}))
.elseif !(.match({a0},a))
lda a0
.endif
pha
.endif
push a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15
.endif
.endmacro
.macro pop a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15
.ifnblank a0
pop a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15
.if .xmatch({a0},p) .or .xmatch({a0},P)
plp
.else
pla
.if .match({a0},x)
tax
.elseif .match({a0},y)
tay
.elseif !(.match({a0},a))
sta a0
.endif
.endif
.endif
.endmacro
The idea here is that you specify what you want to push/pull to/from the stack; A, X, Y and P can be specified along with memory locations.
push pushes all the args in the order specified and
pop pops them in reverse order, so the following:
Code:
push A,X,Y,somevar
pop A,X,Y,somevar
...will save and restore correctly the specified registers and the memory location. Of course, A should be first (or second, if P is first) in the list if one want to save A, or else A won't be correctly saved/restored without a warning.
Also,
push accepts regular immediate (8-bit) arguments but also 'absolute' arguments with
=: one can push absolute addresses, and those addresses are pushed in the same order that
jsr and
brk does. So the following is a slow and convoluted way to jump at
somelabel:
Code:
; using rts
push =somelabel-1
rts
; using rti
push =somelabel, P
rti
mov/movw:Code:
.macro mov dest, src
lda src
sta dest
.endmacro
.macro movw dest, src
.local sepd, seps
sepd .set 0
seps .set 0
.if .match({.right(2,{dest})},{,x}) .or .match({.right(2,{dest})},{,y})
sepd .set 2
.endif
.if .match({.right(2,{src})},{,x}) .or .match({.right(2,{src})},{,y})
seps .set 2
.endif
.if .match(.left(1, {src}),#)
mov {.left(.tcount({dest})-sepd,dest)+0 .right(sepd,dest)}, #<(.right(.tcount({src})-1,{src}))
mov {.left(.tcount({dest})-sepd,dest)+1 .right(sepd,dest)}, #>(.right(.tcount({src})-1,{src}))
.else
mov {.left(.tcount({dest})-sepd,dest)+0 .right(sepd,dest)}, {.left(.tcount({src})-seps,src)+0 .right(seps,src)}
mov {.left(.tcount({dest})-sepd,dest)+1 .right(sepd,dest)}, {.left(.tcount({src})-seps,src)+1 .right(seps,src)}
.endif
.endmacro
mov is trivial so won't explain, but is included here nevertheless because
movw uses it. The latter moves a word (which can be immediate) into some address; it assumes words are stored into consecutive byte addresses.
dest and
src can even use absolute or zeropage indexed addressing mode if you use curly braces:
Code:
movw {$4000,X}, {APU_virtualregs,X}
Limitations: it can only use A for transfers, it does not fit well with the use of the struct-of-array "idiom"
, and if you try to use indexed indirect addressing, it will silently break because it will be converted into absolute indexed (oh nevermind, it gives errors fortunately, but one should be cautious when using parens, it should not begin with an open parens, that how ca65 tells if it's absolute or indirect addressing).