5 ; This subroutine does a byte-at-a-time breadth-first patterned flood fill.
6 ; It works by allocating a circular queue on the stack, with the size of the
7 ; queue determined by an input parameter. The queue is divided into three
8 ; contiguous blocks: the pattern block, the investigate block and the new block.
9 ; The queue contains 3-byte structures (described below) with a special structure
10 ; delimiting each block. The physical end of the queue is marked with a special
11 ; byte. The contents of the queue grow down in memory.
13 ; The pattern block holds pixels that have been blackened on screen and are
14 ; only waiting to have the pattern applied to them before they are removed
17 ; The investigate block holds pixels that have been blackened on screen and
18 ; are waiting to be investigated before they become part of the pattern
19 ; block. 'Investigating' a pixel means trying to expand the fill in all
20 ; four directions. Any successful expansion causes the new pixel to be
21 ; added to the new block.
23 ; The new block holds pixels that have been blackened on screen and are
24 ; waiting to become part of the investigate block. The new block expands
25 ; as the investigate block is investigated.
27 ; The pattern fill algorithm follows these steps:
28 ; 1. Examine the investigate block. Add new pixels to the new block if an
29 ; expansion is possible.
30 ; 2. Pattern the pattern block. All pixels waiting to be patterned are
31 ; patterned on screen.
32 ; 3. The investigate block becomes the pattern block.
33 ; The new block becomes the investigate block.
34 ; 4. Repeat until the investigate block is empty.
36 ; The algorithm may bail out prematurely if the queue fills up. Bailing
37 ; out causes any pending pixels in the queue to be patterned before the
38 ; subroutine exits. If PFILL would continue to run by refusing to enter
39 ; new pixels when the queue is full, there would be no guarantee that
40 ; the subroutine would ever return.
42 ; In English, the idea behind the patterned flood fill is simple. The
43 ; start pixel grows out in a circular shape (actually a diamond). A
44 ; fill boundary two pixels thick is maintained in that circular shape.
45 ; The outermost boundary is the frontier, and is where the flood fill
46 ; is growing from (ie the investigate block). The inner boundary is
47 ; the pattern block, waiting to be patterned. A solid inner boundary
48 ; is necessary to prevent the flood-fill frontier pixels from growing
49 ; back toward the starting pixel through holes in the pattern. Once
50 ; the frontier pixels are investigated, the innermost boundary is
51 ; patterned. The newly investigated pixels become the outermost
52 ; boundary (the investigate block) and the old investigate block becomes
53 ; the new pattern block.
55 ; Each entry in the queue is a 3-byte struct that grows down in memory:
56 ; screen address (2-bytes, MSB first)
58 ; A screen address with MSB < 0x40 is used to indicate the end of a block.
59 ; A screen address with MSB >= 0x80 is used to mark the physical end of the Q.
61 ; The fill pattern is a typical 8x8 pixel character, stored in 8 bytes.
63 ; enter: h = y coord, l = x coord, bc = max stack depth, de = address of fill pattern
64 ; In hi-res mode, carry flag is most significant bit of x coord
65 ; used : ix, af, bc, de, hl
66 ; exit : no carry = success, carry = had to bail queue was too small
67 ; stack: 3*bc+30 bytes, not including the call to PFILL or interrupts
70 push de ; save (pattern pointer) variable
71 dec bc ; we will start with one struct in the queue
72 push bc ; save max stack depth variable
74 call SPGetScrnAddr ; de = screen address, b = pixel byte
75 ex de,hl ; hl = screen address
76 call .bytefill ; b = fill byte
83 ex de,hl ; de = screen address, b = fill byte
86 push hl ; create pattern block pointer = top of queue
88 pop ix ; ix = top of queue
92 push hl ; create investigate block pointer
95 push hl ; create new block pointer
99 dec sp ; mark end of pattern block
100 push de ; screen address and fill byte are
101 push bc ; first struct in investigate block
104 dec sp ; mark end of investigate block
107 ld b,(ix+8) ; bc = max stack depth - 1
111 add hl,bc ; space required = 3*BC (max depth) + 10
112 add hl,bc ; but have already taken 9 bytes
114 ld b,h ; bc = # uninitialized bytes in queue
116 sbc hl,bc ; negate hl, additions above will not set carry
118 ld (hl),0 ; zero last byte in queue
119 ld sp,hl ; move stack below queue
121 push af ; mark end of queue with $80 byte
127 ldir ; zero the uninitialized bytes in queue
129 ; NOTE: Must move the stack before clearing the queue, otherwise if an interrupt
130 ; occurred, garbage could overwrite portions of the (just cleared) queue.
132 ; ix = top of queue, bottom of queue marked with 0x80 byte
134 ; Variables indexed by ix, LSB first:
135 ; ix + 11/12 return address
136 ; ix + 09/10 fill pattern pointer
137 ; ix + 07/08 max stack depth
138 ; ix + 05/06 pattern block pointer
139 ; ix + 03/04 investigate block pointer
140 ; ix + 01/02 new block pointer
142 ; A picture of memory at this point:
144 ;+-----------------------+ higher addresses
146 ;|- return address -| \|/
148 ;+-----------------------+ lower addresses
150 ;|- pattern pointer -|
152 ;+-----------------------+
154 ;|- max stack depth -|
156 ;+-----------------------+
160 ;+-----------------------+
162 ;|- investigate block -|
164 ;+-----------------------+
168 ;+-----------------------+
169 ;| end of block marker | <- ix = pattern block = top of queue
172 ;+-----------------------+
173 ;| screen address MSB | <- investigate block
174 ;| screen address LSB |
176 ;+-----------------------+
177 ;| end of block marker |
180 ;+-----------------------+
184 ;+-----------------------+
186 ;| ...... | size is a multiple of 3 bytes
191 ;+-----------------------+
192 ;| 0x80 | <- sp, special byte marks end of queue
193 ;+-----------------------+
197 ld h,(ix+4) ; hl = investigate block
199 ld d,(ix+2) ; de = new block
202 ld (ix+2),d ; save new block
204 ld (ix+4),h ; save investigate block
207 ld h,(ix+6) ; hl = pattern block
209 ld b,(ix+8) ; bc = max stack depth (available space)
212 ld (ix+8),b ; save stack depth
214 ld (ix+6),h ; save pattern block
216 ld a,(hl) ; done if the investigate block was empty
221 ld de,11 ; return address is at ix+11
224 or a ; make sure carry is clear, indicating success
227 ; IN/OUT: hl = investigate block, de = new block
230 cp 0x80 ; bit 15 of screen addr set if time to wrap
233 pop hl ; hl = ix = top of queue
237 cp 0x40 ; screen address < 0x4000 marks end of block
238 jr c,.endinv ; are we done yet?
241 ld c,(hl) ; bc = screen address
243 ld a,(hl) ; a = fill byte
245 push hl ; save spot in investigate block
247 ld h,b ; hl = screen address
248 ld b,a ; b = fill byte
251 push hl ; save screen address
252 call SPPixelUp ; move screen address up one pixel
253 jr c,.updeadend ; if went off-screen
254 push bc ; save fill byte
256 call c,.addnew ; if up is not dead end, add this to new block
257 pop bc ; restore fill byte
260 pop hl ; restore screen address
263 push hl ; save screen address
264 call SPPixelDown ; move screen address down one pixel
266 push bc ; save fill byte
268 call c,.addnew ; if down is not dead end, add this to new block
269 pop bc ; restore fill byte
272 pop hl ; restore screen address
275 bit 7,b ; can only move left if leftmost bit of fill byte set
280 bit 5,h ; for hi-res mode: column = 1 if l=0 and bit 5 of h is set
284 push hl ; save screen address
286 push bc ; save fill byte
287 ld b,0x01 ; set rightmost pixel for incoming byte
289 call c,.addnew ; if left is not dead end, add this to new block
290 pop bc ; restore fill byte
291 pop hl ; restore screen address
294 bit 0,b ; can only move right if rightmost bit of fill byte set
298 jr c,.nextinv ; went off screen
301 jr z,.nextinv ; wrapped around line
302 ld b,0x80 ; set leftmost pixel for incoming byte
304 call c,.addnew ; if right is not dead end, add this to new block
307 pop hl ; hl = spot in investigate block
313 dec hl ; investigate block now points at new block
315 ld a,(de) ; check if new block is at end of queue
319 ld d,hx ; de = ix = top of queue
323 ld (de),a ; store end marker for new block
329 ; enter b = incoming byte, hl = screen address
330 ; exit b = fill byte, screen blackened with fill byte
333 xor (hl) ; zero out incoming pixels that
334 and b ; run into set pixels in display
339 rra ; expand incoming pixels
340 ld c,a ; to the right and left
346 xor (hl) ; zero out pixels that run into
347 and c ; set pixels on display
349 jr nz,.bfloop ; keep going until incoming byte does not change
352 ld (hl),a ; fill byte on screen
353 scf ; indicate that this was a viable step
356 ; add incoming fill byte and screen address to new block
357 ; enter b = incoming byte, hl = screen address, de = new block
359 push hl ; save screen address
361 ld h,(ix+8) ; hl = max stack depth
364 jr z,.bail ; no space in queue so bail!
365 dec hl ; available queue space decreases by one struct
368 pop hl ; hl = screen address
370 ld a,(de) ; check if new block is at end of queue
374 ld d,hx ; de = ix = top of queue
378 ld (hl),d ; make struct, store screen address (2 bytes)
382 ld (hl),b ; store fill byte (1 byte)
387 ; if the queue filled up, we need to bail. Bailing means patterning any set pixels
388 ; which may still be on the display. If we didnt bail and tried to trudge along,
389 ; there is no guarantee the fill would ever return.
391 pop hl ; hl = screen address, b = fill byte
394 ld (hl),a ; clear this byte on screen
396 ld (de),a ; mark end of new block
398 ld h,(ix+6) ; hl = pattern block
399 call .applypattern ; for pattern block
400 call .applypattern ; for investigate block
401 call .applypattern ; for new block
402 ld de,11 ; return address is at ix+11
405 scf ; indicate we had to bail
408 ; hl = pattern block, bc = max stack depth (available space)
411 cp 0x80 ; bit 15 of screen addr set if time to wrap
414 pop hl ; hl = ix = top of queue
417 cp 0x40 ; screen address < 0x4000 marks end of block
418 jr c,.endapply ; are we done yet?
419 and 0x07 ; use scan line 0..7 to index pattern
424 ld d,a ; de points into fill pattern
425 ld a,(de) ; a = pattern
428 ld e,(hl) ; de = screen address
430 and (hl) ; and pattern with fill byte
431 sub (hl) ; or in complement of fill byte
434 and (hl) ; apply pattern to screen
438 inc bc ; increase available queue space
444 dec hl ; pattern block now pts at investigate block
450 ; Returns the screen address and pixel mask corresponding
451 ; to a given pixel coordinate.
453 ; enter: a = h = y coord
455 ; exit : de = screen address, b = pixel mask
456 ; uses : af, b, de, hl
458 and #07 ; A = 00000SSS
459 or #40 ; A = 01000SSS
460 ld d,a ; D = 01000SSS
461 ld a,h ; A = Y coord = BBLLLSSS
465 and #18 ; A = 000BB000
467 ld d,a ; D = 010BBSSS top 8 bits of address done
468 ld a,l ; A = X coord = CCCCCTTT
469 and #07 ; A = 00000TTT
470 ld b,a ; B = 00000TTT = which pixel?
471 ld a,#80 ; A = 10000000
472 jr z,.norotate ; if B=0, A is the right pixel so skip
474 rra ; rotate the pixel right one place B times
477 ld b,a ; B = pixel mask
481 ld a,h ; A = Y coord = BBLLLSSS
484 and #E0 ; A = LLL00000
486 ld e,a ; E = LLLCCCCC
487 ret ; DE = 010BBSS LLLCCCCC, the screen address!
490 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
494 ;; HL: scraddrnext -- prev y line
512 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
516 ;; HL: scraddrnext -- next y line