Gameboy implementation of fast, reasonably accurate 8-bit interpolation. Licensed under zero-clause BSD/ISC/OpenBSD license.
- ;; Copyright 2021 AlaskanEmily
- ;;
- ;; Permission to use, copy, modify, and/or distribute this software for any
- ;; purpose with or without fee is hereby granted, provided that the above
- ;; copyright notice and this permission notice appear in all copies.
- ;;
- ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- ;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- ;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- ;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- ;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- ;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- ;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- ;; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- ;; POSSIBILITY OF SUCH DAMAGE.
- ; ASM implementation of this C routine:
- ; unsigned interpolate(uint8_t t, uint8_t a, uint8_t b){
- ; uint8_t value, diff, dx;
- ; if(a > b){
- ; value = a;
- ; a = b;
- ; b = value;
- ; t = 255 - t;
- ; }
- ; diff = b - a;
- ; value = a;
- ; do{
- ; dx = diff & 1;
- ; diff >>= 1;
- ; if(t & 0x80)
- ; value += diff + dx;
- ; }while(t <<= 1);
- ; return value;
- ; }
- ; The extra offset of dx here is a very rough form of rounding.
- ; If you do not include this, then the results will be about 1/16th too low.
- ; Calculates the interpolation at a (0 to 255) of b to c
- ; Returns in a.
- interpolate:
- push de
- ld d, a ; Save the T-value in d
- ; Swap the interpolation range and T value if necessary
- ld a, c
- sub b
- jrnc @$interpolate_inner
- ; Swap b and c
- ld e, b
- ld b, c
- ld c, e
- ; Negate the T-value
- ld a, 0xFF
- sub d
- ld d, a
- $interpolate_inner:
- ld a, c
- sub b
- ld e, a
- ; e has the difference now.
- ld a, b
- $interpolate_bit:
- ; srl will set the carry bit, and bit doesn't modify it.
- ; This lets us use adc rather than add plus an inc and a jump below.
- srl e
- bit7 d
- jrz @.no_add
- adc e
- .no_add:
- sla d
- jrnz @$interpolate_bit
- pop de
- ret