CPU.i | |
RW* (register width macros) | Instead of using ca65’s .a* and .i* directives or the “smart” mode to set current register widths, libSFX uses these macros to track CPU state. |
proc | Define procedure with separate RW state |
endproc | End procedure and restore RW state |
RW_init | Define RW state variables in current scope |
RW | Set accumulator/index register widths |
RW_assume | Assume known accumulator/index register widths without emitting any instructions |
RW_forced | Force set accumulator/index register widths (ie. |
RW_assert | Assert (at assemble time) that the specified register widths match with the state of the register tracking logic. |
RW_push | Push current register widths state to the RW stack, and optionally set new state |
RW_pull | Pull register widths state from the RW stack |
RW_pull_forced | Pull register widths state from the RW stack, always emitting rep/sep instructions |
RW_print | Print (at assemble time) the current register widths state |
RW_a_size() | Get the current accumlator register width |
RW_i_size() | Get the current index register width |
CPU register macros | |
push | Push CPU state to stack |
pull | Pull CPU state from stack |
dbank | Set data bank register (DB) |
dpage | Set direct page register (D) |
dpo() | Get address minus current direct page offset |
CPU meta instructions | |
bgt | Branch if greater than |
bsr | Relative subroutine call |
bsl | Relative long subroutine call |
add | Add (without carry) |
sub | Subtract (without carry) |
asr | Arithmetic shift right |
neg | Negate (signed integer) |
break | Break debugger |
Instead of using ca65’s .a* and .i* directives or the “smart” mode to set current register widths, libSFX uses these macros to track CPU state.
The main advantage of this approach is that rep/sep instructions will be emitted only when necessary. When relying a lot on “function inlining” via macros those tend to add up quickly. A bonus is that the state can be queried with the RW_a_size() and RW_i_size() macros, allowing for conditial assembly depending on register widths.
This tracking can only work within the same assembly unit, of course. When calling function over unit barriers there’s an RW stack and a couple of helper macros to keep those pesky register sizes in sync.
.macro call_external RW_push set:a16 ;Push current state and set accumlator ;width to 16 bits if necessary ;Only if accumulator is 8 bits wide a ;rep #$20 instruction will be emitted jsl external ;Call external subroutine that assumes ;16-bit accumulator RW_pull ;Register widths are restored as needed .endmac The 'external' subroutine looks like this: proc external, a16 ;16 bit accumulator is assumed (no instruction emitted) lda #$f00d ;So this will assemble nicely rtl endproc
Define procedure with separate RW state
This is equivalent to .proc directive, but ensures that using the RW* macros inside of the procedure does not affect tracking of CPU state anywhere outside of the procedure, and vice-versa.
It is recommended to use the ‘proc’ and ‘endproc’ macros instead of .proc and .endproc for any code using libSFX macros.
By default, code inside the procedure assumes 8-bit A and 16-bit X/Y (M=1, X=0). Optionally, you may specify other incoming register sizes as a parameter.
(See also: ‘RW_init’, ‘RW_assume’)
:in: name Procedure name :in?: widths Register widths a8/a16/i8/i16/a8i8/a16i16/a8i16/a16i8
Define RW state variables in current scope
Used by all register width macros to ensure that register state can be tracked in any scope. This also means that code inside of a .proc or .scope will not affect tracked register widths for code in the global scope, and vice-versa. (Thus, the register widths inside a nested scope are always initially a8i16.)