module Z80::Stdlib::Macros
Z80::Stdlib
Macros
.¶ ↑
Public Instance Methods
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
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
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
oriy
. 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 optionallybc
only ifchunks_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. Otherwisesp
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
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
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
istrue
thedest
andsource
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
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
istrue
thedest
andsource
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