module Z80::Utils::Shuffle::Macros

Z80::Utils::Shuffle Macros

for i from 0 to length − 1 do
    j ← random integer such that 0 ≤ j ≤ i
    if j ≠ i
        target[i] ← target[j]
    target[j] ← source[i]

Shuffle macros require:

macro_import MathInt

Public Instance Methods

shuffle_bytes_source_max256(next_rng=nil, target:hl, length:a, source:nil, &next_rng_blk) click to toggle source

Creates a routine to shuffle an array of bytes.

After the shuffle is performed hl points to the memory address immediately following the shuffled table.

Modifies: af, bc, de, hl. Stack depth: 6 bytes or 8 if next_rng is used.

next_rng

An address of a random number generator routine. The routine should return an 8-bit random number in the accumulator. If next_rng is nil the block of code with the RNG routine is expected instead.

Options:

  • target

    An address of the target array as a label, a pointer or hl.

  • length

    An 8-bit length of an array in the range of 1..256 (0 is 256) as a label, pointer or a register.

  • source

    A source function. If nil then identity is assumed: source[i] => i, otherwise it should be an address of a source function routine which expects an argument i in the register c and MUST PRESERVE registers: hl and de. Function is expected to return the source value in the accumulator.

# File lib/z80/utils/shuffle.rb, line 44
def shuffle_bytes_source_max256(next_rng=nil, target:hl, length:a, source:nil, &next_rng_blk)
    unless source.nil? or (address?(source) and !pointer?(source))
        raise ArgumentError, "source should be nil or an address" 
    end
    unless next_rng.nil? or (address?(next_rng) and !pointer?(next_rng))
        raise ArgumentError, "next_rng should be an address"
    end
    unless next_rng or block_given?
        raise ArgumentError, "next_rng is not specified and there is no block given"
    end
    i = d
    mask = e
    j = b
    t = c
    isolate do
                    ld   hl, target unless target == hl
        unless length == a
            if immediate?(length) && (length.to_i & 0xFF).zero?
                    xor  a
            else
                    ld   a, length
            end
        end
                    ld   i|mask, 0
        loop0       push af             # save length
                    ld   a, mask
                    ora  i              # make mask from i
                    ld   mask, a
                    push hl

        repeat_rand push i|mask
        if next_rng
                    call next_rng       # a = random
        else
                    ns(&next_rng_blk)
        end
                    pop  i|mask         # i|mask
                    anda mask
                    ld   j, a           # j = random & mask
                    ld   a, i
                    sub  j              # i - j
                    jr   C, repeat_rand # j > i

                    pop  hl             # restore target
                    push i|mask         # save i|mask

                    ld   t, i           # t = i

                    ld16 de, hl         # de: hl
                    jr   Z, skip_mov    # j == i
                    ld   j, a           # if j ≠ i
                    sub_from j, d, e    # de: hl - (i - j)
                    ld   a, [de]        # target[j]
                    ld   [hl], a        # target[i] = target[j]
        skip_mov    label
        if source.nil?
                    ld   a, t
        else
                    call source
        end
                    ld   [de], a        # target[j] = source[i]
                    pop  i|mask         # restore i|mask
                    pop  af             # length
                    inc  hl             # target++
                    inc  i
                    cp   i
                    jr   NZ, loop0
    end
end