diff --git a/defs.asm b/defs.asm index bb583e5..44081f8 100644 --- a/defs.asm +++ b/defs.asm @@ -14,13 +14,25 @@ COLRAM = $d800 ; color RAM CIA1_PA = $dc00 ; CIA1 port A: keyboard matrix column select CIA1_PB = $dc01 ; CIA1 port B: keyboard matrix row read +; ---- sprite registers ---- +SPRPOS = $d000 ; sprite 0 x; +1 = y, +2 = sprite 1 x, ... +SPR_MSBX = $d010 ; 9th bit of each sprite's x coordinate +SPR_EN = $d015 ; sprite enable bits +SPR_YEX = $d017 ; vertical expand +SPR_PRIO = $d01b ; 1 = sprite behind the characters +SPR_MC = $d01c ; multicolor enable +SPR_XEX = $d01d ; horizontal expand +SPR_COL = $d027 ; sprite 0 color, +1 = sprite 1, ... + ; ---- colors ---- BLACK = 0 WHITE = 1 CYAN = 3 YELLOW = 7 +ORANGE = 8 DGREY = 11 LGREEN = 13 +LBLUE = 14 LGREY = 15 ; ---- screen layout (slide 1) ---- @@ -41,9 +53,15 @@ BAR_STEPS = 57 ; 57 steps * 4 lines = up to line 244 NUM_SLIDES = 5 SLIDE_TIME = 300 ; 6 seconds per slide (PAL, 50 frames/s) +S5_TIME = 450 ; slide 5 is longer: the typewriter needs time FADE_STEPS = 5 ; fade steps (longest fadetbl chain is 4) FADE_SPEED = 4 ; frames between two fade steps +; ---- transition types (tin_tbl / tout_tbl in main.asm) ---- +TR_FADE = 0 ; fade through black (the original) +TR_DISSOLVE = 1 ; cells appear/vanish in random order +TR_WIPE = 2 ; column sweep, left to right + FADEBUF = $c000 ; snapshot of color RAM for the fade-in ; ---- zero page pointers ---- diff --git a/main.asm b/main.asm index 54c271a..8fc306a 100644 --- a/main.asm +++ b/main.asm @@ -4,14 +4,16 @@ ; slide 2: OUR FIRST GAME - Definitely not an Impostor ; slide 3: OUR SECOND GAME IS COMING (border raster bars) ; slide 4: CONTACT -; slide 5: the assembly confession -; slides cross-fade through black between switches +; slide 5: the assembly confession (typewriter) +; slides transition through black between switches +; (fade / random dissolve / column wipe, per slide) ; all timed by polling the raster beam ; ; Files: -; defs.asm - constants, zero page, macros -; common.asm - shared routines, tables, variables -; slideN.asm - one file per slide: sN_init, sN_update + data +; defs.asm - constants, zero page, macros +; transitions.asm - slide transitions: fade, dissolve, wipe +; common.asm - shared routines, tables, variables +; slideN.asm - one file per slide: sN_init, sN_update + data ; ; Build: acme -f cbm -o main.prg main.asm ; ----------------------------------------------------------- @@ -90,7 +92,7 @@ dur_dec: bne main advance: - jsr fade_out ; fade the old slide to black + jsr trans_out ; hide the old slide (its out-transition) adv_release: jsr scan_space ; a held space must not race through beq adv_release ; the whole show @@ -106,7 +108,7 @@ slide_ok: jmp main go_back: - jsr fade_out + jsr trans_out back_release: jsr scan_del beq back_release @@ -176,170 +178,13 @@ slide_setup: lda #BLACK sta BORDER sta BG + sta SPR_EN ; a slide that wants sprites enables them itself jmp (vec) ; init ends with rts -> returns to slide_setup's caller ; ============================================================ -; slide transition: fade through black -; ============================================================ - -; ---- draw the current slide while the display is switched off -; (everything shows border color = black), snapshot its colors, -; blacken them, switch the display back on and fade in ---- -show_slide: - lda #$0b ; display off - sta SCRCTRL - jsr slide_setup - jsr snapshot_colram - lda #$1b ; display on - sta SCRCTRL - - ; fade in: fadecnt counts how many times the target color is - ; darkened; counting 4 -> 0 walks each cell from black back - ; up to its snapshot color - lda #FADE_STEPS-1 - sta fadecnt -fi_step: - jsr build_map - jsr apply_map - ldx #FADE_SPEED -fi_wait: - jsr wait_frame - dex - bne fi_wait - lda fadecnt - beq fi_done - dec fadecnt - jmp fi_step -fi_done: - rts - -; ---- darken the whole color RAM step by step until black ---- -fade_out: - lda #FADE_STEPS - sta fadecnt -fo_step: - ldx #FADE_SPEED -fo_wait: - jsr wait_frame - dex - bne fo_wait - jsr apply_fade - dec fadecnt - bne fo_step - rts - -; ---- wait for the next bottom-border arrival of the beam ---- -wait_frame: -wf_leave: - lda RASTER ; first leave line RAS_BOTTOM if we are on it - cmp #RAS_BOTTOM - beq wf_leave -wf_reach: - lda RASTER - cmp #RAS_BOTTOM - bne wf_reach - rts - -; ---- one fade-out step: every color RAM cell one shade darker ---- -apply_fade: - ldx #0 -af_loop: - lda COLRAM,x - and #15 ; color RAM reads have garbage upper bits - tay - lda fadetbl,y - sta COLRAM,x - lda COLRAM+250,x - and #15 - tay - lda fadetbl,y - sta COLRAM+250,x - lda COLRAM+500,x - and #15 - tay - lda fadetbl,y - sta COLRAM+500,x - lda COLRAM+750,x - and #15 - tay - lda fadetbl,y - sta COLRAM+750,x - inx - cpx #250 - bne af_loop - rts - -; ---- copy color RAM to FADEBUF, then blacken color RAM ---- -snapshot_colram: - ldx #0 -sn_loop: - lda COLRAM,x - and #15 - sta FADEBUF,x - lda COLRAM+250,x - and #15 - sta FADEBUF+250,x - lda COLRAM+500,x - and #15 - sta FADEBUF+500,x - lda COLRAM+750,x - and #15 - sta FADEBUF+750,x - lda #BLACK - sta COLRAM,x - sta COLRAM+250,x - sta COLRAM+500,x - sta COLRAM+750,x - inx - cpx #250 - bne sn_loop - rts - -; ---- curmap[c] = color c darkened fadecnt times ---- -build_map: - ldx #15 -bm_color: - txa - tay - lda fadecnt - sta bmcnt - beq bm_store ; 0 steps: identity map -bm_apply: - lda fadetbl,y - tay - dec bmcnt - bne bm_apply -bm_store: - tya - sta curmap,x - dex - bpl bm_color - rts - -; ---- write curmap[FADEBUF] into the whole color RAM ---- -apply_map: - ldx #0 -am_loop: - ldy FADEBUF,x - lda curmap,y - sta COLRAM,x - ldy FADEBUF+250,x - lda curmap,y - sta COLRAM+250,x - ldy FADEBUF+500,x - lda curmap,y - sta COLRAM+500,x - ldy FADEBUF+750,x - lda curmap,y - sta COLRAM+750,x - inx - cpx #250 - bne am_loop - rts - -; ============================================================ -; shared routines and the slides +; transitions, shared routines and the slides ; ============================================================ +!src "transitions.asm" !src "common.asm" !src "slide1.asm" !src "slide2.asm" @@ -357,16 +202,19 @@ slide_upd_lo: slide_upd_hi: !byte >s1_update, >s2_update, >s3_update, >s4_update, >s5_update slide_dur_lo: - !byte SLIDE_TIME, >SLIDE_TIME, >SLIDE_TIME, >SLIDE_TIME, >SLIDE_TIME + !byte >SLIDE_TIME, >SLIDE_TIME, >SLIDE_TIME, >SLIDE_TIME, >S5_TIME + +; ---- per-slide transitions: reveal type / hide type ---- +tin_tbl: + !byte TR_FADE, TR_WIPE, TR_DISSOLVE, TR_FADE, TR_DISSOLVE +tout_tbl: + !byte TR_DISSOLVE, TR_FADE, TR_WIPE, TR_DISSOLVE, TR_WIPE ; ---- variables ---- slide: !byte 0 ; current slide index durlo: !byte 0 ; frames left on this slide (16 bit) durhi: !byte 0 -fadecnt: !byte 0 ; fade step counter -bmcnt: !byte 0 ; build_map inner counter -curmap: !fill 16, 0 ; current fade-in color map paused: !byte 0 ; 1 = slide countdown frozen pheld: !byte 0 ; 'p' key edge detection state diff --git a/slide1.asm b/slide1.asm index 03c73ee..4265094 100644 --- a/slide1.asm +++ b/slide1.asm @@ -1,7 +1,7 @@ ; ----------------------------------------------------------- ; slide1.asm - giant TTG logo + credits scroller ; effects: gold wash on the logo, smooth pixel scroll, -; pulsing title +; pulsing title, raster color gradient behind the scroller ; ----------------------------------------------------------- s1_init: @@ -12,9 +12,9 @@ s1_init: +prtext 17, 9, LGREEN, txt_www, txt_www_end - txt_www +prtext 19, 14, LGREY, txt_city, txt_city_end - txt_city - ; scroller row color + ; scroller row color: white, so it reads on the blue gradient ldx #39 - lda #CYAN + lda #WHITE scrollcol_loop: sta COLRAM+SCROLLROW*40,x dex @@ -36,12 +36,30 @@ wait_scrtop: lda xscroll ; bits 0-2 = x scroll, bit 3 = 0 -> 38 columns sta XCTRL + ; blue raster gradient behind the scroller: one background + ; color per line, chasing the beam through lines 161-170 + ldx #0 +s1_grad: + txa + clc + adc #RAS_SCRTOP+1 +s1_gwait: + cmp RASTER + bne s1_gwait + lda gradtbl,x + sta BG + inx + cpx #10 + bne s1_grad + wait_scrbot: lda RASTER cmp #RAS_SCRBOT bne wait_scrbot lda #$c8 ; back to default: 40 columns, no scroll sta XCTRL + lda #BLACK ; gradient off below the scroller row + sta BG ; beam is in the lower border: do all updates now, invisibly wait_bottom: @@ -213,6 +231,9 @@ bigfont: letter_off: ; column offset of each letter (8 wide + 2 gap) !byte 0, 10, 20 +gradtbl: ; scroller backdrop: blue ramp, cyan peak + !byte $06, $06, $0e, $0e, $03, $03, $0e, $0e, $06, $06 + ; texts in screen codes (!scr); lowercase source shows as uppercase on C64 txt_title: !scr "teletype games" diff --git a/slide2.asm b/slide2.asm index bf9d13c..321ba50 100644 --- a/slide2.asm +++ b/slide2.asm @@ -1,6 +1,8 @@ ; ----------------------------------------------------------- ; slide2.asm - OUR FIRST GAME: Definitely not an Impostor -; big block letters with the gold wash running across them +; big block letters with the gold wash running across them; +; each text block gets a shifted wash phase, so the glow +; rolls through the three lines diagonally ; ----------------------------------------------------------- s2_init: @@ -24,7 +26,8 @@ s2_update: inc washphase s2_hold: - ; gold wash across all 15 big-letter rows (4-8, 10-14, 16-20) + ; gold wash across all 15 big-letter rows (4-8, 10-14, 16-20); + ; each block is 5 wash steps behind the one above -> diagonal ldx #39 s2_loop: txa @@ -38,11 +41,23 @@ s2_loop: sta COLRAM + 6*40,x sta COLRAM + 7*40,x sta COLRAM + 8*40,x + tya + clc + adc #5 + and #15 + tay + lda washtbl,y sta COLRAM + 10*40,x sta COLRAM + 11*40,x sta COLRAM + 12*40,x sta COLRAM + 13*40,x sta COLRAM + 14*40,x + tya + clc + adc #5 + and #15 + tay + lda washtbl,y sta COLRAM + 16*40,x sta COLRAM + 17*40,x sta COLRAM + 18*40,x diff --git a/slide3.asm b/slide3.asm index 1b7d6c9..91e9589 100644 --- a/slide3.asm +++ b/slide3.asm @@ -1,7 +1,8 @@ ; ----------------------------------------------------------- ; slide3.asm - OUR SECOND GAME: IS COMING -; raster bars running on the border only, drop-shadowed -; big letters in the middle +; raster bars on the border that also bleed into the empty +; top and bottom of the screen; the middle stays black, so +; the drop-shadowed big letters remain readable ; ----------------------------------------------------------- s3_init: @@ -25,6 +26,8 @@ s3_hold: ; chase the beam: every 4th raster line from RAS_BARTOP down, ; set the BORDER color from the cyclic 64-entry bar table; + ; above line 74 and from line 210 the screen rows are empty, + ; there the bars run on the background too - in the middle ; the background stays black so the text remains readable lda #RAS_BARTOP sta s3line @@ -40,7 +43,19 @@ s3_wait: and #63 tay lda rastbl,y + ldy s3line + cpy #74 ; top zone: bar color on the background too + bcc s3_bg + cpy #210 ; bottom zone likewise + bcs s3_bg + sta BORDER ; middle: border only, background back to + lda #BLACK ; black (the top zone left its last color) + sta BG + jmp s3_step +s3_bg: + sta BG sta BORDER +s3_step: lda s3line clc adc #4 @@ -51,6 +66,7 @@ s3_wait: lda #BLACK ; below the bar area: back to black sta BORDER + sta BG s3_wbot: ; keep the one-call-per-frame contract lda RASTER diff --git a/slide4.asm b/slide4.asm index 98f7791..396c6cd 100644 --- a/slide4.asm +++ b/slide4.asm @@ -1,6 +1,8 @@ ; ----------------------------------------------------------- ; slide4.asm - CONTACT -; pulsing big title, contact lines below +; pulsing big title, contact lines, three-layer starfield +; drifting left, six star sprites floating on sine paths +; behind the text ; ----------------------------------------------------------- s4_init: @@ -9,6 +11,43 @@ s4_init: +prtext 10, 7, LGREEN, txt_web, txt_web_end - txt_web +prtext 13, 6, YELLOW, txt_bbs, txt_bbs_end - txt_bbs +prtext 16, 4, CYAN, txt_irc, txt_irc_end - txt_irc + + ; starfield: draw the stars at their current positions so + ; the in-transition reveals them together with the text + ldx #11 +s4i_star: + jsr star_ptr + ldy star_x,x + lda #$2e ; '.' + sta (scrlo),y + lda star_col,x + sta (collo),y + dex + bpl s4i_star + + ; sprites: same star shape on all six, floating behind the + ; characters; enabled by the update, so they pop in only + ; after the slide's in-transition is done + lda #(sprdata >> 6) + ldx #5 +s4i_ptr: + sta SCREEN+$3f8,x + dex + bpl s4i_ptr + ldx #5 +s4i_col: + lda sprcols,x + sta SPR_COL,x + dex + bpl s4i_col + lda #0 + sta SPR_MC + sta SPR_XEX + sta SPR_YEX + lda #%00111111 + sta SPR_PRIO ; behind the text + lda #%00100000 ; sprite 5 lives right of x=255 + sta SPR_MSBX rts s4_update: @@ -33,6 +72,79 @@ s4_loop: sta COLRAM + 6*40,x dex bpl s4_loop + + ; starfield: three layers, each layer steps left on its own + ; frame mask (fast/mid/slow parallax) + ldx #11 +s4u_star: + lda framectr + and star_mask,x + bne s4u_next + jsr star_ptr + ldy star_x,x + lda #$20 ; erase at the old position + sta (scrlo),y + lda star_x,x ; step left, wrap at the edge + bne s4u_dec + lda #40 + sta star_x,x +s4u_dec: + dec star_x,x + ldy star_x,x + lda #$2e + sta (scrlo),y + lda star_col,x + sta (collo),y +s4u_next: + dex + bpl s4u_star + + ; sprites: y from a sine table, x = base + slower sine wobble + lda #%00111111 + sta SPR_EN + ldx #5 +s4u_spr: + txa + asl + sta sprofs ; register offset = sprite * 2 + lda framectr + lsr + clc + adc spr_yph,x + and #63 + tay + lda ysin,y + ldy sprofs + sta SPRPOS+1,y + lda framectr + lsr + lsr + clc + adc spr_xph,x + and #63 + tay + lda xwob,y + clc + adc spr_xbase,x + ldy sprofs + sta SPRPOS,y + dex + bpl s4u_spr + rts + +; ---- scrlo/hi + collo/hi = screen/color RAM row of star X ---- +star_ptr: + ldy star_row,x + lda mul40lo,y + sta scrlo + sta collo + lda mul40hi,y + clc + adc #>SCREEN + sta scrhi + clc + adc #$d4 ; COLRAM - SCREEN high byte distance + sta colhi rts ; ---- data ---- @@ -47,3 +159,72 @@ txt_bbs_end: txt_irc: !scr "irc: libera.chat #teletypegames" txt_irc_end: + +; starfield: rows avoid the title (2-6) and text (10, 13, 16) rows +star_row: + !byte 0, 8, 11, 14, 17, 19, 1, 9, 12, 15, 21, 23 +star_mask: ; move on framectr & mask == 0 + !byte 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7 +star_col: ; fast = white, mid = lgrey, slow = dgrey + !byte WHITE, LGREY, DGREY, WHITE, LGREY, DGREY + !byte WHITE, LGREY, DGREY, WHITE, LGREY, DGREY + +sprcols: + !byte YELLOW, LBLUE, LGREEN, WHITE, ORANGE, CYAN +spr_xbase: ; spread across the screen; sprite 5 gets + !byte 40, 85, 130, 175, 220, 20 ; +256 via SPR_MSBX +spr_yph: ; sine phase offsets per sprite + !byte 0, 11, 21, 32, 43, 53 +spr_xph: + !byte 0, 26, 52, 13, 39, 7 + +ysin: ; 64-entry sine, 134 +/- 60 (sprite y) + !byte $86, $8c, $92, $97, $9d, $a2, $a7, $ac + !byte $b0, $b4, $b8, $bb, $bd, $bf, $c1, $c2 + !byte $c2, $c2, $c1, $bf, $bd, $bb, $b8, $b4 + !byte $b0, $ac, $a7, $a2, $9d, $97, $92, $8c + !byte $86, $80, $7a, $75, $6f, $6a, $65, $60 + !byte $5c, $58, $54, $51, $4f, $4d, $4b, $4a + !byte $4a, $4a, $4b, $4d, $4f, $51, $54, $58 + !byte $5c, $60, $65, $6a, $6f, $75, $7a, $80 + +xwob: ; 64-entry sine, 12 +/- 12 (x wobble) + !byte $0c, $0d, $0e, $0f, $11, $12, $13, $14 + !byte $14, $15, $16, $17, $17, $17, $18, $18 + !byte $18, $18, $18, $17, $17, $17, $16, $15 + !byte $14, $14, $13, $12, $11, $0f, $0e, $0d + !byte $0c, $0b, $0a, $09, $07, $06, $05, $04 + !byte $04, $03, $02, $01, $01, $01, $00, $00 + !byte $00, $00, $00, $01, $01, $01, $02, $03 + !byte $04, $04, $05, $06, $07, $09, $0a, $0b + +; 24x21 four-pointed star sprite (must sit on a 64-byte boundary +; and below $4000, the top of the VIC's bank) +!align 63, 0 +sprdata: + !byte $00, $00, $00 + !byte $00, $00, $00 + !byte $00, $18, $00 + !byte $00, $18, $00 + !byte $00, $18, $00 + !byte $00, $3c, $00 + !byte $00, $3c, $00 + !byte $00, $7e, $00 + !byte $07, $ff, $e0 + !byte $00, $7e, $00 + !byte $00, $3c, $00 + !byte $00, $3c, $00 + !byte $00, $18, $00 + !byte $00, $18, $00 + !byte $00, $18, $00 + !byte $00, $00, $00 + !byte $00, $00, $00 + !byte $00, $00, $00 + !byte $00, $00, $00 + !byte $00, $00, $00 + !byte $00, $00, $00 + +; ---- variables ---- +star_x: ; star columns (start scattered) + !byte 3, 17, 29, 8, 35, 22, 13, 31, 5, 26, 19, 37 +sprofs: !byte 0 ; sprite register offset in the update loop diff --git a/slide5.asm b/slide5.asm index bfd4687..6563f94 100644 --- a/slide5.asm +++ b/slide5.asm @@ -1,16 +1,17 @@ ; ----------------------------------------------------------- ; slide5.asm - the assembly confession +; the body is typed in live, one character per frame, with a +; white block cursor; the closing line appears when the +; typing is done and pulses ; ----------------------------------------------------------- s5_init: jsr clear_screen +prtext 3, 11, WHITE, txt_conf1, txt_conf1_end - txt_conf1 - +prtext 7, 3, LGREY, txt_conf2, txt_conf2_end - txt_conf2 - +prtext 9, 3, LGREY, txt_conf3, txt_conf3_end - txt_conf3 - +prtext 12, 3, WHITE, txt_conf4, txt_conf4_end - txt_conf4 - +prtext 15, 3, LGREY, txt_conf5, txt_conf5_end - txt_conf5 - +prtext 17, 3, LGREY, txt_conf6, txt_conf6_end - txt_conf6 - +prtext 21, 11, CYAN, txt_conf7, txt_conf7_end - txt_conf7 + lda #0 ; the body starts empty and gets typed in + sta t_line + sta t_chr + sta t_done rts s5_update: @@ -19,6 +20,9 @@ s5_update: bne s5_update inc framectr + lda t_done + beq s5_type + lda framectr ; pulse on the "(the ai says hi.)" line lsr lsr @@ -33,6 +37,55 @@ s5_loop: bpl s5_loop rts +; ---- type one character per frame ---- +s5_type: + ldx t_line + ldy t_row,x ; screen/color pointers to the line start + lda mul40lo,y + clc + adc #3 ; body column + sta scrlo + sta collo + lda mul40hi,y + adc #>SCREEN ; carry still clear (row starts stay low) + sta scrhi + clc + adc #$d4 + sta colhi + lda t_txtlo,x + sta txtlo + lda t_txthi,x + sta txthi + + ldy t_chr + lda (txtlo),y ; the character lands where the cursor was + sta (scrlo),y + lda t_color,x + sta (collo),y + iny + cpy #34 ; all body lines are exactly 34 chars + beq s5_nextline + sty t_chr + lda #$a0 ; block cursor at the next cell + sta (scrlo),y + lda #WHITE + sta (collo),y + rts + +s5_nextline: + lda #0 + sta t_chr + inc t_line + lda t_line + cmp #5 + bne s5_ret + ; typing done: the closing line appears and starts pulsing + +prtext 21, 11, CYAN, txt_conf7, txt_conf7_end - txt_conf7 + lda #1 + sta t_done +s5_ret: + rts + ; ---- data ---- txt_conf1: !scr "a quick confession" @@ -41,19 +94,28 @@ txt_conf1_end: ; exactly 34 chars, so both edges line up at columns 3 and 36 txt_conf2: !scr "we speak c/c++, lua, python, java," -txt_conf2_end: txt_conf3: !scr "go, ruby, js, elixir, pascal." -txt_conf3_end: txt_conf4: !scr "but 6502 assembly? not a word." -txt_conf4_end: txt_conf5: !scr "so this slideshow was written" -txt_conf5_end: txt_conf6: !scr "with a little help from an ai." -txt_conf6_end: txt_conf7: !scr "(the ai says hi.)" txt_conf7_end: + +t_row: ; body line rows + !byte 7, 9, 12, 15, 17 +t_color: + !byte LGREY, LGREY, WHITE, LGREY, LGREY +t_txtlo: + !byte txt_conf2, >txt_conf3, >txt_conf4, >txt_conf5, >txt_conf6 + +; ---- variables ---- +t_line: !byte 0 ; body line being typed (0-4) +t_chr: !byte 0 ; next character within the line +t_done: !byte 0 ; 1 = typing finished, closing line pulses diff --git a/transitions.asm b/transitions.asm new file mode 100644 index 0000000..e6d7bf1 --- /dev/null +++ b/transitions.asm @@ -0,0 +1,373 @@ +; ----------------------------------------------------------- +; transitions.asm - slide transitions +; every slide is drawn with the display switched off, its +; colors are snapshotted into FADEBUF and blackened, then an +; in-transition reveals it; an out-transition hides it again. +; types (picked per slide in tin_tbl/tout_tbl in main.asm): +; TR_FADE - fade through black via the fadetbl ramps +; TR_DISSOLVE - cells appear/vanish in random (LFSR) order +; TR_WIPE - column sweep from left to right +; each reveal starts with a short grey border flash +; ----------------------------------------------------------- + +; ---- draw the current slide while the display is switched off +; (everything shows border color = black), snapshot its colors, +; blacken them, switch the display back on, flash, transition in ---- +show_slide: + lda #$0b ; display off + sta SCRCTRL + jsr slide_setup + jsr snapshot_colram + lda #$1b ; display on + sta SCRCTRL + + ldx #0 ; short white-to-grey border flash: gives +ssf_loop: ; the reveal a beat + lda flashtbl,x + sta BORDER + jsr wait_frame + inx + cpx #3 + bne ssf_loop + lda #BLACK + sta BORDER + + ldx slide ; dispatch the slide's in-transition + ldy tin_tbl,x + lda tin_lo,y + sta vec + lda tin_hi,y + sta vec+1 + jmp (vec) ; the transition ends with rts + +; ---- dispatch the slide's out-transition ---- +trans_out: + lda #0 ; sprites vanish with the slide + sta SPR_EN + ldx slide + ldy tout_tbl,x + lda tout_lo,y + sta vec + lda tout_hi,y + sta vec+1 + jmp (vec) + +tin_lo: + !byte fade_in, >dissolve_in, >wipe_in +tout_lo: + !byte fade_out, >dissolve_out, >wipe_out + +flashtbl: + !byte WHITE, LGREY, DGREY + +; ============================================================ +; TR_FADE - fade through black +; ============================================================ + +; ---- fade in: fadecnt counts how many times the target color is +; darkened; counting 4 -> 0 walks each cell from black back +; up to its snapshot color ---- +fade_in: + lda #FADE_STEPS-1 + sta fadecnt +fi_step: + jsr build_map + jsr apply_map + ldx #FADE_SPEED +fi_wait: + jsr wait_frame + dex + bne fi_wait + lda fadecnt + beq fi_done + dec fadecnt + jmp fi_step +fi_done: + rts + +; ---- darken the whole color RAM step by step until black ---- +fade_out: + lda #FADE_STEPS + sta fadecnt +fo_step: + ldx #FADE_SPEED +fo_wait: + jsr wait_frame + dex + bne fo_wait + jsr apply_fade + dec fadecnt + bne fo_step + rts + +; ---- one fade-out step: every color RAM cell one shade darker ---- +apply_fade: + ldx #0 +af_loop: + lda COLRAM,x + and #15 ; color RAM reads have garbage upper bits + tay + lda fadetbl,y + sta COLRAM,x + lda COLRAM+250,x + and #15 + tay + lda fadetbl,y + sta COLRAM+250,x + lda COLRAM+500,x + and #15 + tay + lda fadetbl,y + sta COLRAM+500,x + lda COLRAM+750,x + and #15 + tay + lda fadetbl,y + sta COLRAM+750,x + inx + cpx #250 + bne af_loop + rts + +; ---- curmap[c] = color c darkened fadecnt times ---- +build_map: + ldx #15 +bm_color: + txa + tay + lda fadecnt + sta bmcnt + beq bm_store ; 0 steps: identity map +bm_apply: + lda fadetbl,y + tay + dec bmcnt + bne bm_apply +bm_store: + tya + sta curmap,x + dex + bpl bm_color + rts + +; ---- write curmap[FADEBUF] into the whole color RAM ---- +apply_map: + ldx #0 +am_loop: + ldy FADEBUF,x + lda curmap,y + sta COLRAM,x + ldy FADEBUF+250,x + lda curmap,y + sta COLRAM+250,x + ldy FADEBUF+500,x + lda curmap,y + sta COLRAM+500,x + ldy FADEBUF+750,x + lda curmap,y + sta COLRAM+750,x + inx + cpx #250 + bne am_loop + rts + +; ============================================================ +; TR_DISSOLVE - cells appear/vanish in random order +; a 10-bit LFSR walks all indexes 1..1023 exactly once per +; period; indexes >= 1000 are skipped, cell 0 (which the LFSR +; never yields) is handled up front. 25 cells per frame -> +; the full screen takes 40 frames (0.8 s). +; ============================================================ + +dissolve_in: + lda FADEBUF ; cell 0 first + sta COLRAM + lda #40 + sta frcnt +din_frame: + jsr wait_frame + lda #25 + sta cellcnt +din_cell: + jsr lfsr_next ; -> lfsrlo/hi = cell index 1..999 + lda lfsrlo + sta scrlo + sta collo + lda lfsrhi + clc + adc #>FADEBUF + sta scrhi + lda lfsrhi + clc + adc #>COLRAM + sta colhi + ldy #0 + lda (scrlo),y ; restore the snapshot color + sta (collo),y + dec cellcnt + bne din_cell + dec frcnt + bne din_frame + rts + +dissolve_out: + lda #BLACK ; cell 0 first + sta COLRAM + lda #40 + sta frcnt +dot_frame: + jsr wait_frame + lda #25 + sta cellcnt +dot_cell: + jsr lfsr_next + lda lfsrlo + sta collo + lda lfsrhi + clc + adc #>COLRAM + sta colhi + ldy #0 + lda #BLACK + sta (collo),y + dec cellcnt + bne dot_cell + dec frcnt + bne dot_frame + rts + +; ---- 10-bit Galois LFSR (x^10 + x^7 + 1, period 1023) ---- +; steps until the state is a valid cell index (< 1000); the +; state is never reset, coverage holds from any starting point +lfsr_next: + lsr lfsrhi + ror lfsrlo + bcc ln_notap + lda lfsrhi + eor #$02 + sta lfsrhi + lda lfsrlo + eor #$40 + sta lfsrlo +ln_notap: + lda lfsrhi ; skip 1000..1023 ($03e8..$03ff) + cmp #$03 + bcc ln_ok + lda lfsrlo + cmp #$e8 + bcs lfsr_next +ln_ok: + rts + +; ============================================================ +; TR_WIPE - column sweep, one column per frame, left to right +; ============================================================ + +wipe_in: + lda #0 + sta wcol +wi_col: + jsr wait_frame + ldx #24 +wi_row: + lda mul40lo,x + sta scrlo + sta collo + lda mul40hi,x + clc + adc #>FADEBUF + sta scrhi + lda mul40hi,x + clc + adc #>COLRAM + sta colhi + ldy wcol + lda (scrlo),y ; restore the snapshot color + sta (collo),y + dex + bpl wi_row + inc wcol + lda wcol + cmp #40 + bne wi_col + rts + +wipe_out: + lda #0 + sta wcol +wo_col: + jsr wait_frame + ldx #24 +wo_row: + lda mul40lo,x + sta collo + lda mul40hi,x + clc + adc #>COLRAM + sta colhi + ldy wcol + lda #BLACK + sta (collo),y + dex + bpl wo_row + inc wcol + lda wcol + cmp #40 + bne wo_col + rts + +; ============================================================ +; shared helpers +; ============================================================ + +; ---- wait for the next bottom-border arrival of the beam ---- +wait_frame: +wf_leave: + lda RASTER ; first leave line RAS_BOTTOM if we are on it + cmp #RAS_BOTTOM + beq wf_leave +wf_reach: + lda RASTER + cmp #RAS_BOTTOM + bne wf_reach + rts + +; ---- copy color RAM to FADEBUF, then blacken color RAM ---- +snapshot_colram: + ldx #0 +sn_loop: + lda COLRAM,x + and #15 + sta FADEBUF,x + lda COLRAM+250,x + and #15 + sta FADEBUF+250,x + lda COLRAM+500,x + and #15 + sta FADEBUF+500,x + lda COLRAM+750,x + and #15 + sta FADEBUF+750,x + lda #BLACK + sta COLRAM,x + sta COLRAM+250,x + sta COLRAM+500,x + sta COLRAM+750,x + inx + cpx #250 + bne sn_loop + rts + +; ---- variables ---- +fadecnt: !byte 0 ; fade step counter +bmcnt: !byte 0 ; build_map inner counter +curmap: !fill 16, 0 ; current fade-in color map +lfsrlo: !byte $37 ; LFSR state (any nonzero seed) +lfsrhi: !byte $01 +cellcnt: !byte 0 ; dissolve: cells left this frame +frcnt: !byte 0 ; dissolve: frames left +wcol: !byte 0 ; wipe: current column