module ZXLib::Gfx::Sprite8::Macros

ZXLib::Gfx::Sprite8 Macros.

Sprite8::Macros require:

macro_import MathInt
macro_import Gfx

Public Instance Methods

gfx_sprite8_calculate_coords(outofscreen: :ret, **nsopts) { |eoc| ... } click to toggle source

Creates a routine that calculates coordinates and prepares registers for Sprite8.draw_sprite8.

hl

An address of sprite data.

a

A height of a sprite in pixel lines: [1, 192].

a'

A width of a sprite in bytes ((pixel width + 7) / 8): [1, 32].

bc

A horizontal (x) coordinate of a sprite's top-leftmost pixel as a 16-bit twos complement signed integer: [-32768, 32767] where the screen area is between: [0, 255].

de

A vertical (y) coordinate of a sprite's top-leftmost pixel as a 16-bit twos complement signed integer: [-32768..32767] where the screen area is between: [0, 191].

f

Flags specifying a drawing method (see below).

Options:

  • outofscreen

    What to do if the whole sprite is out of the screen area - if no block is given then provide a branching label, otherwise ret is being executed.

  • block

    Should create code to execute when the whole sprite is out of the screen area. The eoc label provided to the block points after the calculating routine. The code must not fall through!

Any other option is being passed over to the block namespace.

Drawing methods:

  mode:    OR    SET     XOR   AND+OR
    CF:     0      1       0        1
    ZF:     0      0       1        1
assuming accumulator contains the non-zero number of sprite lines
how to: ora a  ora a    cp a     cp a
                 scf              scf

See Sprite8.draw_sprite8 for the description of output registers.

NOTE

The outofscreen is invoked only when it would be impossible to formulate valid arguments for Sprite8.draw_sprite8, which is exactly when (x > 255) or (x + pixel width <= 0) or (y > 191) or (y < -255).

Uses: af, af', bc, de, hl, stack: max 4 bytes.

# File lib/zxlib/gfx/sprite8.rb, line 176
def gfx_sprite8_calculate_coords(outofscreen: :ret, **nsopts, &block)
  isolate do |eoc|
            ex   af, af       # store CF and sprite height
            push af           # sprite width
            ld   a, d
            ora  a
            jp   Z, vnext1    # 0 <= de < 256
            inc  a            # d == 0xff
            jr   NZ, quit1    # de < -256
            # xor  a          # a is already 0
            sub  e
            jr   Z, quit1     # de == -256
            ld   d, a         # skip
            ld   e, 0         # y = 0
            jp   hnext1
    quit1   pop  af
    if block_given?
            ns(:quitoos, **nsopts) do
              yield eoc
            end
    elsif label?(outofscreen)
    quitoos jp   outofscreen
    else
            ret
    end
    vnext1  ld   a, e         # 0 <= de < 192
            cp   192
            jr   NC, quit1    # de >= 192
    hnext1  ld   a, b
            ora  a            # 0<= bc < 256
            jp   Z, hnext2    # bc on screen
            inc  a
            jr   NZ, quit1    # bc < -256

            xor  a
            sub  c            # x = -x
            jr   Z, quit1     # x == -256
            anda 0xf8
            jr   Z, fskip     # -8 < x < 0
            rrca
            rrca
            rrca
            ld   b, a         # -x / 8
            pop  af           # sprite width
            sub  b            # width -= -x / 8
    if block_given? or label?(outofscreen)
            jr   C, quitoos   # width <  -x / 8
            jr   Z, quitoos   # width == -x / 8
    else
            ret  C            # width <  -x / 8
            ret  Z            # width == -x / 8
    end
            ex   af, af       # new width
            push af           # height + CZ
            push de
            ld   d, 0
            ld   e, a         # height
            jr   NC, mulh.muls1
            jr   NZ, mulh.muls1 # height*2 (andor) C=1 Z=1
    mulh    mul8(d, e, b, tt:de, clrhl:false, double:true) # sprite address+= height * (-x / 8)
            pop  de
            pop  af
            ex   af, af       # height + CZ
            push af           # new width

    fskip   ld   a, c
            ora  0xf8
            ld   c, e
            ld   e, a
    hnext2  ex   de, hl       # sprite -> de
            ld   b, h         # skip first
            ld   h, l         # h = y & 0xff
            ld   l, c         # l = x & 0xff
            pop  af
            ld   c, a         # sprite width
  end
end
gfx_sprite8_calculate_screen_address(scraddr:SCREEN_ADDRESS, subroutine:false) click to toggle source

Creates a routine that calculates the screen address for Sprite8.draw_sprite8.

The h and l registers should contain the pixel coordinates as described in Sprite8.draw_sprite8.

As a result of executing the routine the hl registers will hold the calculated screen address and the c register will hold the special bit right shift number.

If the horizontal pixel coordinate (x) is positive the bit shift will be between 0 and 7. If the x coordinate is negative and x is between -7 and -1 the bit shift will be between 8 and 14.

Options:

  • scraddr

    A screen memory address which must be a multiple of 0x2000 as an integer or a label.

  • subroutine

    Whether to create a subroutine.

# File lib/zxlib/gfx/sprite8.rb, line 82
def gfx_sprite8_calculate_screen_address(scraddr:SCREEN_ADDRESS, subroutine:false)
  isolate do |eoc|
                ld   a, h
                cp   192
                jp   C, hvertical
                anda 7              # -7..-1 -> 1..7
                jr   Z, noadjust    # sanity check
                add  7              # 1..7 -> 8..14
    noadjust    ld   c, a           # C: negshift (0, 8..14)
                ytoscr l, ah:h, al:l, t:b, scraddr:scraddr
    if subroutine
                ret
    else
                jr   eoc
    end                           # HL<: yx, HL>: screen, C>: shift (0..7), B: temp
    hvertical   xytoscr h, l, ah:h, al:l, s:c, t:b, scraddr:scraddr
                ret if subroutine
  end
end
gfx_sprite8_draw(draw_sprite8=self.draw_sprite8, scraddr:SCREEN_ADDRESS, calculate:CALCULATE_SCREEN_ADDRESS, **nsopts) { |eoc| ... } click to toggle source

Creates a subroutine that calculates the screen address before jumping to Sprite8.draw_sprite8.

This subroutine should be used if you want to access the calculated screen address just before executing draw_sprite8.

draw_sprite8

A label addressing the Sprite8.draw_sprite8 subroutine.

block

A block that creates a code to execute when the screen address has been calculated. The address is available in hl registers. Additionally the c register contains the special bit shift number. See gfx_sprite8_calculate_screen_address for details. The code must preserve the content of c, de and af' registers.

See Sprite8.draw_sprite8 for the description of input registers and usage.

Options:

  • scraddr

    A screen memory address which must be a multiple of 0x2000 as an integer or a label.

  • calculate

    If this option is set to :subroutine then the screen address calculation routine is being called (at draw_sprite8.calc_scr_addr) instead of inlining it. In this instance the Sprite8::CALCULATE_SCREEN_ADDRESS constant must be set to :subroutine before requiring the sprite8 module.

Any other option is being passed over to the block namespace.

# File lib/zxlib/gfx/sprite8.rb, line 123
def gfx_sprite8_draw(draw_sprite8=self.draw_sprite8, scraddr:SCREEN_ADDRESS, calculate:CALCULATE_SCREEN_ADDRESS, **nsopts, &block)
  isolate do |eoc|
                push bc             # save width and skip
    if calculate == :subroutine
                call draw_sprite8.calc_scr_addr
    else
                gfx_sprite8_calculate_screen_address(scraddr:scraddr)
    end
                push hl             # HL: screen addr, C: negshift (0..14)
    if block_given?
                ns(**nsopts) do
                  yield eoc
                end
    end
                jp   draw_sprite8.addr_on_stack
  end
end
gfx_sprite8_flip_horizontally(subroutine:false) click to toggle source

Creates a routine that flips sprite pixel data horizontally (mirrors sprites).

hl

An address immediately following the source sprite data (sprite data address + sprite data size).

de

A target address where the mirrored sprite data should be placed. The target memory area must not overlap with the source.

c

A width of a sprite in bytes ((pixel width + 7) / 8) (0 is 256).

b

A height of a sprite in pixel lines (0 is 256).

Options:

  • subroutine

    Whether to create a subroutine.

After the routine finishes its operation:

  • de

    Will hold a memory address immediately following the flipped sprite data (flipped sprite data address + sprite data size).

  • hl

    Will hold a memory address of the source sprite data.

  • b

    Will hold a provided sprite height in pixel lines.

  • c

    Will be 0.

NOTE

Sprite data must be laid out column-wise as expected by Sprite8.draw_sprite8. If data include the sprite mask the provided height should be twice the sprite height. In this instance the maximum sprite height to be mirrored is 128 pixel lines.

Uses: af, bc, de, hl, stack: 4 bytes.

# File lib/zxlib/gfx/sprite8.rb, line 278
def gfx_sprite8_flip_horizontally(subroutine:false)
  isolate do
    column_loop   push bc
                  sub_from b, h, l
                  push hl
                  scf
    row_loop      ld   c, [hl]
                  inc  hl
                  rl   c
    bit_swap_loop rra
                  sla  c
                  jr   NZ, bit_swap_loop
                  ld   [de], a
                  inc  de
                  djnz row_loop
                  pop  hl
                  pop  bc
                  dec  c
                  jr   NZ, column_loop
                  ret if subroutine
  end
end