; _____ __
; / _ \_______| | ______ ______
; / /_\ \_ __ \ |/ / _ \/ ___/
; / | \ | \/ < <_> )___ \
; \____|__ /__| |__|_ \____/____ >
; \/ \/ \/
; Twither, a 256 bytes intro presented at Forever 2009.
; (252 bytes exactly =)
;
; Hires(640x200) and fullscreen twister bar with some sort
; of dithering effet (not exactly what I planned first but
; I miserably failed to squeeze it all in 256 bytes =).
;
; The twister is 256 (mode 2) pixels wide with 256 steps.
;
; It works on CRTC type 0, 1 and 3.
; It should work on CRTC type 4 (not tested)
; It simply can't work on CRTC type 2.
;
; Grim/Arkos^Semilanceata
;
; NOTES
; - content at &0Cxx corrupted by the fx 64us innerloop, DO NOT USE!
;*** Configuration ************************************************************
; Compile a small build test-code and debug stuff
rubberbar_debug equ 0
; Configure CRTC synchronization
; &E0 - Type 0.
; &F0 - Type 1, 3 (probably 4).
rubberbar_cnf_crtcsync equ &F0
; Sinus lookup tables addresses
rubberbar_data_lut_sinus1024 equ &8800
rubberbar_data_lut_sinus256 equ &0900 ; opt - hibyte used as CRTC Select reg 9
;******************************************************************************
if rubberbar_debug
org &9F00
run $
; clear screen
ld a,2
call &BC0E
jp rubberbar_exec
else
run rubberbar_exec
endif
org &A000
; Dithering patterns
rubberbar_data_dither_seq db 1,17,21,85,87,119,127,255
;rubberbar_data_dither_seq db 255,127,119,87,85,21,17,1
rubberbar_exec:
; disable 300Hz interrupts
di
; select screen mode 2
ld bc,&7F8E
out (c),c
; set ink 1 color to red (&4C)
ld de,&014C
out (c),d
out (c),e
; Wild CRTC 6845 initialization
ld hl,&0701
;ld de,&0100+16
ld e,16
call rubberbar_crtset
;*** Generate a 1024 bytes sinus (8bits unsigned) lookup table *****************
cnf_math_singen_sizeopt_store equ rubberbar_data_lut_sinus1024
cnf_math_singen_inline equ 1
cnf_math_singen_unsigned equ 1
read "math.singen.sizeopt.asm"
; Output
; HL=0
; DE=0
; BC=1
; save HL=&0000 (used as VRAM pointer later)
push hl
; clear vram
inc e
ld (hl),l
ld bc,&87FF
ldir
;*** Generate a 256 bytes sinus (8bits unsigned) lookup table ******************
; 256 bytes sinus
;ld h,rubberbar_data_lut_sinus1024/256
inc hl
ld d,rubberbar_data_lut_sinus256/256
ld c,4
_rubberbar_256b_singen_loop ld a,(hl)
add a,e
ld (de),a
add hl,bc
inc e
jr nz,_rubberbar_256b_singen_loop
;*** Generate the twisterbar graphic *******************************************
ld hl,rubberbar_data_lut_sinus1024 + &180 ; pi/2+pi/4
exx
pop hl ;ld hl,&0000
call rubberbar_generator
call rubberbar_generator
;*** 50Hz Twister display loop *************************************************
; *** 50Hz loop ***
rubberbar_sync ld b,&F5
in a,(c)
rra
jr nc,rubberbar_sync+2
; On first frame, VCC will overflow, and the
; display will be fucked up. No space to waste
; for a clean first 20ms frame =)
; CRTC0 => Wait VCC=2 & VLC=7 (last scanline)
; CRTC1 => Wait VCC=0 & VLC=0 (new screen)
ld b,rubberbar_cnf_crtcsync
djnz $
; setup CRTC for rubberbar fx
ld hl,&0400
ld de,&0900 ; doube usage, &0900 => CRTCReg9=0 & 256bytes sinus LUT address
call rubberbar_crtset
; update twister-sin pointers
ld hl,&0131
_rubberbar_var_sinptr equ $-2
ld bc,&FD04
add hl,bc
ld (_rubberbar_var_sinptr),hl
ld e,h
ld h,d
;ld h,rubberbar_data_lut_sinus256/256
;ld d,h
; rubberbar height (rasterlines)
ld bc,287
; *** 64us innerloop ***
_rubberbar_fx_loop
; A=(sin(DE)+sin(HL)) AND 255
ld a,(de)
inc e
add a,(hl)
inc l
exx
; convert A into screen offset
ld h,&0C
ld (hl),a
xor a
rld
ld d,a
ld e,(hl)
; AE=%xxxxPPLL.LLLL0000
and h ;%1100 = &C
add a,a
add a,a
or d
; AE=%xxPPxxLL.LLLL0000
ld l,a
ld d,&0D
call rubberbar_crtset
exx
; 64us rasterline loop
dec bc
ld a,b
or c
jr nz,_rubberbar_fx_loop
; *** end of splitscreen ***
;ld hl,&0907
ld l,7
ld de,&0402
call rubberbar_crtset
; loop forever
jr rubberbar_sync
;*** Subroutines ***************************************************************
rubberbar_crtset: ; write two CRTC registers
ld b,&BC
out (c),h ; select register
inc b
out (c),l ; write register
dec b
out (c),d ; select register
inc b
out (c),e ; write register
ret
rubberbar_generator:
; draw 64 lines of the twister per 16k page.
; there's 4 pages => 256 lines. Only the firsts
; &800 bytes of each pages is used.
ld b,64
_rubberbar_generator_16k
;save vram pointer
push hl
dec l
exx
; save sin pointer
push hl
; read x1 = cos(a+45)
ld a,(hl)
ld c,a
; set the plotter to the x1 position
exx
inc a
ld c,%0000001
_rubberbar_generator_locate rrc c
jr nc,$+2+1
inc l
dec a
jr nz,_rubberbar_generator_locate
exx
; read x2 = cos(a-45)
dec h
;res 2,h
ld a,(hl)
sub c ; x2-x1 = Line lenght
ld b,a
_rubberbar_generator_plot
; 8bits dither level
ld a,(hl)
;res 2,h
; 3bits dither index
rlca
rlca
rlca
and %111
; 8bits dither value
ld e,a
ld d,rubberbar_data_dither_seq/256
ld a,(de) ; read dithering byte
; plot pixel
exx
and c ; apply bitmask
or (hl) ; merge with vram
ld (hl),a
rrc c ; next pixel, rotate bitmask
jr nc,$+3
inc hl ; move to next byte
exx
jr nc,$+3
inc hl
; repeat for line-lenght pixels
djnz _rubberbar_generator_plot
_rubberbar_generator_plot_skip
; restore sin pointer
pop hl
; move to next angle (0, 511)
inc hl
;res 2,h
exx
; move to the next line
pop hl
ld de,32
add hl,de
djnz _rubberbar_generator_16k
; move to the next 16k page
ld de,&3800
add hl,de
jr nc,rubberbar_generator
; exit after 4x16k pages processed
ret