Tech-tech - more resolution to vertical shift.
by Pasi 'Albert' Ojala (po87553@cs.tut.fi _or_ albert@cc.tut.fi)
Written on 16-May-91  Translation 02-Jun-92

(All timings are in PAL, principles will apply to NTSC too)

One time half of the demos had pictures waving horizontally on the
width of the whole screen. This effect is named tech-tech, and the
audience was puzzled. You can move the screen only eight pixels using
the horizontal scroll register. This effect was done using character
graphics. How exactly and is the same possible with sprites ?


Horizontal scroll register can move the screen by eight pixels. This
isn't even nearly enough to produce a really stunning effect. You have
to move the graphics itself, fortunately with a resolution of one
character position (one byte) only, the rest can be done with the scroll
register. During one scan line there is no time to move the actual data,
you can only move a pointer. Changing the video matrix pointer won't
help, because VIC (video interface controller) will fetch the character
codes only at certain times, called bad lines. You can change the
character set pointer instead, because VIC reads the data it displays
directly from the character set memory.


Character set-implementation has its restrictions

Because horizontal movement is done by changing the character sets, the
picture or text must be pure graphic and the character codes in the
video matrix must be in a numerical order. The normal picture is in the
first character memory and in the next one it is shifted one character
position to the right. One video bank can hold only seven full character
memories besides the video matrix. This limits the movement of the
picture to 56 pixels. It is possible to get more movement if you use
smaller picture or another video bank.

The shift is done so that on each scan line we update the horizontal
scroll register ($D016) with the three lowest bits of the shift value.
We use the other bits to select the right character set ($D018). In a
tech-tech the shift value changes during the display of the whole
picture, and the values are stored in a table. In addition to that, the
shift values should be put into two tables, one for the horizontal
scroll register and another for the character set select. This is
necessary, because there is no time for extra calculations on a bad
line.

Because we have to change the character set and x-scroll dynamically, we
also need a raster routine to show a tech-tech. A raster routine is a
routine which is synchronized to the electron beam. This eats up the
processor time: the bigger the picture, the less time is left over for
other activities. On other than bad lines you can do other funny things,
like change the color of the background or border.


An example program

The demo program uses video bank 2, memory addesses $4000-7fff. The
video matrix is in the beginning of the bank. Only inverted chars are
used for the graphics, this way we have all eight character memories
available and the maximum shift is 64 pixels. The area for the tech-tech
in the video matrix is eight character rows high, but it has identical
graphics on every line. This is why we use only 320 bytes from each
character set.

You can use a joystick to control the movement of the tech-tech. The
stick decreases or increases the shift add value in a resolution of a
half pixel. When the shift reaches its highest/lowest value, the
direction of the add is reversed. Just experiment with it.


You can do it on the screen, how about borders ?

Because you cannot get characters to the border, you might think that it
is impossible to make a tech-tech effect in the borders. It takes so
much time to change sprite x-coordinates, that you can change only some
of them. There is time for five sprite moves, if you do not need to
change the most significant (9th) bit of the x-coordinate. On the other
hand, you could design the movements directly to the sprites and then
just change the images, but then the movements would be constant.

However, there is one trick you can use to get all of the sprites on the
screen, with variable movements and color bars etc. You do not change
the x-coordinates, but the data itself on each scan line. In fact you
change the sprite image pointers. There is multiple sprite pictures,
where the graphics is shifted horizontally, just like the normal
tech-tech charsets. Because of this, the sprites have to be placed side
by side. No gaps are allowed. By changing the image pointers you can get
the graphics to move horizontally on each line as you wish. With
multicolor sprites you have to remember that one pixel corresponds to
two bits - the movement is not so smooth.

Wait ! How come there is enough time to change the sprite pointers, when
there is not time to change the coordinates ? This is another pointer
trick. VIC reads the sprite image pointers from the end of the current
video matrix, normally $07f8. You just have to change the video matrix
pointer ($D018) to change all of the image pointers. This takes only
eight cycles and there is plenty of time left for other effects on each
scan line. If you use only one video bank, you can get the sprite
picture to 16 different places. This allows also another kind of
effects, just use your imagination.

--------------------------------------------------------------------------
Tech-tech demo program (PAL)

BANK=   $96     ; The value of the video bank register (CIA2) in the tech-area
ZP=     $FB     ; Zero page for indirect addressing
START=  $4400   ; Start of the charsets (we use inverted chars)
SCREEN= $4000   ; Position of the video matrix
SHIFTL= $CF00   ; x-shift, lowest 3 bits
SHIFTH= $CE00   ; x-shift, highest 3 bittid (multiplied with two)
POINTER= $033C  ; Pointer to shift-table
VALUE=  $033D   ; Shift now
SPEED=  $033E   ; Shift change

*= $C000  ; Start address..

INIT    SEI             ; Disable interrupts
        LDA #$7F
        STA $DC0D       ; Disable timer interrupt
        LDA #$81
        STA $D01A       ; Enable raster interrupt
        LDA #<IRQ
        STA $0314       ; Our own interrupt handler
        LDA #>IRQ
        STA $0315
        LDA #49         ; The interrupt to the line before the first bad line
        STA $D012
        LDA #$1B
        STA $D011       ; 9th bit of the raster compare

        LDY #0
        LDX #$40
        STX ZP+1
        STY ZP
        TYA
LOOP0   STA (ZP),Y      ; Clear the whole video bank ($4000-7FFF)
        INY
        BNE LOOP0
        INC ZP+1
        BPL LOOP0

        LDA #>START
        STA ZP+1
        LDA #$32        ; Character ROM to address space ($D000-)
        STA $01
LOOP1   TYA             ; (Y-register is zero initially)
        LSR
        LSR
        LSR
        TAX
        LDA TEXT,X      ; Which char to plot ?
        ASL             ; Source
        ASL
        ASL
        TAX             ; low byte to X
        LDA #$D0
        ADC #0          ; high byte (one bit) taken into account
        STA LOOP2+2 ; Self-modifying again..
LOOP2   LDA $D000,X
        STA (ZP),Y
        INX
        INY
        TXA
        AND #7
        BNE LOOP2       ; Copy one char
        CPY #0
        BNE LOOP1       ; Copy 32 chars (256 bytes)
        LDA #$37        ; Memory configuration back to normal
        STA $01

LOOP3   LDA START,Y       ; Copy the data to each charset, shifted by one
        STA START+2056,Y  ;  position to the right
        STA START+4112,Y
        STA START+6168,Y
        STA START+8224,Y
        STA START+10280,Y
        STA START+12336,Y
        STA START+14392,Y
        INY
        BNE LOOP3
        LDA #0          ; Clear the pointer, value and speed
        STA POINTER
        STA VALUE
        STA SPEED

LOOP4   TYA             ; (Y was zero)
        ORA #$80        ; Use the inverted chars
        STA SCREEN,Y    ; Set the character codes to video matrix
        STA SCREEN+40,Y
        STA SCREEN+80,Y
        STA SCREEN+120,Y
        STA SCREEN+160,Y
        STA SCREEN+200,Y
        STA SCREEN+240,Y
        STA SCREEN+280,Y
        LDA #239        ; leave the last line empty
        STA SCREEN+320,Y
        INY
        CPY #40
        BNE LOOP4       ; Loop until the whole area is filled
        CLI             ; Enable interrupts
        RTS

IRQ     LDA #BANK       ; Change the video bank, some timing
        STA $DD00
        NOP
        NOP

        LDY POINTER     ; Y-register will point to x-shift
        JMP BAD         ; next line is a bad line
LOOP5   NOP
LOOP6   LDA SHIFTL,Y    ; Do the shift
        STA $D016       ; 3 lowest bits
        LDA SHIFTH,Y
        STA $D018       ; another 3 bits
        NOP : NOP : NOP : NOP : NOP : NOP ; waste some time
        NOP : NOP : NOP : NOP : NOP : NOP
        NOP : NOP : NOP
        LDA $D012       ; check if it is time to stop
        CMP #$78
        BPL OVER
        INY   ; next position in table
        DEX
        BNE LOOP5       ; No bad line, loop
BAD     LDA SHIFTL,Y    ; This is a bad line, a bit more hurry
        STA $D016
        LDA SHIFTH,Y
        STA $D018
        INY
        LDX #7          ; New bad line coming up
        JMP LOOP6

OVER    LDA #$97        ; Video bank to 'normal'
        STA $DD00
        LDA #22         ; Same with the charset
        STA $D018
        LDA #8          ; and the horizontal scroll register
        STA $D016

        LDA $DC00       ; Let's check the joysticks
        AND $DC01
        TAX
        LDY SPEED
        AND #8          ; Turned right, add speed
        BNE EIP
        INY
        CPY #4          ; Don't store, too much speed
        BPL EIP
        STY SPEED
EIP     TXA
        AND #4          ; Turned left
        BNE ULOS
        DEY
        CPY #$FC        ; Too much ?
        BMI ULOS
        STY SPEED
ULOS    LDA VALUE       ; Add speed to value (signed)
        CLC
        ADC SPEED
        BPL OK
        LDA SPEED       ; Banged to the side ?
        EOR #$FF
        CLC
        ADC #1
        STA SPEED
        LDA VALUE
OK      STA VALUE
        LSR             ; Value is twice the shift
        TAX             ; Remember the shift
        AND #7          ; lowest 3 bits
        ORA #8          ; (screen 40 chars wide)
        LDY POINTER
        STA SHIFTL,Y
        TXA
        LSR
        LSR
        LSR             ; highest 3 bits too
        ASL             ;  multiplicated by two
        STA SHIFTH,Y
        DEC POINTER

        LDA #1          ; Ack the interrupt
        STA $D019
        JMP $EA31       ; The normal interrupt routine

TEXT    SCR "THIS IS TECH-TECH FOR C=64 BY ME" ; Test text
                        ; SCR converts to screen codes

--------------------------------------------------------------------------
Basic loader for the Tech-tech demo 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 78A97F8D0DDCA9818D1AD0A9AD8D1403A9C08D1503A9318D12D0A91B8D11D0A0, 3802
101 DATA 00A24086FC84FB9891FBC8D0FBE6FC10F7A94485FCA9328501984A4A4AAABD5E, 4749
102 DATA C10A0A0AAAA9D069008D4EC0BD00D091FBE8C88A2907D0F4C000D0DDA9378501, 4128
103 DATA B9004499084C99105499185C99206499286C99307499387CC8D0E5A9008D3C03, 3258
104 DATA 8D3D038D3E0398098099004099284099504099784099A04099C84099F0409918, 3236
105 DATA 41A9EF994041C8C028D0DB5860A9968D00DDEAEAAC3C034CE1C0EAB900CF8D16, 4464
106 DATA D0B900CE8D18D0EAEAEAEAEAEAEAEAEAEAEAEAEAEAEAAD12D0C9781016C8CAD0, 5850
107 DATA D9B900CF8D16D0B900CE8D18D0C8A2074CBBC0A9978D00DDA9168D18D0A9088D, 4132
108 DATA 16D0AD00DC2D01DCAAAC3E032908D008C8C00410038C3E038A2904D00888C0FC, 3160
109 DATA 30038C3E03AD3D03186D3E03100EAD3E0349FF1869018D3E03AD3D038D3D034A, 2139
110 DATA AA29070908AC3C039900CF8A4A4A4A0A9900CECE3C03A9018D19D04C31EA1408, 2759
111 DATA 091320091320140503082D1405030820060F1220033D3634200219200D050F12, 652
200 DATA END,0

--------------------------------------------------------------------------
Uuencoded C64 executable version of the demo program (PAL)

begin 644 tech.64
M`0@-"`$`4[(T.3$U,@`F"`(`EJ5(*$,ILD.K-#BJ-ZPH0[$V-"D`7`@#`$-(J
MLC`ZAT$D+$$ZF4$D.HM!)+(B14Y$(J>9(CS7R,G4Q3X\P\S2/B(ZGC0Y,34R;
M.H``DP@$`(%&LC"D,S$Z4;*E2"C&*,HH020L1JPRJC$I*2FL,3:JI4@HQBC*1
M*$$D+$:L,JHR*2DI`+0(!0!#2+)#2*I1.I=3+%$Z4[)3JC$Z@CJ+0TBR0:<S?
M`,P(!@"9(D-(14-+4U5-($524D]2(CJ``!D)9`"#(#<X03DW1CA$,$1$0T$YN
M.#$X1#%!1#!!.4%$.$0Q-#`S03E#,#A$,34P,T$Y,S$X1#$R1#!!.3%".$0Q+
M,40P03`L(#,X,#(`9@EE`(,@,#!!,C0P.#9&0S@T1D(Y.#DQ1D)#.$0P1D)%G
M-D9#,3!&-T$Y-#0X-49#03DS,C@U,#$Y.#1!-$$T04%!0D0U12P@-#<T.0"S+
M"68`@R!#,3!!,$$P04%!03E$,#8Y,#`X1#1%0S!"1#`P1#`Y,49"13A#.#A!]
M,CDP-T0P1C1#,#`P1#!$1$$Y,S<X-3`Q+"`T,3(X```*9P"#($(Y,#`T-#DYE
M,#@T0SDY,3`U-#DY,3@U0SDY,C`V-#DY,C@V0SDY,S`W-#DY,S@W0T,X1#!%,
M-4$Y,#`X1#-#,#,L(#,R-3@`30IH`(,@.$0S1#`S.$0S13`S.3@P.3@P.3DPU
M,#0P.3DR.#0P.3DU,#0P.3DW.#0P.3E!,#0P.3E#.#0P.3E&,#0P.3DQ."P@?
M,S(S-@":"FD`@R`T,4$Y148Y.30P-#%#.$,P,CA$,$1"-3@V,$$Y.38X1#`P-
M1$1%045!04,S0S`S-$-%,4,P14%".3`P0T8X1#$V+"`T-#8T`.<*:@"#($0PN
M0CDP,$-%.$0Q.$0P14%%045!14%%045!14%%045!14%%045!14%%045!040Q,
M,D0P0SDW.#$P,39#.$-!1#`L(#4X-3``-`MK`(,@1#E".3`P0T8X1#$V1#!"Q
M.3`P0T4X1#$X1#!#.$$R,#<T0T)"0S!!.3DW.$0P,$1$03DQ-CA$,3A$,$$Y_
M,#@X1"P@-#$S,@"!"VP`@R`Q-D0P040P,$1#,D0P,41#04%!0S-%,#,R.3`XN
M1#`P.$,X0S`P-#$P,#,X0S-%,#,X03(Y,#1$,#`X.#A#,$9#+"`S,38P`,X+'
M;0"#(#,P,#,X0S-%,#-!1#-$,#,Q.#9$,T4P,S$P,$5!1#-%,#,T.49&,3@V7
M.3`Q.$0S13`S040S1#`S.$0S1#`S-$$L(#(Q,SD`&PQN`(,@04$R.3`W,#DP;
M.$%#,T,P,SDY,#!#1CA!-$$T031!,$$Y.3`P0T5#13-#,#-!.3`Q.$0Q.40PI
M-$,S,45!,30P."P@,C<U.0!G#&\`@R`P.3$S,C`P.3$S,C`Q-#`U,#,P.#)$_
M,30P-3`S,#@R,#`V,$8Q,C(P,#,S1#,V,S0R,#`R,3DR,#!$,#4P1C$R+"`V&
4-3(`<PS(`(,@14Y$+#````````HS!
``
end

