home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
enterprs
/
c128
/
text
/
hacking6.arc
/
DYCP.TXT
< prev
next >
Wrap
Text File
|
1993-09-16
|
21KB
|
514 lines
================================================================================
The Demo Corner: DYCP - Horizontal Scrolling
by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi))
Written: 16-May-91 Translation 02-Jun-92
DYCP - too many sprites !?
--------------------------
DYCP - Different Y Character Position - is a name for a horizontal scroller,
where characters go smoothly up and down during their voyage from right to
left. One possibility is a scroll with 8 characters - one character in each
sprite, but a real demo coder won't be satisfied with that.
Demo coders thought that it looks good to make the scrolling text change its
vertical position in the same time it proceeded from the right side of the
screen to the left. The only problem is that there is only eight sprites
and that is not even nearly enough to satisfy the requirements needed for
great look. So the only way is to use screen and somehow plot the text in
graphics, because character columns can not be scrolled individually.
Plotting the characters take absolutely too much time, because you have to
handle each byte seperately and the graphics bitmap must be cleared too.
_Character hack_
The whole DYCP started using character graphics. You plot six character
rows where the character (screen) codes increase to the right and down.
This area is then used like a small bitmap screen. Each of the text chars
are displayed one byte at a time on each six rows high character columns.
This 240 character positions big piece of screen can be moved horizontally
using the x-scroll register (three lowest bits in $D016) and after eight
pixels you move the text itself, like in any scroll. The screen is of course
reduced to 38 columns wide to hide the jittering on the sides.
A good coder may also change the character sets during the display and
even double the size of the scroll, but because the raster time happens
to go to waste using this technique anyway, that is not very feasible. There
are also other difficulties in this approach, the biggest is the time needed
to clear the display.
_Save characters - and time_
But why should we move an eight-byte-high character image in a 48-line-high
area, when 16 is really enough ? We can use two characters for the graphics
bitmap and then move this in eight pixel steps up and down. The lowest
three bits of the y-position then gives us the offset where the data must
be plotted inside this graphical region. The two character codes are usually
selected to be consecutive ones so that the image data has also 16
consecutive bytes. [See picture 1.]
_Demo program might clear things up_
The demo program is coded using the latter algorithm. The program first
copies the Character ROM to ram, because it is faster to use it from there.
You can easily change the program to use your own character set instead,
if you like. The sinus data for the vertical movement is created of a 1/4
of a cycle by mirroring it both horizontally and vertically.
Two most time critical parts are clearing the character set and plotting the
new one. Neither of these may happen when VIC is drawing the area where the
scroll is, so there is a slight hurry. Using double buffering technique we
could overcome this limitation, but this is just an example program. For
speed there is CLC only when it is absolutely needed.
The NTSC version is a bit crippled, it only covers 32 columns and thus the
characters seem to appear from thin air. Anyway, the idea should become
clear.
_Want to go to the border ?_
Some coders are always trying to get all effects ever done using the C64 go
to the border, and even successfully. The easiest way is to use only a region
of 21 pixels high - sprites - and move the text exactly like in characters.
In fact only the different addressing causes the differences in the code.
Eight horizontally expanded sprites will be just enough to fill the side
borders. You can also mix these techiques, but then you have the usual
"chars-in-the-screen-while-border-opened"-problems (however, they are
solvable). Unfortunately sprite-dycp is even more slower than char-dycp.
_More movement vertically_
You might think that using the sprites will restrict the sinus to only
14 pixels. Not really, the only restriction is that the vertical position
difference between three consequent text character must be less than 14
pixel lines. Each sprites' Y-coordinate will be the minimum of the three
characters residing in that sprite. Line offsets inside the sprites
are then obtained by subtracting the sprite y-coordinate from the character
y-coordinate. Maybe a little hard to follow, but maybe a picture will
clear the situation. [See picture 2.]
Scrolling horizontally is easy. You just have to move sprites like you would
use the character horizontal scroll register and after eight pixels you
reset the sprite positions and scroll the text one position in memory.
And of course, you fetch a new character for the scroll. When we have
different and changing sprite y-coordinates, opening the side borders become
a great deal more difficult. However, in this case there is at least two
different ways to do it.
_Stretch the sprites_
The easiest way is to position all of the sprites where the scroll will
be when it is in its highest position. Then stretch the first and last line
of each sprite so that the 19 sprite lines in the middle will be on the
desired place. Opening the borders now is trivial, because all of the sprites
are present on all of the scan lines and they steal a constant amount of
time. However, we lose two sprite lines. We might not want to use the first
and the last line for graphics, because they are stretched.
[See previous C=Hacking Issues for more information about stretching and
stolen cycles.]
A more difficult approach is to unroll the routine and let another routine
count the sprites present in each line and then change the time the routine
uses accordingly. In this way you save time during the display for other
effects, like color bars, because stretching will take at least 12 cycles
on each raster line. On the other hand, if the sinus is constant (user is
not allowed to change it), it is usually possible to embedd the count
routine directly to the border opening part of the routine.
_More sprites_
You don't necassarily need to plot the characters in sprites to have more
than eight characters. Using a sprite multiplexing techiques you can double
or triple the number of sprites available. You can divide the scroll
vertically into several areas and because the y-coordinate of the scroll
is a sinus, there always is a fixed maximum number of sprites in each area.
This number is always smaller than the total number of sprites in the
whole scroll. I won't go into detail, but didn't want to leave this out
completely. [See picture 3.]
_Smoother and smoother_
Why be satisfied with a scroll with only 40 different slices horizontally ?
It should be possible to count own coordinates for each pixel column on
the scroll. In fact the program won't be much different, but the routine
must also mask the unwanted bits and write the byte to memory with ORA+STA.
When you think about it more, it is obvious that this takes a generous amount
of time, handling every bit seperately will take much more than eight times
the time a simple LDA+STA takes. Some coders have avoided this by plotting
the same character to different character sets simultaneously and then
changing the charsets appropriately, but the resulting scroll won't be much
larger than 96x32 pixels.
--------------------------------------------------------------------------
Picture 1 - Two character codes will make a graphical bitmap
Screen memory:
____________________________________
|Char |Char |Char |Char | ...
|Code |Code |Code |Code |
|0 |2 |80 |80 | .
| |** ** | | | .
| |** ** | | | .
|***** |****** | | |
|****** | **** | | |
|**__**_|__**___|_______|_______|
|** ** | ** | **** |Char |
|** ** | ** |****** |Code |
|****** | |** ** |6 |
|***** | |** | |
|Char |Char |** ** | |
|Code |Code |****** | |
|1 |3 |4**** |***** |
|_______|_______|_______|******_|
|Char |Char | |** ** |
|Code |Code | |****** |
|80 |80 | |***** |
| | | |** |
| | |Char |**ar |
| | |Code |Code |
| | |5 |7 |
|_______|_______|_______|_______|
Character set memory:
_________________________________________________________________
|Char 0 |Char 1 |Char 2 |Char 3 |Char 4 |Char 5 |Char 6 |Char 7 | ...
|_______|_______|_______|_______|_______|_______|_______|_______|__
DDDDDDDD YYYYYYYY CCCCCCCC PPPPPPPP
First column Second column Third column Fourth column
--------------------------------------------------------------------------
Picture 2 - DYCP with sprites
Sprite 0
_______________________
| |** ** | |
| |** ** | |
| |****** | |
|***** | **** | |
|****** | ** | |
|** ** | ** | |
|** ** | ** | |
|**__**_|_______|_______|
|****** | | **** |
|***** | |****** |
| | |** ** |
| | |** |
| | |** ** |
| | |****** |
| | | **** |
|_______|_______|_______| Sprite 1
| | | | _______________________
| | | ||***** | | |
| | | ||****** | | |
| | | ||** ** | | |
|_______|_______|_______||****** | | |
|***** | | |
|** | | |
|** | | |
|_______|_______|_______|
| | | |
| | | |
| | | |
| | **** | |
| | **** | |
| | |****** |
| | |****** |
|_______|_______|__**___|
| | | ** |
| | | ** |
| | | ** |
| | | ** |
|_______|_______|_______|
--------------------------------------------------------------------------
Picture 3 - Sprite multiplexing
__ Set coordinates for eight sprites
__|3 | that start from the top half.
|4 | |__
__| `--|2 |
|5 `--' | |
| | `--'__
`--' |1 |
| |
__ `--'
|6 |
| | __
`--' |0 |
| |
-__------------------------------------`--'When VIC has displayed the last
|0 | __ sprite, set coordinates for the
| | |6 | sprites in the lower half of the
`--' | | area.
`--'
__
|1 | __
| | |5 |
`-- __ | |
|2 | __`--'
| |__|4 | You usually have two sprites that
`--|3 | | are only 'used' once so that you
| `--' can change other sprites when VIC
`__' is displaying them.
--------------------------------------------------------------------------
DYCP demo program (PAL)
SINUS= $CF00 ; Place for the sinus table
CHRSET= $3800 ; Here begins the character set memory
GFX= $3C00 ; Here we plot the dycp data
X16= $CE00 ; values multiplicated by 16 (0,16,32..)
D16= $CE30 ; divided by 16 (16 x 0,16 x 1 ...)
START= $033C ; Pointer to the start of the sinus
COUNTER= $033D ; Scroll counter (x-scroll register)
POINTER= $033E ; Pointer to the text char
YPOS= $0340 ; Lower 4 bits of the character y positions
YPOSH= $0368 ; y positions divided by 16
CHAR= $0390 ; Scroll text characters, multiplicated by eight
ZP= $FB ; Zeropage area for indirect addressing
ZP2= $FD
AMOUNT= 38 ; Amount of chars to plot-1
PADCHAR= 32 ; Code used for clearing the screen
*= $C000
SEI ; Disable interrupts
LDA #$32 ; Character generator ROM to address space
STA $01
LDX #0
LOOP0 LDA $D000,X ; Copy the character set
STA CHRSET,X
LDA $D100,X
STA CHRSET+256,X
DEX
BNE LOOP0
LDA #$37 ; Normal memory configuration
STA $01
LDY #31
LOOP1 LDA #66 ; Compose a full sinus from a 1/4th of a
CLC ; cycle
ADC SIN,X
STA SINUS,X
STA SINUS+32,Y
LDA #64
SEC
SBC SIN,X
STA SINUS+64,X
STA SINUS+96,Y
INX
DEY
BPL LOOP1
LDX #$7F
LOOP2 LDA SINUS,X
LSR
CLC
ADC #32
STA SINUS+128,X
DEX
BPL LOOP2
LDX #39
LOOP3 TXA
ASL
ASL
ASL
ASL
STA X16,X ; Multiplication table (for speed)
TXA
LSR
LSR
LSR
LSR
CLC
ADC #>GFX
STA D16,X ; Dividing table
LDA #0
STA CHAR,X ; Clear the scroll
DEX
BPL LOOP3
STA POINTER ; Initialize the scroll pointer
LDX #7
STX COUNTER
LOOP10 STA CHRSET,X ; Clear the @-sign..
DEX
BPL LOOP10
LDA #>CHRSET ; The right page for addressing
STA ZP2+1
LDA #<IRQ ; Our interrupt handler address
STA $0314
LDA #>IRQ
STA $0315
LDA #$7F ; Disable timer interrupts
STA $DC0D
LDA #$81 ; Enable raster interrupts
STA $D01A
LDA #$A8 ; Raster compare to scan line $A8
STA $D012
LDA #$1B ; 9th bit
STA $D011
LDA #30
STA $D018 ; Use the new charset
CLI ; Enable interrupts and return
RTS
IRQ INC START ; Increase counter
LDY #AMOUNT
LDX START
LOOP4 LDA SINUS,X ; Count a pointer for each text char and according
AND #7 ; to it fetch a y-position from the sinus table
STA YPOS,Y ; Then divide it to two bytes
LDA SINUS,X
LSR
LSR
LSR
STA YPOSH,Y
INX ; Chars are two positions apart
INX
DEY
BPL LOOP4
LDA #0
LDX #79
LOOP11 STA GFX,X ; Clear the dycp data
STA GFX+80,X
STA GFX+160,X
STA GFX+240,X
STA GFX+320,X
STA GFX+400,X
STA GFX+480,X
STA GFX+560,X
DEX
BPL LOOP11
MAKE LDA COUNTER ; Set x-scroll register
STA $D016
LDX #AMOUNT
CLC ; Clear carry
LOOP5 LDY YPOSH,X ; Determine the position in video matrix
TXA
ADC LINESL,Y ; Carry won't be set here
STA ZP ; low byte
LDA #4
ADC LINESH,Y
STA ZP+1 ; high byte
LDA #PADCHAR ; First clear above and below the char
LDY #0 ; 0. row
STA (ZP),Y
LDY #120 ; 3. row
STA (ZP),Y
TXA ; Then put consecuent character codes to the places
ASL ; Carry will be cleared
ORA #$80 ; Inverted chars
LDY #40 ; 1. row
STA (ZP),Y
ADC #1 ; Increase the character code, Carry won't be set
LDY #80 ; 2. row
STA (ZP),Y
LDA CHAR,X ; What character to plot ? (source)
STA ZP2 ; (char is already multiplicated by eight)
LDA X16,X ; Destination low byte
ADC YPOS,X ; (16*char code + y-position's 3 lowest bits)
STA ZP
LDA D16,X ; Destination high byte
STA ZP+1
LDY #6 ; Transfer 7 bytes from source to destination
LDA (ZP2),Y : STA (ZP),Y
DEY ; This is the fastest way I could think of.
LDA (ZP2),Y : STA (ZP),Y
DEY
LDA (ZP2),Y : STA (ZP),Y
DEY
LDA (ZP2),Y : STA (ZP),Y
DEY
LDA (ZP2),Y : STA (ZP),Y
DEY
LDA (ZP2),Y : STA (ZP),Y
DEY
LDA (ZP2),Y : STA (ZP),Y
DEX
BPL LOOP5 ; Get next char in scroll
LDA #1
STA $D019 ; Acknowledge raster interrupt
DEC COUNTER ; Decrease the counter = move the scroll by 1 pixel
BPL OUT
LOOP12 LDA CHAR+1,Y ; Move the text one position to the left
STA CHAR,Y ; (Y-register is initially zero)
INY
CPY #AMOUNT
BNE LOOP12
LDA POINTER
AND #63 ; Text is 64 bytes long
TAX
LDA SCROLL,X ; Load a new char and multiply it by eight
ASL
ASL
ASL
STA CHAR+AMOUNT ; Save it to the right side
DEC START ; Compensation for the text scrolling
DEC START
INC POINTER ; Increase the text pointer
LDA #7
STA COUNTER ; Initialize X-scroll
OUT JMP $EA7E ; Return from interrupt
SIN BYT 0,3,6,9,12,15,18,21,24,27,30,32,35,38,40,42,45
BYT 47,49,51,53,54,56,57,59,60,61,62,62,63,63,63
; 1/4 of the sinus
LINESL BYT 0,40,80,120,160,200,240,24,64,104,144,184,224
BYT 8,48,88,128,168,208,248,32
LINESH BYT 0,0,0,0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,2,3
SCROLL SCR "THIS@IS@AN@EXAMPLE@SCROLL@FOR@"
SCR "COMMODORE@MAGAZINE@BY@PASI@OJALA@@"
; SCR will convert text to screen codes
--------------------------------------------------------------------------
Basic loader for the Dycp demo program (PAL)
1 S=49152
2 DEFFNH(C)=C-48+7*(C>64)
3 CH=0:READA$,A:PRINTA$:IFA$="END"THENPRINT"<white><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 78A9328501A200BD00D09D0038BD00D19D0039CAD0F1A9378501A01FA942187D, 3441
101 DATA 75C19D00CF9920CFA94038FD75C19D40CF9960CFE88810E4A27FBD00CF4A1869, 4302
102 DATA 209D80CFCA10F3A2278A0A0A0A0A9D00CE8A4A4A4A4A18693C9D30CEA9009D90, 3231
103 DATA 03CA10E58D3E03A2078E3D039D0038CA10FAA93885FEA99B8D1403A9C08D1503, 3338
104 DATA A97F8D0DDCA9818D1AD0A9A88D12D0A91B8D11D0A91E8D18D05860EE3C03A026, 3864
105 DATA AE3C03BD00CF2907994003BD00CF4A4A4A996803E8E88810EAA900A24F9D003C, 3256
106 DATA 9D503C9DA03C9DF03C9D403D9D903D9DE03D9D303ECA10E5AD3D038D16D0A226, 3739
107 DATA 18BC68038A7995C185FBA90479AAC185FCA920A00091FBA07891FB8A0A0980A0, 4224
108 DATA 2891FB6901A05091FBBD900385FDBD00CE7D400385FBBD30CE85FCA006B1FD91, 4440
109 DATA FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FB88B1FD91FBCA, 6225
110 DATA 109FEE19D0CE3D031028B99103999003C8C026D0F5AD3E03293FAABDBFC10A0A, 3593
111 DATA 0A8DB603CE3C03CE3C03EE3E03A9078D3D034C7EEA000306090C0F1215181B1E, 2159
112 DATA 202326282A2D2F3133353638393B3C3D3E3E3F3F3F00285078A0C8F018406890, 2268
113 DATA B8E008305880A8D0F82000000000000000010101010101020202020202020314, 1379
114 DATA 08091300091300010E000518010D100C05001303120F0C0C00060F1200030F0D, 304
115 DATA 0D0F040F1205000D0107011A090E050002190010011309000F0A010C01000000, 257
200 DATA END,0
================================================================================