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: 
										
										
- Codebase64 resource on illegal opcodes.
- Pagetable article: How MOS 6502 Illegal Opcodes really work.
- PDF e-book: No More Secrets.
									____
									
									If you value my work and want to support me, a small donation via PayPal would be nice (and thanks if you do!)