Bithacks: Difference between revisions
Runic Rain (talk | contribs) |
Runic Rain (talk | contribs) |
||
Line 11: | Line 11: | ||
<u>outputs:</u> A | <u>outputs:</u> A | ||
<pre> | <pre> | ||
STA $00 | STA $00 | ||
ASL $00 | ASL $00 | ||
ROR | ROR | ||
</pre> | </pre> | ||
Line 32: | Line 32: | ||
?end: | ?end: | ||
endmacro | endmacro | ||
</pre> | |||
'''Absolute Value''' ''(5 bytes / 6 cycles)'' | |||
<br> | |||
<u>inputs:</u> A | |||
<br> | |||
<u>outputs:</u> A | |||
<pre> | |||
macro abs() | |||
BPL ?plus | |||
EOR #$FF | |||
INC | |||
?plus: ; only 3 cycles if branch taken | |||
endmacro | |||
</pre> | |||
'''Absolute Value (SEC)''' ''(4 bytes / 4 cycles)'' | |||
<br> | |||
<u>inputs:</u> A, (Carry Set) | |||
<br> | |||
<u>outputs:</u> A | |||
<pre> | |||
; compared to the branching version this is 1 byte smaller | |||
; it's either 2 cycles slower/faster depending on branch taken | |||
EOR #$7F | |||
; SEC ; the instant you add this in it becomes worse than the branching version | |||
SBC #$7F | |||
</pre> | |||
'''Magnitude/Extents Check''' ''(~7 bytes / 12 cycles)'' | |||
<br> | |||
<u>inputs:</u> A | |||
<br> | |||
<u>outputs:</u> (none) | |||
<pre> | |||
; asks "Is [A] on the zero-side of value [X] or the far side?" | |||
; good for magnitude checks, smaller *AND* faster than alternatives | |||
; NOTE: in the event that it is exactly [X] it will have that value at branch | |||
; doesn't need to be an indexed CMP but is most useful this way | |||
; this can be used to combine the BPL and BMI checks for both signs into one | |||
SEC : SBC Extents,x | |||
EOR Extents,x | |||
BMI .zero_side | |||
.far_side: | |||
; do things | |||
.zero_side: | |||
; do things | |||
Extents: | |||
db -$23, $23 | |||
</pre> | </pre> | ||
Revision as of 10:43, 12 April 2021
Bithacks are optimization tricks that utilize information in bits and bit manipulation
to accomplish their tasks. Usually they work in a slightly non-obvious way, (the most famous being the fast inverse sqrt), and bit manipulation in general is harder on the 65c816. To that end here is a collection of some useful tricks.
Note: cycle counts are intended to be a worst case measure.
Math Bithacks
Signed Division By 2 (5 bytes / 10 cycles)
inputs: A
outputs: A
STA $00 ASL $00 ROR
Signed Division By 2n (6+2n bytes / 5+2n cycles)
inputs: A, n
outputs: A
; signed division by two, n times macro SignedDiv_2N(n) BMI ?negative LSR #<n> BRA ?end ?negative: LSR #<n> ORA #($FF<<(8-<n>)) ; sign extension ?end: endmacro
Absolute Value (5 bytes / 6 cycles)
inputs: A
outputs: A
macro abs() BPL ?plus EOR #$FF INC ?plus: ; only 3 cycles if branch taken endmacro
Absolute Value (SEC) (4 bytes / 4 cycles)
inputs: A, (Carry Set)
outputs: A
; compared to the branching version this is 1 byte smaller ; it's either 2 cycles slower/faster depending on branch taken EOR #$7F ; SEC ; the instant you add this in it becomes worse than the branching version SBC #$7F
Magnitude/Extents Check (~7 bytes / 12 cycles)
inputs: A
outputs: (none)
; asks "Is [A] on the zero-side of value [X] or the far side?" ; good for magnitude checks, smaller *AND* faster than alternatives ; NOTE: in the event that it is exactly [X] it will have that value at branch ; doesn't need to be an indexed CMP but is most useful this way ; this can be used to combine the BPL and BMI checks for both signs into one SEC : SBC Extents,x EOR Extents,x BMI .zero_side .far_side: ; do things .zero_side: ; do things Extents: db -$23, $23
Misc. Tricks
As this list grows tricks here will be consolidated into their own sections. Clever optimization tricks that aren't necessarily what someone might personally call a "bithack" are okay here as well!
Direction As Index (4 bytes / 6 cycles)
inputs: A
outputs: A
; Ever wonder why facing flags are 0=right and 1=left? This is why. It's incredibly cheap. ASL : ROL : AND #$01 ; get facing as index