module Z80::Stdlib::Macros

Z80::Stdlib Macros.

Public Instance Methods

clrmem(dest=hl, size=bc, value=0) click to toggle source

Clears memory at dest using LDIR instruction.

T-states: ~ 21/cleared byte.

Modifies: bc, de, hl, optionally a if value is an indirect (pointer) address.

dest

An address of the memory area to be cleared as an integer, a label, a pointer or hl.

size

16bit size of area to be cleared as an integer, a label, a pointer or bc.

value

A fill byte value as an integer, a label, a pointer or one of the registers: a, b, c, d, e.

# File lib/z80/stdlib.rb, line 118
def clrmem(dest=hl, size=bc, value=0)
    raise ArgumentError unless (address?(dest) or dest == hl) and
                               (address?(size) or size == bc) and
                               (address?(value) or [a,b,c,d,e].include?(value))
    isolate do
                ld   hl, dest unless dest == hl
            if pointer?(value)
                ld   a, value
                ld   [hl], a
            else
                ld   [hl], value
            end
            if size == bc
                dec  bc
            elsif pointer?(size)
                ld   bc, size
                dec  bc
            else
                ld   bc, size - 1
            end
            if dest == hl or pointer?(dest)
                ld16 de, hl
                inc  de
            else
                ld  de, dest + 1
            end
                ldir
    end
end
clrmem8(dest=hl, size=b, value=0, rr:hl) click to toggle source

Clears max 256 bytes of memory at dest. Slower (does not use LDIR/LDDR) but involves less registers.

T-states: ~ 26/cleared byte. (~ 45 when rr is ix or iy)

Modifies: a, b, rr.

dest

An address of the memory area to be cleared as an integer, a label, a pointer or same as rr.

size

A size of area to be cleared or one of the 8bit registers.

value

A fill byte value or one of the 8bit registers except b.

Options:

rr

16bit address register: de, hl, ix, iy.

# File lib/z80/stdlib.rb, line 84
def clrmem8(dest=hl, size=b, value=0, rr:hl)
    raise ArgumentError unless (address?(dest) or dest == rr) and
                               (address?(size) or (register?(size) and size.bit8?)) and
                               ((pointer?(size) and value != a) or (!pointer?(size))) and
                               (address?(value) or (register?(value) and value != b and value.bit8?)) and
                               [de, hl, ix, iy].include?(rr)
    isolate do
        if pointer?(size)
                ld  a, size
                ld  b, a
        elsif size != b
                ld  b, size
        end
        if value == 0
                xor a
        elsif value != a
                ld  a, value
        end
                ld  rr, dest unless dest == rr
        loop1   ld  [rr], a
                inc rr
                djnz loop1
    end
end
clrmem_fastest(address=hl, chunks_count=b, chunk_size=2, value=0, tt:hl, disable_intr:true, enable_intr:true, save_sp:true) click to toggle source

Clears a memory area using unrolled PUSH with a tight loop.

NOTE

Interrupts should be disabled during execution of this code.

T-states: ~ 5,5/cleared byte in a chunk + 13 between chunks + 8 after the last chunk.

address

An address of the next byte AFTER THE END of the memory area to be cleared. The address may be an integer, a label, a pointer or one of: sp, hl, ix or iy.

chunks_count

The number of the unrolled push chunks. It should be between 1 and 256. It may be be an integer, a label, a pointer or an 8-bit register.

chunk_size

The size in bytes of the chunk of memory being cleared by unrolled PUSHes. It must be an EVEN integer number. Each 2 of chunk_size adds a 1 byte to the code.

value

A 16-bit (a word) fill value as an integer, a label, a pointer or tt.

The total size being cleared equals to: chunks_count * chunk_size.

Options:

  • tt

    A 16-bit temporary register containing the filler value of the memory area to be cleared. One of: hl, de and optionally bc only if chunks_count is 1.

  • disable_intr

    A boolean flag indicating that the routine should disable interrupts. Provide false only if you have already disabled the interrupts.

  • enable_intr

    A boolean flag indicating that the routine should enable interrupts. Provide false if you need to perform more uninterrupted actions.

  • save_sp

    A boolean flag indicating that the sp register should be saved and restored. Otherwise sp will point to the beginning of the memory area being cleared.

NOTE

Restoring sp register uses self-modifying code.

Modifies: tt, sp, optionally b if chunks_count is not 1 and a if chunks_count is a pointer.

# File lib/z80/stdlib.rb, line 210
def clrmem_fastest(address=hl, chunks_count=b, chunk_size=2, value=0, tt:hl, disable_intr:true, enable_intr:true, save_sp:true)
    raise ArgumentError unless (address?(address) or [sp,hl,ix,iy].include?(address)) and
                               (address?(chunks_count) or (register?(chunks_count) and chunks_count.bit8?)) and
                               (Integer === chunk_size) and
                               (address?(value) or value == tt) and
                               [bc,de,hl].include?(tt)
    raise ArgumentError, "can't enable interrupts without restoring the sp register first" if enable_intr and !save_sp
    raise ArgumentError, "chunk_size must be a positive and even integer number" unless chunk_size.even? and chunk_size > 0
    raise ArgumentError, "chunks_count must be between 1 and 256" if Integer === chunks_count and
                                                                     !(1..256).include?(chunks_count)
    raise ArgumentError, "tt must not be bc when chunks_count is not 1" if tt == bc and chunks_count != 1
    isolate do
                    ld  [restore_sp + 1], sp if save_sp
        unless chunks_count == 1
            if pointer?(chunks_count)
                    ld  a, chunks_count
                    ld  b, a
            else
                    ld  b, chunks_count unless chunks_count == b
            end
        end
                    di if disable_intr
                    ld  sp, address unless address == sp
                    ld  tt, value unless value == tt
        loop1       label
                    (chunk_size/2).times { push tt }
                    djnz loop1 unless chunks_count == 1
        restore_sp  ld  sp, 0 if save_sp
                    ei if enable_intr
    end
end
clrmem_quick(dest=hl, size=1, value=0, rr:hl) click to toggle source

Clears memory at dest in a faster way using unrolled instructions.

T-states: ~ 13/cleared byte.

Modifies: a, rr.

dest

An address of the memory area to be cleared as an integer, a label, a pointer or same as rr.

size

A static size of area to be cleared. It should be a reasonably small positive integer number. There will be: size * 2 bytes added to the code.

value

A fill byte value as an integer, a label, a pointer or one of the 8-bit registers.

Options:

rr

16bit address register: bc, de, hl.

# File lib/z80/stdlib.rb, line 161
def clrmem_quick(dest=hl, size=1, value=0, rr:hl)
    raise ArgumentError unless (address?(dest) or dest == rr) and
                               (Integer === size) and size > 0 and
                               (address?(value) or (register?(value) and value.bit8?)) and
                               [bc, de, hl].include?(rr)
    isolate do
            if value == 0
                xor a
            elsif value != a
                ld  a, value
            end
                ld  rr, dest unless dest == rr
            (size - 1).times do
                ld  [rr], a
                inc rr
            end
                ld  [rr], a
    end
end
memcpy(dest=de, source=hl, size=bc, reverse: nil) click to toggle source

Copies size bytes from memory area source to memory area dest.

dest

A destination address as an integer, a label, a pointer or de.

source

A destination address as an integer, a label, a pointer or hl.

size

A size of area to be copied as an integer, a label, a pointer or bc.

Options:

reverse

A flag if true the routine uses LDDR, otherwise uses LDIR.

NOTE

In case reverse is true the dest and source should address the last byte of the destinetion and source memory area to be copied.

If reverse is nil and dest and source and size are integers or labels (but not pointers) detects if source and destination memory overlaps and applies LDIR or LDDR during code generation.

Modifies: bc, de, hl.

# File lib/z80/stdlib.rb, line 258
def memcpy(dest=de, source=hl, size=bc, reverse: nil)
    raise ArgumentError unless (address?(dest) or dest == de) and
                               (address?(source) or source == hl) and
                               (address?(size) or size == bc)
    isolate do
        if reverse.nil? and address?(dest) and address?(source) and address?(size) and
                             !pointer?(dest) and !pointer?(source) and !pointer?(size)
                ld  bc, size
            select(dest, source, size) {|dst,src,sz| dst > src && src + sz > dst }.then do |_|
                ld  de, dest + size - 1
                ld  hl, source + size - 1
                lddr
            end.else do
                ld  de, dest
                ld  hl, source
                ldir
            end
        else
                ld  bc, size unless size == bc
                ld  de, dest unless dest == de
                ld  hl, source unless source == hl
            if reverse
                lddr
            else
                ldir
            end
        end
    end
end
memcpy_quick(dest=de, source=hl, size=1, reverse: nil) click to toggle source

Copies size bytes from memory area source to memory area dest using unrolled LDI/LDD.

dest

A destination address as an integer, a label, a pointer or de.

source

A destination address as an integer, a label, a pointer or hl.

size

A static size of area to be copied. It should be a reasonably small positive integer number.

Options:

reverse

A flag if true the routine uses LDD, otherwise uses LDI.

NOTE

In case reverse is true the dest and source should address the last byte of the destinetion and source memory area to be copied.

If reverse is nil and dest and source are integers or labels (but not pointers) detects if source and destination memory overlaps and applies LDI or LDD during code generation.

Modifies: bc, de, hl.

# File lib/z80/stdlib.rb, line 304
def memcpy_quick(dest=de, source=hl, size=1, reverse: nil)
    raise ArgumentError unless (address?(dest) or dest == de) and
                               (address?(source) or source == hl) and
                               (Integer === size) and size > 0
    isolate do
        if reverse.nil? and address?(dest) and address?(source) and
                              !pointer?(dest) and !pointer?(source)
            select(dest, source, size) {|dst,src,sz| dst > src && src + sz > dst }.then do |_|
                ld  de, dest + size - 1
                ld  hl, source + size - 1
                size.times { ldd }
            end.else do
                ld  de, dest
                ld  hl, source
                size.times { ldi }
            end
        else
                ld  de, dest if dest != de
                ld  hl, source if source != hl
            if reverse
                size.times { ldd }
            else
                size.times { ldi }
            end
        end
    end
end