BFLI - New graphics modes 2
by Pasi 'Albert' Ojala <albert@cs.tut.fi>

One day I was watching some demos that used linecrunch routines for
whole-screen multicolor-graphics upscrollers.  I already had my
theories about how and why linecrunch worked, but because I had not
used it anywhere, the details were a bit vague.  In fact, I have
many times accidentally created linecrunch effects when trying to do
something else with $D011.  Probably every demo coder has.

But you learn by doing.  I had the idea of using linecrunch for FLI
instead of a simple multicolor picture as it always seemed to be
used.  However, this has probably been done before and because I
don't like to do things that have been done before, I decided to use
linecrunch to show a two-screen-tall FLI picture.


_Linecrunch Basics_

For those not familiar with linecrunch routines:  linecrunch is used
to scroll the screen UPWARDS by convincing VIC-II that it has
already showed more character rows than it in reality has shown.
Surprisingly (or then, maybe not :) this consists of fiddling with
$D011.  The timing is critical as always.

Linecrunch works by setting $D011 equal the line before the current
line and VIC-II will happily think that it is time to move on to the
next character row - add 40 to the video matrix counter, 320 to the
graphics memory counter and be ready to start a bad line.  Or, maybe
'NOT to go back to the current row' would be a more suitable
description.  (Programming VIC-II is slowly becoming a science.)

The required timing also does not cause bad lines so that you can
skip another line immediately on the successive line.  In addition,
lines can be skipped only after the first character row and half of
the second character row have been displayed.  This has something to
do with the way VIC-II decides when there is a bad line.

Because linecrunch causes VIC-II to skip rows, it will run out of
video matrix and color memory (and graphics memory) before reaching
the end of the screen.  However, VIC-II does not stop displaying the
graphics nor does it reset the internal counters.  The counters keep
on running and wrap around instead.

Normally, when VIC-II is displaying the last character row, it is
showing the memory from offsets $3c0 to $3e7.  If VIC-II has skipped
one character row, it is displaying from $3e8 to $40f instead.  But,
there are only 10 bits for the video matrix counter (0..1023), so it
wraps around to zero after $3ff.  This means that the beginning of
the video matrix is displayed at the bottom of the screen.  The
character rows become shifted by 24 character positions to the right
because there were originally 24 unused memory locations at the end
of the memory (1000..1023).  (To be honest, sprite image pointers
are not unused memory, but they are not used with normal FLI.)

         ____________________    ____________________
        |abcdefghijklmnopqrst|  |abcdefghijklmnopqrst|
        |                    |  |--------------------| <- Skipped row
        :                    :  :                    :
        :                    :  :                    :
        :                    :  :                    :
        |                    |  |normally last line  |
        |normally last line  |  |XXXXXXXXZZZZabcdefgh|
        `--------------------'  `--------------------'
                                X = unused mem      (1000..1015)
                                Z = sprite pointers (1016..1023)

        Figure 1: Linecrunch


The same thing happens for color memory because it uses the same
counter for addressing the memory (in fact, color memory access and
character data access are performed simultaneosly, 12 bits at a
time).  The graphics memory behaves the same way, except that the
counter has three bits more and it counts at eight times the speed,
so that it wraps at the exact same time as the other counter.

The first character row can't be used for linecrunch and the second
one is also lost in the process.  The first usable line to display
is the third character row.  However, those two lost rows can still
be used as an extension at the end of the first screen.  You must
notice, however, that the alignment has been changed.  After these
two rows have been displayed, the video bank is switched to get new
fresh data on the screen.


_Back to BFLI_

Wrapped data is nothing difficult to work with.  It is just the
matter of writing the right conversion program.  Also, the normal
FLI routine can be used, we just have to make sure VIC always has
the right bank visible - simple LDA bank,x:sta $DD00 can accomplish
that.  The more difficult aspect is to make the display freely
locatable.  We have 32 kilobytes of graphics data, this is the main
reason we can't even think about using copying.  Linecrunch combined
with the bad line delaying technique will do the job much more
nicely.

Figure 2 shows the principles.  To make things simpler I have chosen
location 0 to mean that the top of the picture is visible, 1 means
that the picture is scrolled one line upwards and so on.  We can see
that linecrunch is not used at all for the location 0.  To make the
picture start at the same point whether linecrunch has crunched
lines or not we compensate the non-lost raster lines by delaying the
next bad line.  When the location is n*8 (n=0,1,2..), the sum of the
linecrunched and delayed lines is constant - the graphics display
always starts at the same point.

Then how do we deal with the location values that are not evenly
dividable by eight ?  Now, lets assume that the location is L, and
we have C, which is the location divided by eight (C = L/8), and R,
which is the remainder (R = L%8).  To make the picture scroll to the
right position, we need to delay the bad line less than before - R
lines less for location L than for location C*8.  E.g.  for location
2 we delay the bad line two lines less than for location 0.  This
also shows that we need 7 lines more than is needed for to
compensate for the linecrunch.

Determining the number of linecrunch lines is a recursive process,
because when you use more linecrunch lines, that decreases the
number of lines you have available for the display and you need
bigger range for the location value.  The linecrunch can be started
after 12 lines, and we need at least 7 lines to use the soft
y-scroll.  This makes 181 lines available for the display
originally.

Because we need to show 400 lines of graphics, we would need
(400-181)/8=28 linecrunch lines.  However, this in turn reduces the
number of lines we have for graphics to 181-28=153 and we need
(400-153)/8=31 linecrunch lines.  Again, 181-31 is 150.  We get
(400-150)/8=32 and there it finally converges and we have 149 lines
for graphics, which makes location values 0..251 valid.


Location        0       1       2  ..   8       9  ..   251

                ___________________..   ___________..   ________
                ___________________..   ___________..   ________
Linecrunch      -------------------..   ___________..
                ^       ^       ^
                |       |       |       ^       ^
                |       |       |       |       |
Bad line delayed|       |       |       |       |
                |       |       |       |       |       ========
                |       |       v       |       |       244
                |       v       ___..   |       v       :
                v       ________0       v       ___..   :
Gfx Enabled     ________0_______1__..   ________8__..   250_____
                0       1       2       8       9       251
                1       2       3       9       10      252
                2       3       4       10      11      253
                3       4       5       11      12      254
                4       5       6       12      13      255
                5       6       7       13      14      256
                6       7       8       14      15      257
                7       8       9       15      16      258
                :       :       :       :       :       :
                :       :       :       :       :       :
                148     149     150..   156     157..   399

        Figure 2: Linecrunch and DMA delay in BFLI
                  (Graphics lines not in scale)


_Clipping added_

Now we can scroll the picture to any location we want, but the top
of the picture is not clipped and it is very annoying to watch.  We
need to enable the graphics at the same point regardless of the
y-scroll value.  The answer is in the extended color mode (ECM).

When both ECM and multicolor mode (MCM) are selected, VIC-II will
turn the display to black.  This is because there is a conflicting
situation and it just can't decide which color scheme to use.  The
video accesses will continue to happen just like before, the data is
just not displayed.  When the ECM bit is cleared again, the normal
multicolor graphics is shown.

So, we set the ECM bit and start to display the first eight lines of
the FLI.  Because the FLI routine already writes to $D011, we just
make sure the ECM bit is set in the first R number of writes to
$D011 and zero in all other.

The viewer is now 'complete'.  You can take a look at the code below
or you can get C64Gfx1_4.lha and see it in action yourself and not
just rely on my word.  The package includes converter programs for
BFLI, FLI and Koala (ANSI-C), couple of example pictures and viewers
for PAL and NTSC machines.

-Pasi 'Albert' Ojala    albert@cs.tut.fi

--------------------------------------------------------------------------

BFLI viewer program for PAL machines

UPOS   = $C00   ; temporary area for tables
BANK   = $D00   ;  UPOS for linecrunch, BANK for FLI bank select
RASTER = 29     ; where to position the sprite -> IRQ 20 lines later
DUMMY  = $FFF   ; dummy location for timing purposes
FLISZ  = 19-1   ; visible FLI size in character rows - 1

*= $810
        SEI
        LDA #$7F:STA $DC0D      ; IRQ setup
        LDA #1:STA $D01A
        STA $D015:STA KEYW+1
        LDA #<IRQ:STA $314
        LDA #>IRQ:STA $315
        LDA #RASTER:STA $D001:CLC:ADC #20:STA $D012
        LDA #0:STA $D017
        LDA #0:STA 2
        JSR NEWPOS              ; Init the FLI routines
        LDA #$A:STA $D011       ; Blank screen
        LDX #23                 ; Init tables
BLOOP   LDA #$94:STA BANK,X
        LDA #$96:STA BANK+24,X
        DEX:BPL BLOOP
        LDX #15
LOOP0   LDA YINIT,X:AND #$77    ; Change to $37 to better see the
        STA UPOS,X              ;  workings of the routines
        STA UPOS+16,X
        STA UPOS+32,X
        DEX:BPL LOOP0

        LDA #$34:STA 1          ; Copy to the last video bank
        LDA #$80:STA SRC+2      ; from $8000-$BFFF to $C000-$FFFF
        LDA #$C0:STA DST+2
        LDX #0:LDY #$40
SRC     LDA $8000,X
DST     STA $C000,X
        INX:BNE SRC
        INC SRC+2:INC DST+2
        DEY:BNE SRC
        LDA #$37:STA 1

        LDX #0                  ; Init color memory
LP      LDA $3C00,X:STA $D800,X ; All 1024 bytes are used
        LDA $3D00,X:STA $D900,X ;  - some even twice!
        LDA $3E00,X:STA $DA00,X
        LDA $3F00,X:STA $DB00,X
        INX:BNE LP
        LDA $DC0D:CLI

KEYW    LDX #0:BNE KEYW         ; Wait for space to be pressed
        SEI                     ; System to normal
        LDA #$37:STA 1
        JSR $FDA3
        LDA #$97:STA $DD00
        JSR $E5A0
        LDY #3
IRQL    LDA $FD30,Y:STA $314,Y
        DEY:BPL IRQL

        LDX #0:LDA #1           ; Clear color memory
CLL     STA $D800,X:STA $D900,X
        STA $DA00,X:STA $DB00,X
        INX:BNE CLL
        CLI:RTS

YINIT   BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F
        BYT $78,$79,$7A,$7B,$7C,$7D,$7E,$7F

*=*-<*+256

IRQ     LDA #$18:STA $D016:LDX #0:LDA #$5A
        INC DUMMY:DEC DUMMY             ; Synchronization
        STX $D020:STX $D021:STA $D011

        LDA #$15:STA $D018
        LDA #$97:STA $DD00
        LDX #44                 ; Wait for the 4th line
LL      DEX:BPL LL:NOP
        LDX #0

LOOP3   NOP                     ; Linecrunch-part routine
        LDA UPOS+6,X:INC DUMMY:STA $D011
        NOP:NOP:INC DUMMY
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        INX
E1      CPX #$10:BNE LOOP3      ; Skip that many character rows-4
        BIT $EA
LOOP4   LDA UPOS,X:INC DUMMY:STA $D011
        NOP:NOP:NOP:INC DUMMY
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP:LDA #0
        INX
E2      CPX #$1F:BNE LOOP4      ; Delay DMA until we are at the
                                ;  'same place' each time

        LDA #0:STA $D020        ; Now wait for the bad line and start FLI
        BIT $EA:NOP
        NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP
        NOP:NOP:NOP:NOP
B0      LDA #$92:STA $DD00:NOP  ; The right video bank

        ; Wait for 0-7 lines to set the ECM mode off
        ;  (makes the graphics visible)

F0      LDA #0:STA $D011:LDA #$08:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F1      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F2      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F3      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F4      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F5      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F6      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
F7      LDA #0:STA $D011:LDA #$78:STA $D018
        LDX #FLISZ:NOP:NOP:NOP:BIT $EA

        ; Do FLI 18 more character rows

F8      LDA #0:STA $D011:LDA #$08:STA $D018
B1      LDA BANK,X:STA $DD00:BIT $EA
F9      LDA #0:STA $D011:LDA #$18:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FA      LDA #0:STA $D011:LDA #$28:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FB      LDA #0:STA $D011:LDA #$38:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FC      LDA #0:STA $D011:LDA #$48:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FD      LDA #0:STA $D011:LDA #$58:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FE      LDA #0:STA $D011:LDA #$68:STA $D018:NOP:NOP:NOP:NOP:BIT $EA
FF      LDA #0:STA $D011:LDA #$78:STA $D018:NOP:NOP:DEX:BMI EFLI:JMP F8
EFLI    NOP
        LDA #$FC
WL      CMP $D012:BNE WL
        INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
        INC DUMMY:INC DUMMY:INC DUMMY:INC DUMMY
        INC DUMMY:INC DUMMY:STA $D020

        JSR NEWPOS      ; Update the location
        JSR CHPOS       ; Change to a new location
        LDA $DC01:AND #$10:BNE OV3      ; Check for the space bar
        LDA #0:STA KEYW+1
OV3     LDX #$53:STX $D011:INC $D019:JMP $EA81

NEWPOS  LDA #0          ; Init the IRQ routine for this position
        LSR:LSR:LSR:CLC:ADC #4:STA E1+1
        LDA #7:SEC:SBC NEWPOS+1:AND #7:TAX:TAY:CLC:ADC #35:STA E2+1
        LDA UPOS+3+7,Y:DEX:BMI J0:AND #$3F
J0      STA F7+1:AND #$3F:STA FF+1
        LDA UPOS+3+6,Y:DEX:BMI J1:AND #$3F
J1      STA F6+1:AND #$3F:STA FE+1
        LDA UPOS+3+5,Y:DEX:BMI J2:AND #$3F
J2      STA F5+1:AND #$3F:STA FD+1
        LDA UPOS+3+4,Y:DEX:BMI J3:AND #$3F
J3      STA F4+1:AND #$3F:STA FC+1
        LDA UPOS+3+3,Y:DEX:BMI J4:AND #$3F
J4      STA F3+1:AND #$3F:STA FB+1
        LDA UPOS+3+2,Y:DEX:BMI J5:AND #$3F
J5      STA F2+1:AND #$3F:STA FA+1
        LDA UPOS+3+1,Y:DEX:BMI J6:AND #$3F
J6      STA F1+1:AND #$3F:STA F9+1
        LDA UPOS+3+0,Y:DEX:BMI J7:AND #$3F
J7      STA F0+1:AND #$3F:STA F8+1
        LDA #$96:STA B0+1:LDA #199:SEC:SBC NEWPOS+1:BCC OV2
        LSR:LSR:LSR:CLC:ADC #5:STA B1+1
        RTS
OV2     LDA #0:STA B1+1:LDX #$94:STX B0+1:RTS

CHPOS   LDX NEWPOS+1
        LDA $DC00:TAY           ; Get joystick
        AND #$10:BNE DIR        ; If no button pressed
        TYA:AND #1:BEQ UP       ; If joy up
        TYA:AND #2:BEQ DOWN     ; If joy down
        RTS
DIR     LDA #0:BEQ UP
DOWN    DEX:CPX #$FF:BNE DOK
        LDX #0:STX DIR+1        ; Change direction
DOK     STX NEWPOS+1:RTS
UP      INX:CPX #$FD:BCC UOK    ; 251(locations)+149(visible)=400
        LDX #$FC:STX DIR+1      ; Change direction
UOK     STX NEWPOS+1:RTS


--------------------------------------------------------------------------

The BFLI file format:

                        File            BFLI Display
        Lines           Offset          Offset          Lines     Size
Colors  0-1.3           0..55           944..999        22.7-24   56
  I     1.3-2           56..79          -                         24
        2-24            80..999         0..919          0-22      920
        24-24.7         1000..1023      920..943        22-22.7   24

  II    0-1.3           0..55           1968..2024      49.3-50.6 56
        1.3-24.7        56..1023        1000..1967      24-49.3   968


Gfx     0-1.3           0..447          7552..7999      22.7-24   448
  I     1.3-2           448..639        -                         192
        2-24            640..7999       0..7359         0-22      7360
        24-24.7         8000..8191      7360..7551      22-22.7   192

  II    0-1.3           0..447          15744..16192    49.3-50.6 448
        1.3-24.7        448..8191       8000..15743     24-49.3   7744

