Skip to content

Instantly share code, notes, and snippets.

@ped7g
Last active February 3, 2021 20:38
Show Gist options
  • Save ped7g/ad3fb234fb800df7a22e858c4d2daf4f to your computer and use it in GitHub Desktop.
Save ped7g/ad3fb234fb800df7a22e858c4d2daf4f to your computer and use it in GitHub Desktop.
ZX Spectrum fade-in/fade-out effects using attributes, focusing on small code size (second version)
; Authors: Omega, Ped7g, Baze ; (C) 2021 ; license: https://opensource.org/licenses/MIT
; Z80 assembly, syntax for sjasmplus: https://github.com/z00m128/sjasmplus
; to assemble run: sjasmplus omega_fades_2.asm
OPT --syntax=abf
DEVICE ZXSPECTRUM48,31999
ORG $8000
; uncomment and set to available SCR file to run the effect on ZX screen image (6912 bytes)
; DEFINE SCR_FILE "diver - Mercenary 4. The Heaven's Devil (2014) (Forever 2014 Olympic Edition, 1).scr"
;------------------------------------------------------------------------------
; ULA attributes "fade in/out" effect, add/sub 1 to PAPER/INK/BRIGHT components
; during seven iterations (call of routine), to add/sub total value of "target"
; attribute at area $D800..$DCFF
; (the starting attribute must be zero/target_value, to reach correct result)
;
; Input: DE = instruction table pointer, target/source attribute values at $D800..$DCFF
; modifies AF, BC', DE', HL', ++DE
atrFade:
; setup second instruction of bit-seeking pair, from table at DE
ld a,(de)
inc de
ld (.s),a
exx ; rest of routine works in shadow registers
; self-mod setup of bit-seeking finished, run the main loop
ld hl,$5800
ld de,$D800 ; FLASH+BRIGHT is treated as third component (F+B together)
ld bc,(3<<8) | %01'001'001 ; bit-mask of PAPER/INK bottom bits, and FLASH+BRIGHT
.doThird: ; process 256 bytes (third of ULA attributes)
ld a,(de) ; target attribute value + advance pointer to next one
inc e
; bit seeking pair (second instruction is modified to target correct bit)
srl a ; can't ensure carry here, in fade-out it becomes CF=1 from previous
.s: DB $00 ; rra / nop / rla (to check b2 / b1 / b0 of the target-value triplet)
and c ; having +1/+8/+64 bits for INK/PAPER/B+F as needed
DB $ED
.n: DB $00 ; "NOP" for fade-in, "NEG" for fade-out effect
add a,(hl) ; +current attribute (raising it toward target/lowering it to zero)
ld (hl),a ; write the patched attribute into ULA VRAM
inc l
jp nz,.doThird ; process next attribute, until 256 of them were done
inc d ; adjust high-bytes of pointers to be ready for next screen-third
inc h
djnz .doThird
exx ; revert to regular reg-set
ret
secondProcessInstruction: ; to extract correct target bit of each three-bit component
rra ; b2
nop ; b1
rra ; b2
rla ; b0
rra ; b2
nop ; b1
rra ; b2
DISPLAY "atrFade routine size: ",/D,$-atrFade
InitFadeIn:
xor a ; NOP (instead of NEG) to finalize calculated value
ld (atrFade.n),a
ld de,secondProcessInstruction ; reset self-modify-code LUT pointer
ret
InitFadeOut:
ld a,$44 ; NEG to finalize calculated value
ld (atrFade.n),a
ld de,secondProcessInstruction ; reset self-modify-code LUT pointer
ret
;------------------------------------------------------------------------------
; calling 10 times "fade in", then 10 times "fade out", and then again
; to demonstrate them.
; Press keys 1 to 5 to set delay between iteractions (1 = 50 FPS, 5 = 3 FPS)
startDEMO:
call PrepareTestData ; some pixels and "target" attribute data
call ClsAttributes ; make sure the attributes are really all-zeroed before Fade-in
ei
halt
; start of fadeIn + FadeOut main-thread example
.demoLoop:
; init fade-In (target attributes are ready at $D800)
call InitFadeIn
ld b,7
.fadeInLoop:
call delayAndKeys
call atrFade
djnz .fadeInLoop
; empty iteraction delay to see result of fade-in
ld b,7
.pauseLoopA:
call delayAndKeys
djnz .pauseLoopA
; init fade-out (self-modify-code LUT pointer)
call InitFadeOut
ld b,7
.fadeOutLoop:
call delayAndKeys
call atrFade
djnz .fadeOutLoop
; empty iteraction delay to see result of fade-out
ld b,7
.pauseLoopB:
call delayAndKeys
djnz .pauseLoopB
jr .demoLoop
delayAndKeys:
xor a
out (254),a ; BORDER 0
ld a,1 ; HALT n-many times (constant is affected by keyboard)
.haltDelay:
halt
dec a
jr nz,.haltDelay
; check for keys 1-5 and adjust delay wait based on the key (1 = fast, 5 = slow)
ld a,~(1<<3) ; fourth keyboard-matrix row, keys 1-5
in a,(254) ; read the keyboard matrix, flip the logic (to 1 = pressed)
cpl
and $1F ; if any key from 1-5 was pressed, it forms new wait-delay value
jr z,.noKeyPressed
ld (.haltDelay-1),a ; modify "LD A,1" instruction with new wait value
.noKeyPressed:
; do BORDER 1 when delay is 50 FPS (for slower ones keep black border)
ld a,(.haltDelay-1)
dec a
ret nz
inc a
out (254),a ; BORDER 1 (to see performance-timing visually in border stripe)
ret
; zeroing attributes of ULA VRAM (black ink, black paper, black border)
ClsAttributes:
xor a
out (254),a ; BORDER 0
ld hl,$5800
ld de,$5801
ld bc,$02FF
ld (hl),a
ldir
ret
; Sets test-data for FadeOut/FadeIn demoing:
; - some pixels in the ULA VRAM at $4000
; - at $D8000 creating "target attributes", three blocks of 0..255 values
IFNDEF SCR_FILE
; no SCR file was defined, create debug-data by code
PrepareTestData:
; set some pixels in ULA
ld hl,$4000
ld de,$4001
ld bc,$0100
ld (hl),%1010'0110
ldir
ld bc,$00FF
ld (hl),%0101'0110
ldir
ld hl,$4000
ld bc,$1800-$200
ldir
; at $D800: 3x 0-255 attribute bytes in "target" area for FadeIn effect
ld hl,$D800
ld b,3
.doThird:
ld (hl),e
inc l
ld (hl),e
inc l
ld (hl),e
inc l
ld (hl),e
inc e
res 7,e ; don't use FLASH 1 at all
inc l
jr nz,.doThird
inc h
djnz .doThird
ret
ELSE
; SCR file was defined, include that directly into VRAM for pixels and D800 for attributes
PrepareTestData: ; no code, data are already loaded in VRAM/D800 buffer by INCBIN
ret
ORG $4000 : INCBIN SCR_FILE, 0, $1800 : ORG $D800 : INCBIN SCR_FILE, $1800, $300
ENDIF
SAVESNA "omega_fades_2.sna", startDEMO : CSPECTMAP "omega_fades_2.map"
DISPLAY "Try keys 1 to 5 to change speed-delay between effect calls"
IFDEF LAUNCH_EMULATOR : IF 0 == __ERRORS__ && 0 == __WARNINGS__
SHELLEXEC "( sleep 0.2s ; runCSpect -debug -brk -map=omega_fades_2.map -w3 omega_fades_2.sna ) &"
ENDIF : ENDIF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment