module Z80::Utils::SinCos::Macros

Z80::Utils::SinCos Macros

Public Instance Methods

create_sincos_from_sintable(sincos, sintable:hl) click to toggle source

Creates a subroutine that generates a full SinCosTable from a quarter sinus table obtainable from neg_sintable256_pi_half_no_zero_lo.

sincos

Address of a SinCos table as a label or an integer.

sintable

Address of a neg_sintable256_pi_half_no_zero_lo sinus table. Can be a label, hl register or a label pointer.

NOTE

sincos must be an address on a 256 byte boundary (lower byte of sincos address must be 0); reserve 1024 bytes.

Mofifies: af, bc, de, hl, af', bc', de', hl'.

# File lib/z80/utils/sincos.rb, line 163
def create_sincos_from_sintable(sincos, sintable:hl)
    isolate do
        sincos0     addr 0, SinCos
                    ld   hl, sintable unless sintable == hl
                    ld   b, 64
                    xor  a         # -sin256(0) == -0
                    jr   skip_aget
        aloop       ld   a, [hl]   # -sin256(64-b)
                    inc  hl
        skip_aget   ex   af, af
                    ld   a, 64
                    sub  b         # a = angle (1-63)
                    exx
                    ld   b, a      # save angle (1-63)
                    ld   a, 64
                    add  b         # a + 64: cos256(a + 64) == -sin256(a)
                    call to_sincos
                    scf
                    call put_cos
                    ld   a, 128
                    add  b         # a + 128: sin256(a + 128) == -sin(a)
                    call to_sincos
                    scf
                    call put_sin
                    ld   a, 192
                    sub  b         # 192 - a: cos256(192 - a) == -sin256(a)
                    call to_sincos
                    scf
                    call put_cos
                    xor  a
                    sub  b         # (256) - a: sin256(-a) == -sin256(a)
                    call to_sincos
                    scf
                    call put_sin
                    ex   af, af    # -sin256(a)
                    neg            # sin256(a)
                    ex   af, af
                    ld   a, b      # a: sin256(a)
                    call to_sincos # CF=0
                    call put_sin
                    ld   a, 64
                    sub  b         # 64 - a: cos256(64-a) == sin256(a)
                    call to_sincos # CF=0
                    call put_cos
                    ld   a, 128
                    sub  b         # 128 - a: sin256(128 - a) == sin256(a)
                    call to_sincos # CF=0
                    call put_sin
                    ld   a, 192
                    add  b         # a + 192: cos256(a+192) == sin256(a)
                    call to_sincos # CF=0
                    call put_cos
                    exx
                    djnz aloop

                    ld   hl, 256
                    ld   [sincos0[0].cos + sincos], hl     # cos256(0) == 1
                    ld   [sincos0[64/4].sin + sincos], hl  # sin256(64) == 1
                    ld   h, -1
                    ld   [sincos0[128/4].cos + sincos], hl # cos256(128) == -1
                    ld   [sincos0[192/4].sin + sincos], hl # sin256(192) == -1
                    ret

        put_cos     inc  hl
                    inc  hl
        put_sin     ex   af, af    # sin(a)
                    ld   [hl], a   # lower sin256 byte
                    inc  hl
                    ex   af, af    # save sin
                    sbc  a         # 0 or -1 depending on CF
                    ld   [hl], a   # higher sin256 byte
                    ret
        to_sincos   sincos_from_angle sincos
                    ret
    end
end
neg_sintable256_pi_half_no_zero_lo() click to toggle source

Returns an array of 63 bytes containing the first quarter sinus table, 256-based angle, negated, fractional parts only.

for a in 1..63 -> (-256 * sin(PI * a / 128)) & 0x00FF

Suitable for create_sincos_from_sintable macro.

# File lib/z80/utils/sincos.rb, line 88
def neg_sintable256_pi_half_no_zero_lo
    (1..63).map{|a| (-Math.sin(Math::PI*a.to_f/128.0)*256.0).truncate & 0xff }
end
sincos_from_angle(sincos, th=h, tl=l, mask:nil) click to toggle source

Creates code that returns an address of SinCos entry for a given 256-based angle in the register a.

For each angle: a <= llllllhh; th => MSB SinCos address + 000000hh, tl => llllll00

sincos

Address of SinCos table, must be aligned to 256 bytes or an 8-bit register holding MSB of the SinCos address. LSB of sincos address must be 0.

Options:

  • mask

    An pptional 8-bit register holding preloaded mask value: 0xFC (0b11111100).

T-states: 30|27|24.

Mofifies: af, th, tl.

# File lib/z80/utils/sincos.rb, line 118
def sincos_from_angle(sincos, th=h, tl=l, mask:nil)
    raise ArgumentError, "sincos must be a direct address" if pointer?(sincos)
    if immediate?(sincos)
        sincos = sincos.to_i
        raise ArgumentError, "sincos address must be aligned to 256 bytes" unless (sincos & 0x00FF).zero?
    end
    raise ArgumentError, "invalid mask argument" unless mask.nil? or
            (register?(mask) and mask.bit8? and ![a, th, tl, sincos].include?(mask))
    mask = 0b11111100 if mask.nil?
    isolate do
        if register?(sincos)
            raise ArgumentError, "invalid sincos register" unless sincos.bit8? and 
                                                            ![a, th, tl].include?(sincos)
                ld   th, a
                anda mask
                ld   tl, a
                xor  th
                add  sincos
                ld   th, a
        else
            select(sincos & 0x00FF, &:zero?).then do |_|
                ld   th, a
                anda mask
                ld   tl, a
                xor  th
                add  sincos >> 8
                ld   th, a
            end.else do
                raise ArgumentError, "sincos address must be aligned to 256 bytes"
            end
        end
    end
end
sincos_table_descriptors() click to toggle source

Returns a SinCosTable descriptors.

Example:

sincos data SinCosTable, sincos_table_descriptors
# File lib/z80/utils/sincos.rb, line 96
def sincos_table_descriptors
    (0..255).map do |a|
        a = ((a & 0x3F) << 2) | ((a & 0xC0) >> 6)
        sin = (Math.sin(Math::PI*a.to_f/128.0)*256.0).truncate
        cos = (Math.cos(Math::PI*a.to_f/128.0)*256.0).truncate
        {sin: sin, cos: cos}
    end
end