module ZXLib::Gfx::Bobs::Macros
ZXLib::Gfx::Bobs
macros.¶ ↑
Bobs::Macros
require:
macro_import MathInt macro_import Gfx
Public Instance Methods
Creates a routine that combines bitmap pixel lines as a rectangle of pixels with the ink/paper screen data.
No pixel shifting is performed on a bitmap.
bitmap
-
A direct or indirect address of a bitmap data containing the pixel lines as a label, an integer or
hl
.
lines
-
A number of pixel lines to be copied as an 8-bit register or a label or a pointer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A number of 8 pixel columns to be copied as an 8-bit register or a label or a pointer. If an accumulator (
a
) is given tocols
then the value is expected ina'
. The number 0 is treated as 65536 and will lead to UNDEFINED BEHAVIOUR, clobbering the whole memory in the process.
- NOTE
-
Unless
cols
is one of:ixh
,ixl
,iyh
oriyl
the routine uses self modifying code.
Options:
target
-
A direct or indirect address of the screen memory to be combined with the lines of the
bitmap as a label, an integer or +de+. The starting address of the entire screen memory must be a multiple of 0x2000.
scraddr
-
An optional entire screen memory address which must be a multiple of 0x2000 as
an integer or an immediate label. If provided, the routine breaks execution when the bottom of the screen has been reached. +CF+ = 0 (NC) is signalled if the routine terminates prematurely due to reaching the bottom of the screen. Otherwise +CF+ = 1 if the whole bitmap has been copied.
subroutine
-
A boolean indicating whether to create a subroutine.
The mode
option can be changed at run time in the resulting routine, by storing a new operation under the fx_a
sub-label. The following constants can be used for this purpose: COMBINE_FX_NOP
, COMBINE_FX_SET
, COMBINE_FX_AND
, COMBINE_FX_XOR
, COMBINE_FX_OR
.
ld a, Gfx::Bobs::COMBINE_FX_AND ld [combine_bitmap.fx_a], a # ... combine_bitmap bobs_combine_pixels(mode: :xor, subroutine: true)
Using :set
or COMBINE_FX_SET
mode has been included for completeness in this routine, but is sub-optimal. For copying bitmaps to the screen, using bobs_copy_pixels
is preferable.
Modifies: af
, af'
, bc
, bc'
, de
, hl
. Swaps registers unless out of screen.
# File lib/zxlib/gfx/bobs.rb, line 233 def bobs_combine_pixels(bitmap=hl, lines=a, cols=c, target:de, mode: :or, scraddr:nil, subroutine:false) raise ArgumentError, "bobs_combine_pixels: invalid scraddr argument" unless scraddr.nil? or direct_address?(scraddr) or (Integer === scraddr and scraddr == (scraddr & 0xE000)) unless address?(target) or target == de raise ArgumentError, "bobs_combine_pixels: target should be an address or de" end unless address?(bitmap) or bitmap == hl raise ArgumentError, "bobs_combine_pixels: bitmap should be an address or hl" end unless (register?(lines) and lines.bit8?) or address?(lines) raise ArgumentError, "bobs_combine_pixels: lines should be an integer, a label or a pointer or a register" end unless (register?(cols) and cols.bit8?) or address?(cols) raise ArgumentError, "bobs_combine_pixels: cols should be an integer, a label or a pointer or a register" end fx = case mode when :or, COMBINE_FX_OR then ->(tgt) { ora tgt } when :xor, COMBINE_FX_XOR then ->(tgt) { xor tgt } when :and, COMBINE_FX_AND then ->(tgt) { anda tgt } when :nop, COMBINE_FX_NOP then ->(tgt) { nop } when :set, COMBINE_FX_SET then ->(tgt) { ld a, tgt } else raise ArgumentError, "bobs_combine_pixels: mode should be one of: :or, :xor, :and, :none, :set" end isolate do |eoc| ld a, lines unless lines == a ex af, af # a': lines unless [ixh,ixl,iyh,iyl].include?(cols) ld a, cols unless cols == a ld [cols_p], a end ld hl, bitmap unless bitmap == hl ld de, target unless target == de ld c, e # lo if address?(target) and !pointer?(target) exx ld b, 8 - (target>>8) % 8 else ld a, d # calculate counter based on screen address modulo 8 anda 0b11111000 # (h & 0b11111000) sub d # (h & 0b11111000) - h % 8 add 8 # 8 - h % 8 exx ld b, a # b: counter: 8 - h % 8 end ex af, af # a: lines ld c, a # c: lines dec a # a: lines - 1 (remaining lines) sub b # a: lines - 1 - counter jr NC, start ld b, c # b: counter = c: lines start ex af, af # a': remaining lines - 1, CF': 1 == last batch rloop exx loop0 ld e, c # lo if [ixh,ixl,iyh,iyl].include?(cols) ld b, cols else cols_a ld b, 0 # self-modified code cols_p cols_a + 1 end cloop ld a, [de] fx_a fx.call [hl] # or, xor, and inc hl ld [de], a inc e djnz cloop inc d exx djnz rloop ex af, af # a: remaining lines if subroutine ret C else jr C, eoc end ld b, 8 sub b jr NC, loop8 # b: 8, CF: 0 add b ld b, a inc b # b: remaining lines, CF: 1 loop8 exx ex af, af # a': remaining lines, CF': 1 == last batch ld a, c # lo add 0x20 # a: lo + 0x20 ld c, a # c: lo jr C, loop0 unless scraddr ld a, d jr C, check_ooscr if scraddr sub 0x08 ld d, a # d: adjusted jp loop0 if scraddr check_ooscr cp (scraddr >> 8)|0x18 jr C, loop0 ret if subroutine end end end
Creates a routine that copies rows of attribute data to (or from) the screen forming a rectangle on the screen.
attrs
-
A direct or indirect address of attribute rows as a label, an integer or a 16-bit register
hl
orde
depending on thecopy_back
option.
rows
-
A number of attribute rows to be copied as an 8-bit register or a label or a pointer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A number of attribute columns to be copied as an 8-bit register or a label or a pointer. If an accumulator (
a
) is given tocols
then the value is expected ina'
. The number 0 is treated as 65536 and will lead to UNDEFINED BEHAVIOUR, clobbering the whole memory in the process.
- NOTE
-
Unless
cols
is one of:ixh
,ixl
,iyh
oriyl
the routine uses self modifying code.
Options:
screen
-
A direct or indirect address of the screen attributes memory where the rows of the
attributes will be copied to (or from), as a label, an integer or a 16-bit register +de+ or +hl+, depending on the +copy_back+ option. The starting address of the entire screen memory must be a multiple of 0x2000. If this option is +nil+, an appropriate 16-bit register pair is chosen based on the +copy_back+ option.
copy_back
-
A boolean indicating whether to copy attributes from the screen (
true
) or to
the screen (+false+).
scraddr
-
An optional entire screen memory address which must be a multiple of 0x2000 as
an integer or an immediate label. If provided, the routine breaks execution when the bottom of the screen has been reached. +CF+ = 0 (NC) is signalled if the routine terminates prematurely due to reaching the bottom of the screen. Otherwise +CF+ = 1 if all the attributes has been copied.
subroutine
-
A boolean indicating whether to create a subroutine.
Modifies: af
, af'
, bc
, bc'
, de
, hl
. Swaps registers unless out of screen.
# File lib/zxlib/gfx/bobs.rb, line 365 def bobs_copy_attrs(attrs=hl, rows=a, cols=c, screen:nil, copy_back: false, scraddr:nil, subroutine:false, **opts) raise ArgumentError, "bobs_copy_attrs: invalid scraddr argument" unless scraddr.nil? or direct_address?(scraddr) or (Integer === scraddr and scraddr == (scraddr & 0xE000)) unless copy_back target = opts.delete(:target) # legacy option screen = target unless target.nil? end unless screen.nil? or register?(screen) or address?(screen) raise ArgumentError, "bobs_copy_attrs: screen should be an address, a 16-bit register pair or nil" end unless register?(attrs) or address?(attrs) raise ArgumentError, "bobs_copy_attrs: attrs should be an address or a 16-bit register pair" end unless (register?(rows) and rows.bit8?) or address?(rows) raise ArgumentError, "bobs_copy_attrs: rows should be an integer, a label or a pointer or a register" end unless (register?(cols) and cols.bit8?) or address?(cols) raise ArgumentError, "bobs_copy_attrs: cols should be an integer, a label or a pointer or a register" end raise ArgumentError, "bobs_copy_attrs: invalid option used" unless opts.empty? attrs_tt = if copy_back then de else hl end if register?(attrs) and attrs != attrs_tt raise ArgumentError, "bobs_copy_attrs: invalid attrs register used" end scrhi, scrlo = if copy_back hl # source else de # target end.split if register?(screen) && screen != (scrhi|scrlo) raise ArgumentError, "bobs_copy_attrs: invalid screen register used" end screen = scrhi|scrlo if screen.nil? isolate do |eoc| ld a, rows unless rows == a ex af, af ld a, cols unless cols == a unless [ixh,ixl,iyh,iyl].include?(cols) ld [cols_p], a end exx cpl add 33 ld c, a # 32 - cols ex af, af # a: rows ld b, a exx ld attrs_tt, attrs unless attrs == attrs_tt ld scrhi|scrlo, screen unless screen == (scrhi|scrlo) ld b, 0 jr start0 rowloop ld a, c # 32 - cols exx adda_to scrhi, scrlo if scraddr cp (scraddr>>8)|0x1B if subroutine ret NC else jr NC, eoc end end start0 label if [ixh,ixl,iyh,iyl].include?(cols) ld c, cols else cols_a ld c, 0 # self-modified code cols_p cols_a + 1 end ldir exx djnz rowloop ret if subroutine end end
Creates a routine that copies rows of attribute data to (or from) the screen forming a rectangle on the screen using unrolled POP (or PUSH) instructions.
- NOTE
-
Interrupts must be disabled prior to calling this routine or the
disable_intr
option must be set totrue
.
attrs
-
An address of attributes to be copied from as a label, pointer, an integer or one of the registers:
ix
,iy
orsp
.
rows
-
A number of attribute rows to be copied as an 8-bit register or a label, pointer or an integer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A constant number of attribute columns to be copied as an integer.
Options:
screen
-
A direct or indirect address of the screen attributes memory where the attribute rows
will be copied to (or from), as a label, an integer or a 16-bit register +hl+. The starting address of the entire screen memory must be a multiple of 0x2000.
disable_intr
-
A boolean flag indicating that the routine should disable interrupts. Provide
false
only if interrupts are disabled prior to entering this routine.
enable_intr
-
A boolean flag indicating that the routine should enable interrupts. Provide
false
if more uninterrupted actions need to performed after this routine completes execution.
save_sp
-
A boolean flag indicating that the
sp
register should be saved and restored. Otherwisesp
will point behind the end of the last source row.
check_oos
-
If
true
reduces number of rows if the bottom of the screen would have been exceeded.
subroutine
-
A boolean indicating whether to create a subroutine.
If attrs
is a direct address, it can be later changed at run time by storing a new bitmap address at the attrs_p
sub-label.
- NOTE
-
Restoring
sp
register uses self-modifying code.
Modifies: af
, bc
, de
, hl
, optionally: sp
.
# File lib/zxlib/gfx/bobs.rb, line 777 def bobs_copy_attrs_fast(attrs, rows=a, cols=32, screen:hl, copy_back:false, disable_intr:true, enable_intr:true, save_sp:true, check_oos:false, subroutine:false, **opts) unless [false, true, :padded].include?(copy_back) raise ArgumentError, "bobs_copy_attrs_fast: copy_back should be a boolean or :padded" end unless copy_back target = opts.delete(:target) # legacy option screen = target unless target.nil? end raise ArgumentError, "bobs_copy_attrs_fast: screen should be an address or a pointer or hl" unless screen == hl or address?(screen) unless [sp,ix,iy].include?(attrs) or address?(attrs) raise ArgumentError, "bobs_copy_attrs_fast: attrs should be an address or a pointer or ix/iy/sp" end unless (register?(rows) and rows.bit8?) or address?(rows) raise ArgumentError, "bobs_copy_attrs_fast: rows should be an integer or a label or a pointer or a register" end cols = cols.to_i raise ArgumentError, "bobs_copy_attrs_fast: cols must be less than or equal to 32" if cols > 32 raise ArgumentError, "bobs_copy_attrs_fast: cols must be greater than or equal to 1" if cols < 1 raise ArgumentError, "bobs_copy_attrs_fast: disable_intr should be a boolean" unless [true, false].include?(disable_intr) raise ArgumentError, "bobs_copy_attrs_fast: enable_intr should be a boolean" unless [true, false].include?(enable_intr) raise ArgumentError, "bobs_copy_attrs_fast: save_sp should be a boolean" unless [true, false].include?(save_sp) raise ArgumentError, "bobs_copy_attrs_fast: save_sp makes no sense if bitmap = sp" if bitmap == sp and save_sp raise ArgumentError, "bobs_copy_attrs_fast: check_oos should be a boolean" unless [true, false].include?(check_oos) raise ArgumentError, "bobs_copy_attrs_fast: subroutine requires save_sp" if subroutine and !save_sp raise ArgumentError, "bobs_copy_attrs_fast: interrupts should be disabled if bitmap = sp" if bitmap == sp and disable_intr isolate do |eoc| if check_oos if address?(rows) && pointer?(rows) ld a, rows ld c, a else ld c, rows unless rows == c end ld a, l # llllllll xor h # xxxxxxxx anda 0b11100000 # xxx00000 xor h # lllhhhhh 3.times { rlca } # hhhhhlll if copy_back sub 0xC0 cp c jr NC, skip_rows else add 0x28 add c jr NC, skip_rows cpl adc c end # copy_back ld c, a skip_rows ld a, c else ld a, rows unless rows == a end # check_oos next_attr_line = if cols == 32 if copy_back proc { dec hl } else proc { inc hl } end # copy_back else # cols != 32 proc { add hl, bc } end # cols == 32 if copy_back ld bc, -(33 - cols) else ld bc, 33 - cols end unless cols == 32 ld hl, screen unless screen == hl ld [restore_sp_p], sp if save_sp di if disable_intr if direct_address?(attrs) attrs_a ld sp, attrs attrs_p as attrs_a + 1 else ld sp, attrs unless attrs == sp end jp start0 if cols.even? rowloop label next_attr_line.call start0 label ((cols-2)/2).times do if copy_back ld d, [hl] dec l ld e, [hl] dec l push de else pop de ld [hl], e inc l ld [hl], d inc l end # copy_back end if copy_back ld d, [hl] dec l ld e, [hl] push de else pop de ld [hl], e inc l ld [hl], d end # copy_back dec a jr NZ, rowloop else # cols.odd? evenloop label next_attr_line.call start0 label ((cols-1)/2).times do if copy_back ld d, [hl] dec l ld e, [hl] dec l push de else pop de ld [hl], e inc l ld [hl], d inc l end # copy_back end # times if copy_back ld d, [hl] else pop de ld [hl], e end # copy_back dec a if copy_back == :padded jr NZ, oddloop push de # last push with clobber jp quit else jr Z, quit end # copy_back oddloop label next_attr_line.call if copy_back ld e, [hl] push de else ld [hl], d end # copy_back ((cols-1)/2).times do if copy_back dec l ld d, [hl] dec l ld e, [hl] push de else pop de inc l ld [hl], e inc l ld [hl], d end # copy_back end # times dec a jr NZ, evenloop end # cols.even? quit label if save_sp restore_sp ld sp, 0 restore_sp_p as restore_sp + 1 end ei if enable_intr ret if subroutine end end
Creates a routine that copies bitmap pixel lines to (or from) the ink/paper screen as a rectangle of pixels.
No pixel shifting is performed on a bitmap.
bitmap
-
A direct or indirect address of a bitmap data containing the pixel lines as a label, an integer or a 16-bit register
hl
orde
depending on thecopy_back
option.
lines
-
A number of pixel lines to be copied as an 8-bit register or a label or a pointer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A number of 8 pixel columns to be copied as an 8-bit register or a label or a pointer. If an accumulator (
a
) is given tocols
then the value is expected ina'
. The number 0 is treated as 65536 and will lead to UNDEFINED BEHAVIOUR, clobbering the whole memory in the process.
- NOTE
-
Unless
cols
is one of:ixh
,ixl
,iyh
oriyl
the routine uses self modifying code.
Options:
screen
-
A direct or indirect address of the screen memory where the lines of the bitmap will
be copied to (or from), as a label, an integer or a 16-bit register +de+ or +hl+ depending on the +copy_back+ option. The starting address of the entire screen memory must be a multiple of 0x2000. If this option is +nil+, an appropriate 16-bit register pair is chosen based on the +copy_back+ option.
copy_back
-
A boolean indicating whether to copy pixel data from the screen (
true
) or to
the screen (+false+).
scraddr
-
An optional entire screen memory address which must be a multiple of 0x2000 as
an integer or an immediate label. If provided the routine breaks execution when the bottom of the screen has been reached. +CF+ = 0 (NC) is signalled if the routine terminates prematurely due to reaching the bottom of the screen. Otherwise +CF+ = 1 if the whole bitmap has been copied.
subroutine
-
A boolean indicating whether to create a subroutine.
The 16-bit registers that can be used for a bitmap
argument and a screen
option:
copy_back bitmap screen false hl de true de hl
Modifies: af
, af'
, bc
, bc'
, de
, hl
. Swaps registers unless out of screen.
# File lib/zxlib/gfx/bobs.rb, line 79 def bobs_copy_pixels(bitmap=hl, lines=a, cols=c, screen:nil, copy_back:false, scraddr:nil, subroutine:false, **opts) raise ArgumentError, "bobs_copy_pixels: invalid scraddr argument" unless scraddr.nil? or direct_address?(scraddr) or (Integer === scraddr and scraddr == (scraddr & 0xE000)) unless copy_back target = opts.delete(:target) # legacy option screen = target unless target.nil? end raise ArgumentError, "bobs_copy_pixels: invalid option used" unless opts.empty? unless screen.nil? or register?(screen) or address?(screen) raise ArgumentError, "bobs_copy_pixels: screen should be an address, a 16-bit register pair or nil" end unless register?(bitmap) or address?(bitmap) raise ArgumentError, "bobs_copy_pixels: bitmap should be an address or a 16-bit register pair" end unless (register?(lines) and lines.bit8?) or address?(lines) raise ArgumentError, "bobs_copy_pixels: lines should be an integer, a label or a pointer or a register" end unless (register?(cols) and cols.bit8?) or address?(cols) raise ArgumentError, "bobs_copy_pixels: cols should be an integer, a label or a pointer or a register" end bitmap_tt = if copy_back then de else hl end if register?(bitmap) and bitmap != bitmap_tt raise ArgumentError, "bobs_copy_pixels: invalid bitmap register used" end scrhi, scrlo = if copy_back hl # source else de # target end.split if register?(screen) && screen != (scrhi|scrlo) raise ArgumentError, "bobs_copy_pixels: invalid screen register used" end screen = scrhi|scrlo if screen.nil? isolate do |eoc| ld a, lines unless lines == a ex af, af # a': total lines unless [ixh,ixl,iyh,iyl].include?(cols) ld a, cols unless cols == a ld [cols_p], a end ld b, 0 # ldir counter msb ld scrhi|scrlo, screen unless screen == (scrhi|scrlo) ld bitmap_tt, bitmap unless bitmap == bitmap_tt if address?(screen) and !pointer?(screen) ld a, scrlo # screen.lo exx ld b, 8 - (screen>>8) % 8 else ld a, scrhi # calculate counter based on screen address modulo 8 anda 0b11111000 # (h & 0b11111000) sub scrhi # (h & 0b11111000) - h % 8 add 8 # 8 - h % 8 exx ld b, a # b: counter: 8 - h % 8 exx ld a, scrlo # screen.lo exx end ex af, af # a: total lines, a': screen.lo ld c, a # c: total lines dec a # a: total lines - 1 (remaining lines) sub b # a: remaining lines - counter jr NC, start # first batch last? ld b, c # b: counter = lines (last batch) start ex af, af # a': remaining rows - 1; CF': 1 == last batch, a: screen.lo rloop exx loop0 ld scrlo, a # restore screen.lo loop1 label if [ixh,ixl,iyh,iyl].include?(cols) ld c, cols else cols_a ld c, 0 # self-modified code cols_p cols_a + 1 end ldir # copy line dec scrhi|scrlo # backup screen address, so it points to the last column (1/3rd screen overflow correction) inc scrhi # next screen line exx djnz rloop ex af, af # a: remaining lines, a': screen.lo if subroutine # CF=1 if this was the last batch ret C else jr C, eoc end ld b, 8 # b: counter = 8 lines (next row) sub b # remaining lines - counter jr NC, loop8 # next batch last? add b # restore remaining lines ld b, a inc b # b: counter = remaining lines + 1 (last batch) # next row address loop8 exx ex af, af # a: screen.lo, a': remaining lines + CF add 0x20 # a: screen.lo + 0x20 jr C, loop0 unless scraddr ld scrlo, a # screen.lo ld a, scrhi # screen.hi jr C, check_ooscr if scraddr sub 0x08 ld scrhi, a # adjusted screen.hi ld a, scrlo # screen.lo jp loop1 if scraddr check_ooscr cp (scraddr >> 8)|0x18 ld a, scrlo # screen.lo jr C, loop1 ret if subroutine end end end
Creates a routine that copies bitmap pixel lines to (or from) the ink/paper screen as a rectangle of pixels using unrolled POP (or PUSH) instructions.
No pixel shifting is performed on a texture.
- NOTE
-
Interrupts must be disabled prior to calling this routine or the
disable_intr
option must be set totrue
.
Copying back bitmap data from screen memory to bitmap memory is performed backwards - from the last byte of memory to the first one.
Note that when copying back bitmap data from the screen while the bitmap byte size is odd (when both cols
and lines
are odd), and because a single push
instruction modifies 2 bytes the first (odd) bitmap byte will remain in the d
register and has to be transfered to the beginning of the bitmap outside of this routine, unless copy_back
option is :padded
. In this instance the last push will be performed modifying an additional pad byte, preceding in memory the very first address of destination bitmap data. In this instance the bitmap data must be padded by a single byte in front of it.
bitmap
-
A direct or indirect address of a bitmap data containing the pixel lines as a label,
an integer or a 16-bit register pair: +ix+, +iy+ or +sp+.
lines
-
A number of pixel lines to be copied as an 8-bit register, a label, pointer or an integer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A constant number of 8 pixel columns to be copied as an integer.
Options:
screen
-
A direct or indirect address of the screen memory where the lines of the bitmap will
be copied to (or from), as a label, an integer or +hl+. The starting address of the entire screen memory must be a multiple of 0x2000.
copy_back
-
A boolean indicating whether to copy pixel data from the screen (
true
) or to the
screen (+false+). If +copy_back+ is +true+, the +bitmap+ and +screen+ must point to the __last__ byte of both the bitmap data and the screen area (bottom - rightmost corner).
disable_intr
-
A boolean flag indicating that the routine should disable interrupts. Provide
false
only if interrupts are disabled prior to entering this routine.
enable_intr
-
A boolean flag indicating that the routine should enable interrupts. Provide
false
if more uninterrupted actions need to performed after this routine completes execution.
save_sp
-
A boolean flag indicating that the
sp
register should be saved and restored. Otherwise
+sp+ will point behind the end of the last source line (or before the first if +copy_back+ is +true+).
scraddr
-
An optional entire screen memory address which must be a multiple of 0x2000 as
an integer or an immediate label. If provided the routine breaks execution when the bottom of the screen has been reached. +CF+ = 0 (NC) is signalled if the routine terminates prematurely due to reaching the bottom of the screen. Otherwise +CF+ = 1 if the whole bitmap has been copied.
subroutine
-
A boolean indicating whether to create a subroutine.
If bitmap
is a direct address, it can be later changed at run time by storing a new bitmap address at the bitmap_p
sub-label.
- NOTE
-
Restoring
sp
register uses self-modifying code.
Modifies: af
, af'
, bc
, de
, hl
, optionally: sp
.
# File lib/zxlib/gfx/bobs.rb, line 494 def bobs_copy_pixels_fast(bitmap, lines=c, cols=32, screen:hl, copy_back:false, disable_intr:true, enable_intr:true, save_sp:true, scraddr:nil, subroutine:false, **opts) raise ArgumentError, "bobs_copy_pixels_fast: invalid scraddr argument" unless scraddr.nil? or direct_address?(scraddr) or (Integer === scraddr and scraddr == (scraddr & 0xE000)) unless [false, true, :padded].include?(copy_back) raise ArgumentError, "bobs_copy_pixels_fast: copy_back should be a boolean or :padded" end unless copy_back target = opts.delete(:target) # legacy option screen = target unless target.nil? end raise ArgumentError, "bobs_copy_pixels_fast: screen should be an address or a pointer or hl" unless screen == hl or address?(screen) unless [sp,ix,iy].include?(bitmap) or address?(bitmap) raise ArgumentError, "bobs_copy_pixels_fast: bitmap should be an address or a pointer or ix/iy/sp" end unless (register?(lines) and lines.bit8?) or address?(lines) raise ArgumentError, "bobs_copy_pixels_fast: lines should be an integer or a label or a pointer or a register" end cols = cols.to_i raise ArgumentError, "bobs_copy_pixels_fast: cols must be less than or equal to 32" if cols > 32 raise ArgumentError, "bobs_copy_pixels_fast: cols must be greater than or equal to 1" if cols < 1 raise ArgumentError, "bobs_copy_pixels_fast: disable_intr should be a boolean" unless [true, false].include?(disable_intr) raise ArgumentError, "bobs_copy_pixels_fast: enable_intr should be a boolean" unless [true, false].include?(enable_intr) raise ArgumentError, "bobs_copy_pixels_fast: save_sp should be a boolean" unless [true, false].include?(save_sp) raise ArgumentError, "bobs_copy_pixels_fast: save_sp makes no sense if bitmap = sp" if bitmap == sp and save_sp raise ArgumentError, "bobs_copy_pixels_fast: subroutine requires save_sp" if subroutine and !save_sp raise ArgumentError, "bobs_copy_pixels_fast: interrupts should be disabled if bitmap = sp" if bitmap == sp and disable_intr fits_single_row = if Integer === screen && Integer === lines if copy_back lines <= (1 + (screen>>8) % 8) else lines <= (8 - (screen>>8) % 8) end else false end next_row = ->(loop0, quit=nil) do isolate do |eoc| quit = eoc if quit.nil? ex af, af # a': screen.lo; a: remaining lines - 1; CF: 1 == last batch jr C, quit ld b, c # 8 sub b jr NC, not_last add b ld b, a inc b not_last ex af, af # a': remaining lines - 1; CF': 1 == last batch, a: screen.lo if copy_back sub 0x20 # a: screen.lo - 0x20 else add 0x20 # a: screen.lo + 0x20 end ld l, a # l: screen.lo jr C, loop0 unless scraddr ld a, h jr C, check_oos if scraddr if copy_back add c # +8 else sub c # -8 end ld h, a # h: adjusted ld a, l jp loop0 if scraddr || quit == eoc if scraddr scrhiaddr = (scraddr >> 8) scrhiaddr = scrhiaddr|0x18 unless copy_back check_oos cp scrhiaddr ld a, l if copy_back jr NC, loop0 ccf # flip C to signal oos else jr C, loop0 end jr quit unless quit == eoc end end end isolate do ld c, lines if register?(lines) && lines != c if address?(screen) and !pointer?(screen) ld hl, screen if fits_single_row ld b, lines elsif copy_back ld b, 1 + (screen>>8) % 8 # first row count: [1, 8] else ld b, 8 - (screen>>8) % 8 # first row count: [1, 8] end else ld hl, screen unless screen == hl ld a, h # calculate counter based on screen address modulo 8 if copy_back anda 0b00000111 # (h & 0b00000111) h % 8 inc a # (1 + h % 8) else anda 0b11111000 # (h & 0b11111000) sub h # (h & 0b11111000) - h % 8 add 8 # (8 - h % 8) end ld b, a # b: counter: (8 - h % 8) or (1 + h % 8) end unless fits_single_row if register?(lines) ld a, c # a: lines dec a # a: lines - 1 (remaining lines) elsif pointer?(lines) ld a, lines ld c, a dec a # a: lines - 1 (remaining lines) else ld a, lines - 1 end sub b # a: lines - 1 - counter unless Integer === lines && lines > 8 ns do |eoc| jr NC, eoc if register?(lines) || pointer?(lines) ld b, c # b: counter = dy else ld b, lines end end end ex af, af # a': remaining lines - 1; CF': 1 == last batch ld c, 8 # constant = 8 end ld a, l # a: screen.lo ld [restore_sp_p], sp if save_sp di if disable_intr if direct_address?(bitmap) bitmap_a ld sp, bitmap bitmap_p as bitmap_a + 1 else ld sp, bitmap unless bitmap == sp end if cols.even? rowloop ld l, a loop0 label ((cols-2)/2).times do if copy_back ld d, [hl] dec l ld e, [hl] dec l push de else pop de ld [hl], e inc l ld [hl], d inc l end # copy_back end # times if copy_back ld d, [hl] dec l ld e, [hl] dec h push de else pop de ld [hl], e inc l ld [hl], d inc h end # copy_back djnz rowloop next_row.call loop0 unless fits_single_row else # cols.odd? evenline ld l, a evenloop label ((cols-1)/2).times do if copy_back ld d, [hl] dec l ld e, [hl] dec l push de else pop de ld [hl], e inc l ld [hl], d inc l end # copy_back end # times if copy_back ld d, [hl] dec h else pop de ld [hl], e inc h end # copy_back djnz oddline if copy_back == :padded next_row.call oddloop unless fits_single_row push de # last push with clobber jp quit elsif fits_single_row jp quit else next_row.call oddloop, quit # might fall thru here instead of jp oddloop end # copy_back oddline ld l, a oddloop label if copy_back ld e, [hl] push de else ld [hl], d end # copy_back ((cols-1)/2).times do if copy_back dec l ld d, [hl] dec l ld e, [hl] push de else pop de inc l ld [hl], e inc l ld [hl], d end # copy_back end # times if copy_back dec h else inc h end # copy_back djnz evenline next_row.call evenloop quit label end # cols.even?|odd? if save_sp restore_sp ld sp, 0 restore_sp_p as restore_sp + 1 end ei if enable_intr ret if subroutine end end
Creates a routine that draws bitmap pixel lines to the ink/paper screen as a rectangle object using unrolled POP instructions.
- NOTE
-
Interrupts must be disabled prior to calling this routine or the
disable_intr
option must be set totrue
.
bitmap
-
A direct or indirect address of a bitmap data containing the pixel lines as a label, an integer or a 16-bit register pair:
hl'
,ix
,iy
orsp
. Ifhl
is specified the actual value will be read from alternative registerhl'
.
lines
-
A number of pixel lines to be drawn as an 8-bit register or a label, a pointer or an integer. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A constant number of 8 pixel columns to be drawn as an integer from 1 to 32.
Options:
target
-
An address of a screen memory area to be copied to as a label, pointer, an integer or
hl
. The starting address of the whole screen area must be a multiple of 0x2000.
bshift
-
One of 8-bit registers:
b
,c
,d
,e
. The value of the register specifies how many pixels to the right the bitmap must be shifted before drawing the bitmap on the screen. The actual value must be in the range from 0 up to 7. Values outside of the allowed range will resolve in UNDEFINED BEHAVIOUR.
mode
-
Drawing mode, one of:
:copy
,:set
,:or
,:xor
,:and
,:nand
.
skip_cols
-
If set, the number of bitmap 8-bit columns will be skipped after each line is drawn. Set as an address, an integer or an 8-bit register from alternative set, e.g.
e
specifiese'
.
lclip
-
Skips drawing the leftmost screen column for non zero
bshift
values if enabled.
rclip
-
Skips drawing the rightmost screen column for non zero
bshift
values if enabled.
no0shift
-
Skips creating the drawing routine for
bshift
zero if enabled. Calling such a routine withbshift
register holding zero is safe but nothing will be drawn and CF flag will be reset upon return.
tx
-
A temporary jump address register:
ix
oriy
.
disable_intr
-
A boolean flag indicating that the routine should disable interrupts. Provide
false
only if interrupts are disabled prior to entering this routine.
enable_intr
-
A boolean flag indicating that the routine should enable interrupts. Provide
false
if more uninterrupted actions need to performed after this routine completes execution.
save_sp
-
A boolean flag indicating that the
sp
register should be saved and restored. Otherwisesp
will point behind the end of the last source line.
scraddr
-
An optional entire screen memory address which must be a multiple of 0x2000 as
an integer or an immediate label. If provided the routine breaks execution when the bottom of the screen has been reached. +CF+ = 0 (NC) is signalled if the routine terminates prematurely due to reaching the bottom of the screen. Otherwise +CF+ = 1 if the whole bitmap has been drawn.
jump_table
-
A label, a pointer address or one of
de
/bc
/ix
/iy
register pairs referencing
an external jump table created with Macros#bobs_draw_pixels_fast_jump_table. If not provided an internal jump table will be created instead. In this instance a +jump_table+ can be later changed at run time by storing a new jump table address at the +jump_table_p+ sub-label.
subroutine
-
A boolean indicating whether to create a subroutine.
If bitmap
is a direct address, it can be later changed at run time by storing a new bitmap address at the bitmap_p
sub-label.
- NOTE
-
Restoring
sp
register uses self-modifying code.
Drawing modes:
Bitmap Screen (cols = 2, bshift = 4) 76543210 76543210 76543210 76543210 76543210 ░██░███░ ░███░█░█ ░░██░░██ (original screen data) █░██░██░ ░███░░░█ ->> ░░░░█░██ ░██░░███ ░░░█░░░░ :copy █░██░██░ ░███░░░█ ->> ░██░█░██ ░██░░███ ░░░█░░██ :set █░██░██░ ░███░░░█ ->> ░██░████ ░███░███ ░░██░░██ :or █░██░██░ ░███░░░█ ->> ░██░░█░█ ░░░█░░█░ ░░█░░░██ :xor █░██░██░ ░███░░░█ ->> ░░░░█░█░ ░██░░█░░ ░░░█░░░░ :and █░██░██░ ░███░░░█ ->> ░██░░█░░ ░░░█░░░░ ░░█░░░██ :nand
Modifies: af
, af'
, bc
, bc'
, de
, d'
, hl
, (hl'
, e'
if skip_cols is given). Swaps registers unless out of screen.
# File lib/zxlib/gfx/bobs.rb, line 1389 def bobs_draw_pixels_fast(bitmap, lines=a, cols=2, target:hl, bshift:b, mode: :set, skip_cols: nil, # e lclip: false, rclip: false, no0shift: false, tx:ix, disable_intr:true, enable_intr:true, save_sp:true, scraddr:nil, jump_table:nil, subroutine:false) raise ArgumentError, "bobs_draw_pixels_fast: invalid scraddr argument" unless scraddr.nil? or direct_address?(scraddr) or (Integer === scraddr and scraddr == (scraddr & 0xE000)) raise ArgumentError, "bobs_draw_pixels_fast: target should be an address or a pointer or hl" unless target == hl or address?(target) unless [sp,ix,iy,hl].include?(bitmap) or address?(bitmap) raise ArgumentError, "bobs_draw_pixels_fast: bitmap should be an address or a pointer or ix/iy/sp/hl'" end raise ArgumentError, "bobs_draw_pixels_fast: bitmap should not be the same as jump_table" if bitmap == jump_table unless (register?(lines) and lines.bit8?) or address?(lines) raise ArgumentError, "bobs_draw_pixels_fast: lines should be a label or a pointer or an integer or a register" end cols = cols.to_i raise ArgumentError, "bobs_draw_pixels_fast: cols must be less than or equal to 32" if cols > 32 raise ArgumentError, "bobs_draw_pixels_fast: cols must be greater than or equal to 1" if cols < 1 raise ArgumentError, "bobs_draw_pixels_fast: disable_intr should be a boolean" unless [true, false].include?(disable_intr) raise ArgumentError, "bobs_draw_pixels_fast: enable_intr should be a boolean" unless [true, false].include?(enable_intr) raise ArgumentError, "bobs_draw_pixels_fast: save_sp should be a boolean" unless [true, false].include?(save_sp) raise ArgumentError, "bobs_draw_pixels_fast: save_sp makes no sense if bitmap = sp" if bitmap == sp and save_sp raise ArgumentError, "bobs_draw_pixels_fast: subroutine requires save_sp" if subroutine and !save_sp raise ArgumentError, "bobs_draw_pixels_fast: interrupts should be disabled if bitmap = sp" if bitmap == sp and disable_intr raise ArgumentError, "bobs_draw_pixels_fast: bshift should be one of: b, c, d or e" unless [b,c,d,e].include?(bshift) raise ArgumentError, "bobs_draw_pixels_fast: tx should be one of: ix or iy" unless [ix, iy].include?(tx) raise ArgumentError, "bobs_draw_pixels_fast: lclip should be a boolean" unless [true, false].include?(lclip) raise ArgumentError, "bobs_draw_pixels_fast: rclip should be a boolean" unless [true, false].include?(rclip) raise ArgumentError, "bobs_draw_pixels_fast: no0shift should be a boolean" unless [true, false].include?(no0shift) txh, txl = tx.split t = b m = c fx = case mode when :or then ->(tgt) { ora tgt } when :xor then ->(tgt) { xor tgt } when :and then ->(tgt) { anda tgt } when :nand then ->(tgt) { cpl; anda tgt } when :set, :copy then nil else raise ArgumentError, "bobs_draw_pixels_fast: mode should be one of: :or, :xor, :and, :set or :copy" end ncols = skip_cols skip_cols = case ncols # modifies: hl, sp (e: skip) when 0 then false when Integer then true else unless address?(ncols) or (register?(ncols) and ncols.bit8? and ncols != a) raise ArgumentError, "bobs_draw_pixels_fast: skip_cols should be one of: nil, an integer, an address or an 8-bit alt register except an accumulator" end true end if ncols local_jump_table = if jump_table.nil? true elsif [de, bc, ix, iy].include?(jump_table) jh, jl = jump_table.split if [jh, jl].include?(lines) or [jh, jl].include?(bshift) raise ArgumentError, "bobs_draw_pixels_fast: lines, bshift and jump_table should use different registers" end false else raise ArgumentError, "bobs_draw_pixels_fast: jump_table is not a label" unless label?(jump_table) jump_table = unwrap_pointer(jump_table).to_label(self) # in case a symbol was given false end isolate do jump_table = define_label(:jump_table) if local_jump_table # normalize in isolated ns ld a, lines unless lines == a ex af, af # a': lines ld hl, target unless target == hl # calculate 1st row counter and remaining lines ld a, h # calculate counter based on screen address modulo 8 anda 0b11111000 # (h & 0b11111000) sub h # = - h % 8 [-7, 0] exx # regs' if skip_cols and register?(ncols) ld e, ncols unless ncols == e end ld d, 8 # d: 8 (const) add d # = 8 - h % 8 [1, 8] ld b, a # b: counter = 8 - h % 8 [1, 8] ex af, af # a: lines, a': ... ld c, a # c: lines dec a # a: lines - 1 sub b # a: lines - 1 - counter jr NC, more_rows ld b, c # b: counter = c: lines more_rows ex af, af # a': lines left - 1, CF': last row? if skip_cols and address?(ncols) and pointer?(ncols) ld a, ncols ld e, a end # calculate routine address in jump_table from bshift exx # regs ld a, bshift # rshift add a, a # a: rshift * 2 add bshift # a: rshift * 3 if register?(jump_table) ld16 de, jump_table unless jump_table == de else jump_table_a ld de, jump_table jump_table_p jump_table_a + 1 unless jump_table.pointer? end add e ld e, a # de: ->routine # get routine address (tx) and bshift mask (m) if [sp, tx].include?(bitmap) # bitmap already in sp or from tx, can't use it for jump_table ld [restore_sp_p], sp if save_sp di if disable_intr ld sp, bitmap unless bitmap == sp ld a, [de] # a: routine.loop0.lo inc e ld txl, a ld a, [de] # a: routine.loop0.hi inc e ld txh, a # tx: routine.loop0 ld a, [de] # a: mask ld m, a # m: mask ld a, l # a: screen.lo exx # regs' else ex de, hl # hl: jump[], de: screen ld [restore_sp_p], sp if save_sp di if disable_intr ld sp, hl # sp: jump[] ex de, hl # hl: screen pop tx # tx: routine.loop0 pop t|m # m: mask ld a, l # a: screen.lo exx # regs' if direct_address?(bitmap) bitmap_a ld sp, bitmap # bitmap: address bitmap_p as bitmap_a + 1 else ld sp, bitmap # bitmap: ix/iy/hl' or indirect address (tx != bitmap) end end ld c, a # c': screen.lo exx # regs # hl: screen, m: mask, c': screen lo, b': counter # d': const 8, a': lines left - 1, CF': last row jp (tx) # pc: tx: routine.loop0 if local_jump_table jump_table bobs_draw_pixels_fast_jump_table nil end ########################### ## bshift routines ## ########################### no0shift = if no0shift quit else nil end bobs_draw_pixels_fast_routines(next_row, cols, mode:mode, skip_cols:ncols, lclip:lclip, rclip:rclip, no0shift: no0shift, merge: true, jump_eoc: false) ################################################ ## next row ## ################################################ ns :next_row do # check remaining lines and set up line counter for the next row ex af, af # a: lines left - 1, CF: last row? jr C, quit ld b, d # b: 8 (counter) sub b # a: lines left - 1 - 8 jr NC, not_last add b # a: lines left - 1, CF: 1 ld b, a # b: lines left - 1 inc b # b: lines left (counter) not_last ex af, af # a': lines left - 1, CF': last row? # calculate next row screen address ld a, c # a: lo add 0x20 # a: lo + 0x20 ld c, a # c': next row (screen.lo) exx # regs ld l, a # l: next row (screen.lo) jr C, loop0fw unless scraddr ld a, h # a: sreen.hi jr C, check_oos if scraddr sub 8 # a: sreen.hi - 8 ld h, a # h: adjusted sreen.hi loop0fw jp (tx) if scraddr check_oos cp (scraddr >> 8)|0x18 check_oos_p check_oos + 1 jr NC, quit jp (tx) end end unless subroutine && !enable_intr && !save_sp quit label end if save_sp restore_sp ld sp, 0 restore_sp_p as restore_sp + 1 end ei if enable_intr ret if subroutine end end
Creates a jump table for the Macros#bobs_draw_pixels_fast
routine.
Provide a label (or a symbol) referencing a routine created by either Macros#bobs_draw_pixels_fast
or Macros#bobs_draw_pixels_fast_routines
.
Returns a label addressing the created jump table.
Provide an address of a jump table as a jump_table:
option to bobs_draw_pixels_fast
.
- NOTE
-
The jump table must be aligned in memory so it does not cross 256-byte address pages. In other words, the high 8 bits of addresses of each jump table byte must be the same. Otherwise an error will be raised when trying to instanciate code.
The jump table occupies 24 bytes of code.
The content is lazy evaluated so the order of creating a table and a routine doesn't matter.
# File lib/zxlib/gfx/bobs.rb, line 1064 def bobs_draw_pixels_fast_jump_table(draw_pixels_fast_label) unless draw_pixels_fast_label.nil? throw ArgumentError, "draw_pixels_fast_label must be a label" unless direct_label?(draw_pixels_fast_label) draw_pixels_fast_label = draw_pixels_fast_label.to_label(self) end ns do size = 8*3-1 # correction, jump_table may reside on the far end of the page select((label + size) & 0xFF){|x| x >= size }.else do raise ArgumentError, "jump_table data must fit on a single 256-byte page of memory" end (0..7).each do |index| line_rshiftN = if draw_pixels_fast_label.nil? define_label :"line_rshift#{index}" else draw_pixels_fast_label.send :"line_rshift#{index}" end define_label :"jump#{index}", dw( line_rshiftN.loop0 ) define_label :"mask#{index}", db( 0xff >> index ) end end end
Creates specialized routines for Macros#bobs_draw_pixels_fast
that can be used externally via jump table.
- NOTE
-
skip_cols
option should matchskip_cols
given toMacros#bobs_draw_pixels_fast
if an 8-bit register (other thane'
) or an indirect (a pointer) address is specified.
next_row
-
An address of
next_row
routine. Usually:draw_pixels_fast_label.next_row
.
cols
-
A constant number of 8 pixel columns to be drawn as an integer from 1 to 32.
Options:
mode
-
Drawing mode, one of:
:copy
,:set
,:or
,:xor
,:and
,:nand
. SeeMacros#bobs_draw_pixels_fast
.
skip_cols
-
If set, the number of bitmap 8-bit columns will be skipped after each line is drawn. Set as an address, an integer or an 8-bit register from alternative set, e.g.
e
specifiese'
. Also see the note above.
lclip
-
Skips drawing the leftmost screen column for non zero
bshift
values if enabled.
rclip
-
Skips drawing the rightmost screen column for non zero
bshift
values if enabled.
no0shift
-
If this option is not
nil
skips creating the drawing routine forbshift
equal
to 0. Provide a label to
draw_pixels_fast_label.quit
or to an existing implementation, e.g.:draw_pixels_fast_routines.line_rshift0.loop0
. Calling such a routine withbshift
register holding zero is safe but nothing will be drawn and CF flag will be reset upon return ifquit
was provided.merge
-
Whether the
line_rshift{0-7}
labels should be merged with the current context.
jump_eoc
-
Whether the
jp next_row
should be appended at the end of the generated code.
# File lib/zxlib/gfx/bobs.rb, line 1110 def bobs_draw_pixels_fast_routines(next_row, cols, mode: :set, skip_cols: nil, lclip: false, rclip: false, no0shift: nil, merge: false, jump_eoc: true) throw ArgumentError, "next_row must be a label" unless direct_label?(next_row) cols = cols.to_i raise ArgumentError, "cols must be less than or equal to 32" if cols > 32 raise ArgumentError, "cols must be greater than or equal to 1" if cols < 1 raise ArgumentError, "lclip should be a boolean" unless [true, false].include?(lclip) raise ArgumentError, "rclip should be a boolean" unless [true, false].include?(rclip) raise ArgumentError, "merge should be a boolean" unless [true, false].include?(merge) raise ArgumentError, "jump_eoc should be a boolean" unless [true, false].include?(jump_eoc) raise ArgumentError, "no0shift should be a nil or a label (quit)" unless no0shift.nil? or direct_label?(no0shift) next_row = next_row.to_label(self) t = b m = c fx = case mode when :or then ->(tgt) { ora tgt } when :xor then ->(tgt) { xor tgt } when :and then ->(tgt) { anda tgt } when :nand then ->(tgt) { cpl; anda tgt } when :set, :copy then nil else raise ArgumentError, "mode should be one of: :or, :xor, :and, :set or :copy" end if !pointer?(skip_cols) && label_immediate?(skip_cols) ncols = skip_cols.to_i else ncols = skip_cols end raise ArgumentError, "skip_cols must not be negative" if Integer === ncols and ncols < 0 skip_cols = case ncols # modifies: hl, sp (e: skip) when 0 then nil when 1..5 n = if cols.odd? then ncols - 1 else ncols end lambda do (n/2).times { pop hl } inc sp if n.odd? end else unless address?(ncols) or (register?(ncols) and ncols.bit8? and ncols != a) raise ArgumentError, "skip_cols should be one of: nil, an integer, an address or an 8-bit alt register except an accumulator" end lambda do if address?(ncols) and !pointer?(ncols) if Integer === ncols && cols.odd? ld hl, ncols - 1 else ld hl, ncols end else ld l, e ld h, 0 end add hl, sp ld sp, hl end end if ncols rotate_bits = ->(rshift) do if (1..4).include?(rshift) rshift.times { rrca } elsif (5..7).include?(rshift) (8-rshift).times { rlca } end end isolate merge:merge do # hl: screen, c': screen lo, b': counter, d': const 8, a': lines left - 1, CF': last row # e': ncols if skip_cols ns :line_rshift0 do if no0shift loop0 as no0shift # created jump table will forward to this address else fx0 = if fx ->(r) do ld a, r fx.call [hl] ld [hl], a end else ->(r) { ld [hl], r } end rowloop ld a, c # a: screen.lo exx # regs ld l, a # l: screen.lo (restore) loop0 label (0...cols).step(2) do |col| last_column = (col >= cols - 2) pop de fx0.call e if last_column && cols.odd? dec sp unless skip_cols && ncols.is_a?(Integer) # offset last column else inc l fx0.call d end inc l unless last_column end inc h exx skip_cols.call if skip_cols djnz rowloop jp next_row end end # hl: screen, m: mask, c': screen lo, b': counter, d': const 8, a': lines left - 1, CF': last row # e': ncols if skip_cols [1,7,2,6,3,5,4].each do |rshift| ns :"line_rshift#{rshift}" do rowloop ld a, c # a: screen.lo (from c') exx # regs ld l, a # l: screen.lo (restore) loop0 label (0...cols).step(2) do |col| last_column = (col >= cols - 2) pop de # de: FEDCBA98 76543210 ld a, e # a: 76543210 rotate_bits.call rshift unless last_column and cols.odd? and rclip ld e, a # e: 32107654 (e.g. rshift=4) end unless col.zero? and lclip if !col.zero? # t: BA98FEDC xor t # a: xxxxxxxx (32107654 ^ BA98FEDC) t: BA98FEDC anda m # a: ____xxxx (____7654 ^ ____FEDC) m: mask 00001111 xor t # a: BA987654 (____7654 ^ ____FEDC ^ BA98FEDC) t: BA98FEDC elsif mode == :set ld t, [hl] # t: ssssssss xor t # a: xxxxxxxx (32107654 ^ ssssssss) t: ssssssss anda m # a: ____xxxx (____7654 ^ ____ssss) m: mask 00001111 xor t # a: ssss7654 (____7654 ^ ____ssss ^ ssssssss) t: ssssssss else anda m # a: ____7654 m: mask 00001111 end fx.call [hl] unless fx.nil? ld [hl], a # [hl]: ____7654(fx)[hl] (col=0) # or ssss7654 (col=0 and :set) # or BA987654(fx)[hl] (col>0) end cut_left label if last_column and cols.odd? dec sp unless skip_cols && ncols.is_a?(Integer) # offset last column unless rclip unless col.zero? and lclip inc l # l: screen.lo (next column) end if mode == :set unless col.zero? and lclip ld a, e # a: 32107654 end xor [hl] # a: xxxxxxxx (32107654 ^ ssssssss) anda m # a: ____xxxx (____7654 ^ ____ssss) m: 00001111 else unless col.zero? and mode == :copy and !lclip unless col.zero? and lclip ld a, e # a: 32107654 end anda m # a: ____7654 m: 00001111 end end # a: ____7654 (or ____7654 ^ ____ssss) xor e # a: 3210____ (or 3210ssss) e: 32107654 fx.call [hl] unless fx.nil? ld [hl], a # [hl]: 3210____(fx)[hl] end else ld a, d # a: FEDCBA98 rotate_bits.call rshift ld d, a # d: BA98FEDC (e.g. rshift=4) xor e # a: xxxxxxxx (BA98FEDC ^ 32107654) e: 32107654 anda m # a: ____xxxx (____FEDC ^ ____7654) m: mask 00001111 xor e # a: 3210FEDC (____FEDC ^ ____7654 ^ 32107654) e: 32107654 unless col.zero? and lclip inc l # l: screen.lo (next column) end fx.call [hl] unless fx.nil? ld [hl], a # [hl]: 3210FEDC(fx)[hl] unless last_column and rclip inc l # l: screen.lo (next column) if last_column ld a, d # a: BA98FEDC (e.g. rshift=4) if mode == :set xor [hl] # a: xxxxxxxx (BA98FEDC ^ ssssssss) end anda m # a: ____xxxx (____FEDC [^ ____ssss]) m: mask 00001111 xor d # a: BA98ssss (____FEDC [^ ____ssss] ^ BA98FEDC) d: BA98FEDC fx.call [hl] unless fx.nil? ld [hl], a # [hl]: BA98____(fx)[hl] else ld t, d # t: BA98FEDC end end end end inc h # h: screen.hi (next line) exx # regs' skip_cols.call if skip_cols djnz rowloop # b': line counter jp next_row if jump_eoc || rshift != 4 end end end end
Creates a routine that renders 7 bitmap textures by shifting 7 times each source bitmap lines by 1 bit to the right.
Rendered bitmaps are extended to the right by 1 column (an octet).
bitmap
-
An address of a source bitmap or
hl
.
lines
-
A number of lines or an 8-bit register holding the number of lines. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A number of 8-bit columns (octets) per line or an 8-bit register holding the number.
Options:
target
-
A destination address where bitmaps should be rendered or
de
.
Modifies: af
, af'
, bc
, de
, hl
and some stack.
# File lib/zxlib/gfx/bobs.rb, line 971 def bobs_rshift_bitmap_pixels_7times(bitmap=hl, lines=c, cols=a, target:de) isolate do if address?(lines) && pointer?(lines) ex af, af if cols == a ld a, lines ld c, a ex af, af if cols == a else ld c, lines unless lines == c end ld a, cols unless cols == a ld hl, bitmap unless bitmap == hl ld de, target unless target == de ld b, 7 scf bitloop push bc push de bobs_rshift_bitmap_pixels_once adc a, 0 pop hl pop bc djnz bitloop end end
Creates a routine that renders a bitmap texture by shifting each source bitmap lines by 1 bit to the right.
- Carry Flag
-
If set (CF=1) indicates that the target bitmap should be extended by one additional column holding the bits right-shifted out of the last source bitmap column. Otherwise (if CF=0) the carried out bits are lost. The Carry Flag value is preserved when the routine exits.
bitmap
-
An address of a source bitmap or
hl
.
lines
-
A number of lines or an 8-bit register holding the number of lines. The number 0 is treated as 256 and most likely will lead to UNDEFINED BEHAVIOUR.
cols
-
A number of 8-bit columns (octets) per line or an 8-bit register holding the number.
Options:
target
-
A destination address where bitmap should be rendered or
de
.
Modifies: af
, af'
, bc
, de
, hl
.
# File lib/zxlib/gfx/bobs.rb, line 1013 def bobs_rshift_bitmap_pixels_once(bitmap=hl, lines=c, cols=a, target:de) isolate do if address?(lines) && pointer?(lines) ex af, af if cols == a ld a, lines ld c, a ex af, af if cols == a else ld c, lines unless lines == c end ld a, cols unless cols == a ld hl, bitmap unless bitmap == hl ld de, target unless target == de line_start ld b, a ex af, af # a': cols, CF': 1 extend cols (right margin) ora a # CF: 0 line_loop ld a, [hl] inc hl rra ld [de], a inc de djnz line_loop ex af, af jr NC, skip_last ex af, af ld a, 0 rra ld [de], a inc de ex af, af skip_last dec c jr NZ, line_start end end