[ The Demo Corner is going to be a column where each month we'll be
  introduced to a new feature (some people call them bugs, we'll call them
  features) of the Commodore 64 or 128 in the Video and Sound areas that
  have commonly been shown on demos but with no mention of how to accomplish
  them. Note that readers may also want to take a look at the introduction
  to Rasters elsewhere in this magazine.]

The Demo Corner: Missing Cycles
by Pasi 'Albert' Ojala   (po87553@cs.tut.fi albert@cc.tut.fi)
                          Written on  15-May-91  Translation 30-May-92


                          Missing Cycles
                          --------------
       [all timings are in PAL, the principle applies to NTSC too]

Everybody knows that there are 63 cycles available to the C64 processor on
each scan line, except for one which only provides 23 cycles (later referred
to as a "bad" scan line). But what happens when we add sprites and why ?


In the C64, the VIC (video interface controller) has much more to do than
just showing graphics on the screen. It also handles the memory refresh.
On each scanline, it has to refresh five rows in the memory matrix and
fetch fourty bytes of graphics data.

The VIC does all of this during the cycles (phase 1) that the processor is
not using the memory.  These cycles, however, are not sufficient when the
VIC also needs to access the character and color codes for the next row.
The memory bus can't be used by the CPU and the VIC at the same time, so CPU
access to the bus must be denied to allow the VIC to fetch its data.
Fortunately, the VIC bus (12-bit wide) allows the character (8 bits) and
color (4 bits) codes to be fetched at the same time.


_Understanding how sprites work_

If there are sprites on the screen, the VIC needs even more cycles to fetch
all of the graphics data. Scan lines are time divided so that there is
enough time for all actions during one line. On each line, the sprite
image pointers are fetched during phase 1. If the sprite is to be displayed
on that line, the three bytes of image data are fetched right after that.
Out of these three fetches, two take place during phase 2 of the clock,
so the processor will lose these. On average, two clock cycles are lost
for each sprite that is displayed on that line.

But how is it possible for all eight sprites to only take 16-19 cycles
(depending on the timing) when we have observed that one sprite requires
three cycles? And why do sprites 0, 2, 4, 6 and 7 together take up as many
cycles as all eight sprites ? The answer may be found in the way the VIC
tells the CPU that it needs additional cycles.


_The BA signal_

When the VIC wants to use the bus, the BA (Bus Available) signal goes
inactive. This will happen three cycles before the bus must be released !
During these three cycles, the CPU must complete all memory accesses or
delay them until it has the bus again.

The CPU either completes the current instruction in the remaining cycles
or sits and waits for the bus to become available again. It can't execute
a new instruction as long as it doesn't have the bus. This is why cycles
seem to be lost (besides those stolen directly for the sprites). Usually,
all 8 sprites take 17 cycles while one sprite takes three cycles. However,
the CPU may continue to execute an instruction if it does not use the bus.


_Theory and speculation_

Let's suppose that all the sprites are enabled and on the same scan line.
Then, the VIC steals 16 cycles (2 cycles for each sprite) for the memory
fetches and 3 cycles as overhead for the BA signal, for a total of 19 cycles.
However, it will be usually less because the CPU will use some of the cycles
when the bus request is pending.

If we now disable sprite 4, no cycles are released for the CPU's use. This
is because during the previous sprite 4 data fetch, the VIC already signals
that it needs the bus for the sprite 5 data fetch and BA stays low (Refer
to the timing chart). Thus, the CPU never sees BA go high during sprite 4
and 2 cycles are still lost.

Accordingly, if we only turn off sprites 1, 3 and 5 we get no cycles back
from the VIC. So in time-critical raster routines, always use sprites in
order.


_What can we do with this feature ?_

A good use is for synchronization. Normally before the CPU starts to execute
the raster interrupt code, it's executing an instruction of undefined
cycle-length. This execution time varies from two to seven cycles.

With a sprite, you can do the synchronization with a minimal effort using
a DEC or INC instruction in the right place. If the processor is early,
it has to wait for the bus, otherwise it will continue to execute cycles
from the instruction.

I have never experimented with any other instruction than DEC/INC, but
some others should work also. You need an instruction which has a cycle that
do not need the bus to be available. e.g. INC $3fff will increase the
value during the fifth cycle and do not need the bus for that.


_A demo program_

The enclosed program includes a short raster color routine to demonstrate
this strict timing and synchronization. The background color is changed
12 times on each line. The electron beam runs over eight pixels during
one cycle, so the timing must be precise.

--------------------------------------------------------------------------
_Table for PAL VIC timing for the Missing cycles_


012345678901234567890123456789012345678901234567890123456789012 cycles

Normal scan line, 0 sprites
ggggggggggggggggggggggggggggggggggggggggrrrrr  p p p p p p p p  phi-1 VIC
                                                                phi-2 VIC
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx phi-2 6510
63 cycles available

Normal scan line, 8 sprites
ggggggggggggggggggggggggggggggggggggggggrrrrr  pspspspspspspsps phi-1 VIC
                                               ssssssssssssssss phi-2 VIC
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXX                 phi-2 6510
46-49 cycles available

Normal scan line, 4 sprites
ggggggggggggggggggggggggggggggggggggggggrrrrr  psp psp psp psp  phi-1 VIC
                                               ss  ss  ss  ss   phi-2 VIC
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXX              xx phi-2 6510
48-51 cycles available

Bad scan line, 0 sprites
ggggggggggggggggggggggggggggggggggggggggrrrrr  p p p p p p p p  phi-1 VIC
cccccccccccccccccccccccccccccccccccccccc                        phi-2 VIC
                                        xxxxxxxxxxxxxxxxxxxxxxx phi-2 6510
23 cycles available

Bad scan line, 8 sprites
ggggggggggggggggggggggggggggggggggggggggrrrrr  pspspspspspspsps phi-1 VIC
cccccccccccccccccccccccccccccccccccccccc       ssssssssssssssss phi-2 VIC
                                        xxxxXXX                 phi-2 6510
4-7 cycles available


g= grafix data fetch (character images or graphics data)
r= refresh
p= sprite image pointer fetch
c= character and color CODE fetch during a bad scan line
s= sprite data fetch
x= processor executing instructions
X= processor executing an instruction, bus request pending

Observe! The left edge of the chart is not the left edge of the screen nor
         the left edge of the beam, but the sprite x-coordinate 0. If you
         have opened the borders, you know what I mean. A sprite can be
         moved left from the coordinate 0 by using x-values greater than 500.
 ___________
|  _______  |<-- Maximum sized video screen
|||       | |
|||       |<-- Normal C64 screen
|||       | |
|||_______| |
||          |
||__________|
 ^ Sprite coordinate 0

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

Demonstration program for missing cycles


COLOR0= $CE00  ; Place for color bar 0
COLOR1= $CF00  ; Place for color bar 1
RASTER= $FA    ; Line for the raster interrupt
DUMMY= $CFFF   ; Timing variable

*= $C000
        SEI             ; Disable interrupts
        LDA #$7F        ; Disable timer interrupts
        STA $DC0D
        LDA #$01        ; Enable raster interrupts
        STA $D01A
        STA $D015       ; Enable Sprite 0
        LDA #<IRQ       ; Init interrupt vector
        STA $0314
        LDA #>IRQ
        STA $0315
        LDA #$1B
        STA $D011
        LDA #RASTER     ; Set interrupt position (inc. 9th bit)
        STA $D012
        LDA #RASTER-20  ; Sprite will just reach the interrupt position
        STA $D001       ;  when it is positioned 20 lines earlier

        LDX #51
        LDY #0
        STA $D017       ; No Y-enlargement
LOOP0   LDA COL,X       ; Create color bars
        PHA
        AND #15
        STA COLOR0,X
        STA COLOR0+52,Y
        STA COLOR0+104,X
        STA COLOR0+156,Y
        PLA
        LSR
        LSR
        LSR
        LSR
        STA COLOR1,X
        STA COLOR1+52,Y
        STA COLOR1+104,X
        STA COLOR1+156,Y
        INY
        DEX
        BPL LOOP0
        CLI             ; Enable interrupts
        RTS             ; Return


IRQ     NOP             ; Wait a bit
        NOP
        NOP
        NOP
        LDY #103        ; 104 lines of colors (some of them not visible)
                        ; Reduce for NTSC, 55 ?
        INC DUMMY       ; Handles the synchronization with the help of the
        DEC DUMMY       ;  sprite and the 6-clock instructions
                        ; Add a NOP for NTSC

FIRST   LDX COLOR0,Y    ; Do the color effects
SECOND  LDA COLOR1,Y
        STA $D020
        STX $D020
        STA $D020
        STX $D020
        STA $D020
        STX $D020
        STA $D020
        STX $D020
        STA $D020
        STX $D020
        STA $D020
        STX $D020
                        ; Add a NOP for NTSC (one line = 65 cycles)
        LDA #0          ; Throw away 2 cycles (total loop = 63 cycles)
        DEY
        BPL FIRST       ; Loop for 104 lines

        STA $D020
        LDA #103        ; For subtraction
        DEC FIRST+1     ; Move the bars
        BPL OVER
        STA FIRST+1
OVER    SEC
        SBC FIRST+1
        STA SECOND+1

        LDA #1          ; Ack the raster interrupt
        STA $D019
        JMP $EA31       ; Jump to the standard irq handler

COL     BYT $09,$90,$09,$9B,$00,$99,$2B,$08,$90,$29,$8B,$08,$9C,$20,$89,$AB
        BYT $08,$9C,$2F,$80,$A9,$FB,$08,$9C,$2F,$87,$A0,$F9,$7B,$18,$0C,$6F
        BYT $07,$61,$40,$09,$6B,$48,$EC,$0F,$67,$41,$E1,$30,$09,$6B,$48,$EC
        BYT $3F,$77,$11,$11
                        ; Two color bars

--------------------------------------------------------------------------
Basic loader for Missing cycles example program (PAL)

1 S=49152
2 DEFFNH(C)=C-48+7*(C>64)
3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<clr>":SYS49152:END
4 FORF=0TO31:Q=FNH(ASC(MID$(A$,F*2+1)))*16+FNH(ASC(MID$(A$,F*2+2)))
5 CH=CH+Q:POKES,Q:S=S+1:NEXT:IFCH=ATHEN3
6 PRINT"CHECKSUM ERROR":END
100 DATA 78A97F8D0DDCA9018D1AD08D15D0A9578D1403A9C08D1503A91B8D11D0A9FA8D, 3773
101 DATA 12D0A9E68D01D0A233A0008D17D0BDACC048290F9D00CE9934CE9D68CE999CCE, 4157
102 DATA 684A4A4A4A9D00CF9934CF9D68CF999CCFC8CA10D95860EAEAEAEAA067EEFFCF, 4878
103 DATA CEFFCFBE18CEB94FCF8D20D08E20D08D20D08E20D08D20D08E20D08D20D08E20, 4403
104 DATA D08D20D08E20D08D20D08E20D0A9008810D18D20D0A967CE64C010038D64C038, 3923
105 DATA ED64C08D67C0EE19D04C31EA0990099B00992B0890298B089C2089AB089C2F80, 3483
106 DATA A9FB089C2F87A0F97B180C6F076140096B48EC0F6741E130096B48EC3F771111, 3133
200 DATA END,0

--------------------------------------------------------------------------
Uuencoded C64 executable version (PAL)

begin 644 missing.64
M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
M2U-532!%4E)/4B(Z@``."60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0Q040P.$0QK
M-40P03DU-SA$,30P,T$Y0S`X1#$U,#-!.3%".$0Q,40P03E&03A$+"`S-S<SA
M`%L)90"#(#$R1#!!.44V.$0P,40P03(S,T$P,#`X1#$W1#!"1$%#0S`T.#(Y?
M,$8Y1#`P0T4Y.3,T0T4Y1#8X0T4Y.3E#0T4L(#0Q-3<`J`EF`(,@-C@T031!4
M-$$T03E$,#!#1CDY,S1#1CE$-CA#1CDY.4-#1D,X0T$Q,$0Y-3@V,$5!14%%>
M045!03`V-T5%1D9#1BP@-#@W.`#U"6<`@R!#149&0T9"13$X0T5".31&0T8X^
M1#(P1#`X13(P1#`X1#(P1#`X13(P1#`X1#(P1#`X13(P1#`X1#(P1#`X13(PH
M+"`T-#`S`$(*:`"#($0P.$0R,$0P.$4R,$0P.$0R,$0P.$4R,$0P03DP,#@XV
M,3!$,3A$,C!$,$$Y-C=#138T0S`Q,#`S.$0V-$,P,S@L(#,Y,C,`CPII`(,@^
M140V-$,P.$0V-T,P144Q.40P-$,S,45!,#DY,#`Y.4(P,#DY,D(P.#DP,CDX[
M0C`X.4,R,#@Y04(P.#E#,D8X,"P@,S0X,P#<"FH`@R!!.49",#@Y0S)&.#=!?
M,$8Y-T(Q.#!#-D8P-S8Q-#`P.39"-#A%0S!&-C<T,44Q,S`P.39"-#A%0S-&V
;-S<Q,3$Q+"`S,3,S`.@*R`"#($5.1"PP````8
``
end
size 747

--------------------------------------------------------------------------
Uuencoded C64 executable version (NTSC)

begin 644 missing.64
M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`40@#`$-(?
MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(I,B.IXT.3$U,CJ``(@(!`"!1K(P/
MI#,Q.E&RI4@HQBC**$$D+$:L,JHQ*2DIK#$VJJ5(*,8HRBA!)"Q&K#*J,BDI:
M*0"I"`4`0TBR0TBJ43J74RQ1.E.R4ZHQ.H(ZBT-(LD&G,P#!"`8`F2)#2$5#F
M2U-532!%4E)/4B(Z@``."60`@R`W.$$Y-T8X1#!$1$-!.3`Q.$0Q040P.$0QK
M-40P03DU-SA$,30P,T$Y0S`X1#$U,#-!.3%".$0Q,40P03E&03A$+"`S-S<SA
M`%L)90"#(#$R1#!!.44V.$0P,40P03(S,T$P,#`X1#$W1#!"1$%%0S`T.#(YA
M,$8Y1#`P0T4Y.3,T0T4Y1#8X0T4Y.3E#0T4L(#0Q-3D`J`EF`(,@-C@T031!6
M-$$T03E$,#!#1CDY,S1#1CE$-CA#1CDY.4-#1D,X0T$Q,$0Y-3@V,$5!14%%>
M045!03`S-T5%1D9#1BP@-#@S,`#U"6<`@R!#149&0T9%04)%,#!#14(Y,#!#4
M1CA$,C!$,#A%,C!$,#A$,C!$,#A%,C!$,#A$,C!$,#A%,C!$,#A$,C!$,#A%$
M+"`T-3`R`$(*:`"#(#(P1#`X1#(P1#`X13(P1#`X1#(P1#`X13(P1#!%04$Y.
M,#`X.#$P1#`X1#(P1#!!.38W0T4V-4,P,3`P,SA$-C4L(#,Y-#(`CPII`(,@R
M0S`S.$5$-C5#,#A$-CA#,$5%,3E$,#1#,S%%03`Y.3`P.3E",#`Y.3)",#@Y(
M,#(Y.$(P.#E#,C`X.4%",#@Y0RP@,S4U.`#<"FH`@R`R1C@P03E&0C`X.4,R_
M1C@W03!&.3=",3@P0S9&,#<V,30P,#DV0C0X14,P1C8W-#%%,3,P,#DV0C0XK
M14,S1C<W+"`S,C<T`"<+:P"#(#$Q,3$P,#`P,#`P,#`P,#`P,#`P,#`P,#`PO
M,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`L(#,T`#,+1
,;`"#($5.1"PP````"
``
end
size 822
