====== TechTech using FLI routine ======
===== Introduction =====
Below is an example of a tech-tech effect, achieved using a so-called FLI-routine. This is **not** a tech-tech with a FLI logo, its a simple FLI based routine to allow videoram switching at every rasterline.
The routine differs from [[magazines:chacking7#tech-tech_-_more_resolution_to_vertical_shift]|this article]] in that it uses videoram banks to create the tech-tech, not character sets. One could combine these two techniques by cleverly interleaving character sets and videoram to allow for a wider sinus, perhaps even bank switching with $dd00 for even more movement. I'll leave that as an exercise for the reader ;)
{{:base:tech-tech-fli.png?404|Screenshot of the tech-tech in action}}
I wrote this code as part of an attempt to regain my old VIC-trickery skills, it's been about 25 years since I did a tech-tech. As such, the code may not be up to today's standards. I just published it here since there was no article on tech-tech's using $d011/FLI.
Note: I don't explain FLI in detail in this article, there are other articles here which explain FLI properly.
The code can be assembled by using [[https://sourceforge.net/projects/tass64/|64tass]]:
64tass -C -a -o techtech.prg techtech.asm
===== The Theory =====
//I'll use the word 'logo' here to simplify things a bit. My code doesn't actually include a logo, just some text
copied from the BASIC boot screen.//
Since $d016 only allows us to scroll 8 pixels, we need a way to scroll more. We can achieve that using multiple videoram banks (screens). For pixels 0-7 we use a screen with the logo at its left-most position. For each 8 pixels extra, we put the logo one column to the right from the previous position in a new videoram bank, and so on.
An illustration would probably be clearer:
screen: $4800 $4c00 $5000 $5400 and so on..
column: 01234567 01234567 01234567 01234567
logo: HELLO HELLO HELLO HELLO
Now we can select a column-offset of the logo using videoram banks (bit 7-4 of $d018) and we can use $d016 (bit 0-2) to select a pixel offset of 0-7.
For example, we want a line of the logo scrolled 21 pixels from its left-most position. We can achieve this with setting $d016 to 5 (21 & 7 == 5) and setting $d018 to $40 (21 / 8 == 2, multiply with 16 to get the correct videoram bits, and add $20 to skip the first two videoram banks which contain the character set)
In pseudo-code:
lda logo_offset ; some value from the sinus table
and #7 ; ORA with $10 for multi-color
sta $d016
lda logo_offset
asl ; divide by 8, multiply by 16 == multiply by 2
and #$f0 ; mask out lower nybble
clc
adc #$20 ; add $20 to skip the first two videoram banks
; since the character set occupies those banks
sta $d018
This code we need to do for each (raster)line of the logo. But we have a problem...
===== The Problem =====
Manipulating $d016 on each raster line works fine, manipulating $d018 doesn't: the videoram pointer only gets updated on bad lines (every 8th raster line), so we need a way to update the videoram pointer on each raster line. That's were FLI comes in.
===== The Solution =====
Using the so-called FLI technique, we can trigger bad lines at every raster line, allowing us to manipulate the videoram pointer at every raster line.
So our basic tech-tech display routine would look like this: (loop unrolled to keep the FLI bug at three characters while using a single sprite to cover the bug)
; sinus = 0
lda #$1b
sta $d011
lda #$00 ; 0 & 7 == 0
sta $d016
lda #$20 ; column 0
sta $d018
; sinus = 3
; next rasterline
lda #$1c
sta $d011
lda #$03
sta $d016
lda #$20 ; still column 0
sta $d018
; sinus = 6
lda #$1d
sta $d011
lda #$06
sta $d016
lda #$20
sta $d018
; sinus = 9
lda #$1e
sta $d011
lda #$01 ; 9 & 7 == 1
sta $d016
lda #$30
sta $d018 ; column 1: 9 pixels means we need to select
; the second videoram bank to move the logo
; one column to the right
; and so on for each line of the logo, wrapping the $d011
; value around from $1f to $18
Now we can tech-tech the logo by storing the correct values for $d016 and $d018 in the unrolled code, using for example a sinus table. The example code contains a routine which pre-calculates the $d016 and $d018 values from a sinus table for faster performance and another unrolled loop to store these values in the FLI-routine each frame, again for performance.
===== The Code =====
Here's the actual source code, in 64tass syntax. The FLI routine needs to be unrolled, the sinus calculation can all be done in a loop, but that eats cycles, so I also wrote some unrolled code for the $d016/$d018 values updating.
Before assembling, one should probably comment out the references to the music, I would be surprised to see everyone having their HVSC at "/home/compyx/c64/HSVC" ;)
; vim: set et ts=8 sw=8 sts=8 syntax=64tass :
;
; Simple tech-tech using FLI, 112 pixels wide sinus, using one charset and
; 14 videoram banks in $4000-$7fff.
;
;
; 2016-04-20
; Music, a nice old school JCH tune
music_sid ="/home/compyx/c64/HVSC/MUSICIANS/J/JCH/Ninjackie.sid"
music_init = $1000
music_play = $1003
; height of the tech-tech in pixels
TECHTECH_HEIGHT = 88
; size of a single line of FLI code
TECHTECH_MACRO_LEN = 15
; location of the FLI-bug cover sprite
COVER_SPRITE = $7f80
; zero page
zp = $14
; BASIC SYS line: SYS2061
* = $0801
.word (+), 2016
.null $9e, ^start
+ .word 0
; Entry point
start
jsr $fda3
jsr $fd15
; jsr $ff5b
sei
lda #0
sta $d020
sta $d021
jsr tt_setup
lda #0
jsr music_init
lda #$35
sta $01
lda #$7f
sta $dc0d
sta $dd0d
lda #0
sta $dc0e
lda #$01
sta $d01a
lda #$1b
sta $d011
lda #$2e
ldx #irq1
sta $d012
stx $fffe
sty $ffff
ldx #break
stx $fffa
sty $fffb
stx $fffc
sty $fffd
bit $dc0d
bit $dd0d
inc $d019
cli
jmp *
; IRQ: use the 'double IRQ' trick to stabilize the raster
irq1
pha
txa
pha
tya
pha
lda #$2e
ldx #irq2
sta $d012
stx $fffe
sty $ffff
lda #1
inc $d019
tsx
cli
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
irq2
txs
ldx #8
- dex
bne -
bit $ea
lda $d012
cmp $d012
beq +
+
; raster is stable here
clc
lda #$32
sta $d001
adc #42
sta $d003
lda #0
sta $d027
sta $d028
lda #%00000011 ; stretch cover sprites in X and Y direction
sta $d017
sta $d015
sta $d01d
lda #$08 ; we need to conver the first four chars on screen,
sta $d000 ; since we have the classic three char FLI bug and we
sta $d002 ; need one more char to cover since we use $d016, which
lda #$7b ; scrolls the FLI bug into the screen
sta $d011
lda #2
sta $dd00
; waste some cycles to start the FLI routine at the correct time
ldx #7
- dex
bne -
nop
nop
nop
; the unrolled tech-tech FLI routine
jsr tt_unrolled
; use invalid graphics mode to cover bugs
lda #$7b
sta $d011
lda #$03
sta $dd00
lda #$15
sta $d018
lda #8
sta $d016
ldx #89 ; waste cycles until we've covered a full screen row
- dex
bne -
lda #$1b
sta $d011
lda #$98
ldx #irq3
sta $d012
stx $fffe
sty $ffff
lda #1
sta $d019
pla
tay
pla
tax
pla
break rti
irq3
pha
txa
pha
tya
pha
dec $d020
jsr tt_sinus_unrolled ; calculate the tech-tech's sinus data
dec $d020
jsr music_play
lda #0
sta $d020
lda #$2d
ldx #irq1
sta $d012
stx $fffe
sty $ffff
lda #1
sta $d019
pla
tay
pla
tax
pla
rti
; Tech-tech setup code
tt_setup
; copy CHARGEN
;
; For the 'logo', we use the CBM font
lda #$32
sta $01
ldx #0
- lda $d000,x
sta $4000,x
lda $d100,x
sta $4100,x
lda $d200,x
sta $4200,x
lda $d300,x
sta $4300,x
lda $d400,x
sta $4400,x
lda $d500,x
sta $4500,x
lda $d600,x
sta $4600,x
lda $d700,x
sta $4700,x
inx
bne -
; ldx #7
; lda #0
;- sta $47f8,x
; dex
; bpl -
; generate FLI-bug cover sprite
ldx #$3f
lda #$ff
- sta COVER_SPRITE,x
dex
bpl -
; Set up the videoram banks for the tech-tech effect
;
; The first videoram bank at $4800 contains the 'logo' in its
; default (left-aligned) position, every next videoram bank contains
; the 'logo' shifted one column to the right. This is what makes the
; tech-tech effect possible.
;
; Right now, for the 'logo', we simply copy the BASIC screen data
; from $0400
ldx #0
-
lda $0400,x
sta $4800,x
sta $4c00 + 1,x
sta $5000 + 2,x
sta $5400 + 3,x
sta $5800 + 4,x
sta $5c00 + 5,x
sta $6000 + 6,x
sta $6400 + 7,x
sta $6800 + 8,x
sta $6c00 + 9,x
sta $7000 + 10,x
sta $7400 + 11,x
sta $7800 + 12,x
sta $7c00 + 13,x
lda $0500,x
sta $4900,x
sta $4d00 + 1,x
sta $5100 + 2,x
sta $5500 + 3,x
sta $5900 + 4,x
sta $5d00 + 5,x
sta $6100 + 6,x
sta $6500 + 7,x
sta $6900 + 8,x
sta $6d00 + 9,x
sta $7100 + 10,x
sta $7500 + 11,x
sta $7900 + 12,x
sta $7d00 + 13,x
inx
bne -
; make last tech-tech line use invalid $d011 mode to mask bug
lda #((TECHTECH_HEIGHT - 1) & 7 | $78)
sta tt_unrolled + ((TECHTECH_HEIGHT - 1) * TECHTECH_MACRO_LEN) + 1
; set cover sprite pointers for each videoram bank used
lda #$f8
ldx #$4b
sta zp
stx zp + 1
ldx #0
-
lda #(COVER_SPRITE & $3fff) / 64
ldy #0
sta (zp),y
iny
sta (zp),y
lda zp + 1
clc
adc #4
sta zp + 1
inx
cpx #14
bne -
jsr tt_sinus_precalc
lda #$37
sta $01
rts
; Precalcute sinus data
;
tt_sinus_precalc
ldx #0
ldy #0
- lda sinus,y
and #7
sta sinus_d016,x
sta sinus_d016 + 256,x
lda sinus,y ; divide by 8, multiply by 16 to get videoram
; index
asl
and #$f0
adc #$20 ; C is clear
sta sinus_d018,x
sta sinus_d018 + 256,x
tya
clc
adc #1
tay
inx
bne -
rts
.cerror * > $0fff, "code too long"
; Link music
* = $1000
.binary music_sid, $7e
; A single rasterline of FLI-code to display the tech-tech
;
; @param 1: row number (used to calculate the correct $d011 value)
;
; We trigger a badline condition at each rasterline to trigger a videoram
; update which we use to alter the videoram bank
;
ttmacro .macro
lda #$18 + ((\1 + 3) & 7)
sta $d011
lda #$20 ; gets updated in the sinus routine
sta $d018
lda #$08 ; gets updated in the sinus routine
sta $d016
.endm
* = $2000
; The tech-tech display routine: a simple unrolled FLI routine which also sets
; $d016 at each line
tt_unrolled
.for row = 0, row < TECHTECH_HEIGHT, row = row + 1
#ttmacro row
.next
rts
.align 256
; Unrolled sinus updating routine
;
; Uses two tables of 512 bytes each, to allow for X index overflow (we add
; an offset to each sinus_d016 and sinus_d018 table for each row, combine that
; with X register indexing and we would go past the 256-byte mark in a table)
tt_sinus_unrolled
_index ldx #0
.for row = 0, row < TECHTECH_HEIGHT, row = row + 1
lda sinus_d018 + row,x
sta tt_unrolled + (row * TECHTECH_MACRO_LEN) + 6
lda sinus_d016 + row,x
sta tt_unrolled + (row * TECHTECH_MACRO_LEN) + 11
.next
lda _index + 1
clc
adc #2
sta _index + 1
rts
; Sinus used for the tech-tech effect: 112 pixels wide since we use 14
; videoram banks
.align 256
sinus
.byte 55.5 + 56 * cos(range(256) * rad(360.0/256))
; Precalculated $d016 values
sinus_d016
.fill 512, 0
; Precalculated $d018 values
sinus_d018
.fill 512,0