Illegal Opcode SAX/AXS: A Practical Use

Posted on

By Kodiak


As a 6502 assembly coder on the Commodore 64, sooner or later you may, if trying to trim down raster time, find the use of one or more of the so-called illegal opcodes (aka undocumented or unintended opcodes) the solution to your problem.

For example, often when working with sprites you'll want to switch off some of a specific sprite's characteristics (such as MCM mode, x-expand, y-expand, char priority, etc.)

In standard code (i.e., without using illegals), you might write something like this for sprite 04 (for example) to un-expand in the x- and y-directions, and to set the MSB to 0 and to turn MCM off (which is the same as turning hi-res mode back on, obviously):

(In the snippets below, please note that ZP is my standard prefix on all Zero-Page variables, and also note, using binary when working with the VIC registers (or SID registers, for that matter) makes life so much easier than using raw hexadecimal or even decimal).

LDA ZPSPREXPANDX04
AND #%11101111
STA ZPSPREXPANDX04

LDA ZPSPREXPANDY04
AND #%11101111
STA ZPSPREXPANDY04

LDA ZPSPRMSB
AND #%11101111
STA ZPSPRMSB

LDA ZPSPRMCMMODE
AND #%11101111
STA ZPSPRMCMMODE

Each 3 instruction cluster in the above code snippet takes 8 CPU cycles, so 8 x 4 = 32 cycles, which is approximately half of a raster line (63 cycles on PAL, 65 on NTSC).

But by using the illegal opcode SAX (aka AXS or AAX, perhaps historically confusingly also known as, or conflated with, SHA), you can rewrite this as follows (and I use this method a lot in Parallaxian):

; Define the "mask" by storing it in the x-register
;
LDX #%11101111

LDA ZPSPREXPANDX04
SAX ZPSPREXPANDX04

LDA ZPSPREXPANDY04
SAX ZPSPREXPANDY04

LDA ZPSPRMSB
SAX ZPSPRMSB

LDA ZPSPRMCMMODE
SAX ZPSPRMCMMODE

This way we only need to set one mask and the same workload now takes 2 + (6 x 4) = 26 cycles, which saves 6 cycles from the standard opcode method; so, the more things you use the mask for, the more cycles you can save.

SAX thus performs an AND operation between the A-register and the X-register - without affecting the contents of either - and then stores the result in the target memory location (in the above examples, that would be in the relevant Zero Page variables).

So, in situations where you need to save every cycle of raster time, this kind of application of illegal opcodes is very desirable.

Further reading:

  1. Codebase64 resource on illegal opcodes.
  2. Pagetable article: How MOS 6502 Illegal Opcodes really work.
  3. PDF e-book: No More Secrets.

____


PS: Don't forget to check the home page regularly for more articles like this.

And of course, kindly subscribe to my YouTube channel!

Last of all, for additional short snippets of content, check out the posts on my Ko-fi page.

Kodiak