Page 1 of 1

Stack tricks

Posted: Tue Dec 29, 2020 5:16 pm
by presh
My confidence with manipulating SP is growing, having successfully implemented my first stack-based screen blitter with Display and Attribute rows interleaved.

It got me thinking - what else can we do faster utilising the stack pointer?

I discovered a "clear memory" routine in my code which I figured could go faster:

Code: Select all

  ; clear 256 byte heap
  LD HL, heap_addr  ; divisible by $100 [10]
  XOR A             ; [4]
REPT 256
  LD (HL), A        ; [7] x256
  INC L             ; [4] x256
ENDM
Total: 2830 Ts in 516 bytes

Using SP:

Code: Select all

  ; clear 256 byte heap
  LD (sp_store), SP       ; [20]
  LD SP, heap_addr + $100 ; [10]
  LD HL, 0                ; [10]
REPT 128
  PUSH HL                 ; [11] x128
ENDM
  LD SP, (sp_store)       ; [20]
Total: 1468 Ts in 142 bytes - nearly half the time, and nearly 1/4 of the memory! :shock: :)


I've also manipulated SP in a look-up table when drawing sprites; after each row, I simply POP the screen address at which the next pixel row starts, and then just add the sprite's x position... much quicker than the arithmetic & logic required to determine and adjust for the cell / block boundaries (at least within my implementation!) but also much simpler-looking code, cleaner and easier to understand without all those extra logical caveats.

I'm sure none of this is anything new, so my question is: which other cool SP-based tricks can you think of?

Re: Stack tricks

Posted: Tue Dec 29, 2020 5:53 pm
by cmal
Nice clearing trick!
Similar to what you've done, I've used the stack to load sprite data to the screen instead of loading the screen address. It's not new or groundbreaking. I think quite a lot of games do this. That way you can pop two sprite bytes from the stack at once.

Something like this:

Code: Select all

pop de
ld (hl),e
inc l
ld (hl),d
For the screen address, you can fairly quickly look it up:

Code: Select all

;L = Y coordinate
;E = X coordinate
ld h,HIGH ScreenAdrTable (address is divisable by 256)
ld a,(hl)	;get display address low byte
inc h
ld h,(hl)	;get display address high byte
add a,e
ld l,a

Re: Stack tricks

Posted: Tue Dec 29, 2020 6:20 pm
by presh
cmal wrote: Tue Dec 29, 2020 5:53 pm I've used the stack to load sprite data to the screen instead of loading the screen address.
[...]

Code: Select all

pop de
ld (hl),e
inc l
ld (hl),d
Nice, exactly the kind of thing I'm after! And I guess you could modify this to work with interleaved mask/graphic data...

Code: Select all

POP DE
LD A, (HL)
AND E       ; mask
OR D        ; graphic
LD (HL), A

Re: Stack tricks

Posted: Tue Dec 29, 2020 7:08 pm
by cmal
You can use the stack to simplify code to some extent, I guess. For example writing strings, you can have the string following right after the call:

Code: Select all

		call	write_string
		DEFM "This sentence will prin", "t"+128
		ld	c,2
		...


write_string:
                di
		ex	(sp),hl	;HL points to start of string
		...
		...		;Code to print the string
		...
		ex	(sp),hl	;before this point, HL must point to the location right after the last character in the string
		ei
		ret

Re: Stack tricks

Posted: Wed Dec 30, 2020 6:11 pm
by presh
Found another one!

The final chunk of code on this page shows how to quickly print repeated background tiles to screen:

"ZX Spectrum Ghost 'n Goblins: graphics routines"
http://www.emix8.org/ggdisasm/

Image

I'm assuming a similar method is/could be used to generate tiled parallax backgrounds such as those in Rastan - just shift the tile graphics first, then POP the data and PUSH to screen en-masse.

In fact there are repeating tiles everywhere you look in this one. Hmm!

https://www.youtube.com/watch?v=QDzrkndlnWk

Re: Stack tricks

Posted: Sat Jan 02, 2021 3:51 am
by cmal
That was an interesting read on Ghosts 'n Goblins. I always liked how smooth the scrolling was in that game. Only now that I realize that the two right-most columns are attributed out for clipping reasons :o . I just assumed that was the black border.

Re: Stack tricks

Posted: Wed Jan 13, 2021 2:49 pm
by presh
You can do arithmetic on the stack pointer - in Hell Yeah!'s sprite engine, I need to skip a precalculated number of data bytes after each row if the sprite has its width cropped. Turn's out, that's dead easy!

Code: Select all

    ; Get the byte offset
    ; (this value was self-modified by the code earlier for speed)
    LD HL, 8    
    ; Add the current SP value (sprite data pointer)
    ADD HL, SP
    ; Set the new SP value
    LD SP, HL
Or if you just want HL = SP

Code: Select all

    LD HL,0
    ADD HL, SP
(HL can be substituted for IX or IY in all of the above)