Opening the borders by Pasi 'Albert' Ojala (po87553@cs.tut.fi or albert@cc.tut.fi) All timings are in PAL, principles will apply to NTSC too. Refer to VIC memory map in Hacking Issue 4. VIC has many features and transparent borders are one of them. You can not make characters appear in the border, but sprites are displayed in the border too. "How to do this then?" is the big question. The screen resolution in C64 has been and will be 320 x 200 pixels. Most games need to use the whole screen to be efficient or just plain playable. But there still is that useless border area, and you can put score and other status information there instead of having them interfere with the full-screen smooth-scrolling playing area. _How to disable the vertical borders_ When VIC (Video Interface Controller) has displayed all character rows, it will start displaying the vertical border area. It will start displaying the characters again in top of the screen. The row select register sets the number of character lines on the screen. If we select the 24-row display when VIC is drawing the last (25th) row, it does not start to draw the border at all ! VIC will think that it already started to draw the border. The 25-row display must be selected again in the top of the screen, so that the border may be opened in the next frame too. The number of displayed rows can be selected with the bit 3 in $d011. If the bit is set, VIC will display 25 rows and 24 rows otherwise. We have to clear the bit somewhere during the last row (raster lines $f2-$fa) and set it again in top of the screen or at least somewhere before the last row (line $f2). This has to be done in every frame (50 times per second in PAL). _How to open the sideborders_ The same trick can be applied to sideborders. When VIC is about to start displaying the sideborder, just select 38-column mode and restore 40-column mode so that you can do the trick again in the next scan line. If you need to open the sideborders in the bottom or top border area, you have to open the vertical borders also, but there shouldn't be any difficulty in doing that. There is two drawbacks in this. The timing must be precise, one clock cycle off and the sideborder will not open (the sprites will generally take care of the timing) and you have to do the opening on each and every line. With top/bottom borders once in a frame was enough. Another problem is bad-lines. There is not enough time to open the borders during a bad line and still have all of the sprites enabled. One solution is to open the borders only on seven lines and leave the bad lines unopened. Another way is to use less than eight sprites. You can have six of them on a bad line and still be able to open the sideborders (PAL). The old and still good solution is to scroll the bad lines, so that VIC will not start to draw the screen at all until it is allowed to do so. [Read more about bad lines from previous C=Hacking Issues] _Scrolling the screen_ VIC begins to draw the screen from the first bad line. VIC will know what line is a bad line by comparing its scan line counter to the vertical scroll register : when they match, the next line is a bad line. If we change the vertical scroll register ($d011), the first bad line will move also. If we do this on every line, the line counter in VIC will never match with it and the drawing never starts (until it is allowed to do so). When we don't have to worry about bad lines, we have enough time to open the borders and do some other effects too. It is not necassary to change the vertical scroll on every line to get rid of the bad lines, just make sure that it never matches the line counter (or actually the least significant 8 bits). You can even scroll the bad lines independently and you have FLD - Flexible Line Distance. You just allow a bad line when it is time to display the next character row. With this you can bounce the lines or scroll a hires picture very fast down the screen. But this has not so much to do with borders, so I will leave it to another article. (Just send requests and I might start writing about FLD ..) _Garbage appearing_ When we open the top and bottom borders, some graphics may appear. Even though VIC has already completed the graphics data fetches for the screen area, it will still fetch data for every character position in top and bottom borders. This will not do any harm though, because it does not generate any bad lines and happens during video fetch cycles [see Missing Cycles article]. VIC reads the data from the last address in the current video bank, which is normally $3fff and displays this over and over again. If we change the data in this address in the border area, the change will be visible right away. And if you synchronize the routine to the beam position, you can have a different value on each line. If there is nothing else to do in the border, you can get seven different values on each scan line. The bad thing about this graphics is that it is impossible to change its color - it is always black. It is of course possible to use inverted graphics and change the background color. And if you have different data on each line, you can as easily have different color(s) on each line too. If you don't use $3fff for any effects, it is a good idea to set it to zero, but remember to check that you do not store anything important in that address. In one demo I just cleared $3fff and it was right in the middle of another packed demopart. It took some time to find out what was wrong with the other part. _Horizontal scrolling_ This new graphics data also obeys the horizontal scroll register ($D016), so you can do limited tech-tech effects in the border too. You can also use sprites and open the sideborders. You can see an example of the tech-tech effect in the first example program. Multicolor mode select has no effect on this data. You can read more about tech-tech effects in a future article. _Example routine_ The example program will show how to open the top and bottom borders and how to use the $3fff-graphics. It is fairly well commented, so just check it for details. The program uses a sprite to do the synchronization [see Missing Cycles article] and reads a part of the character ROM to the display data buffer. To be honest, I might add that this is almost the same routine than the one in the Missing Cycles article. I have included both PAL and NTSC versions of the executables. -------------------------------------------------------------------------- The example program - $3fff-graphics IMAGE0= $CE00 ; First graphics piece to show IMAGE1= $CF00 ; Second piece TECH= $CD00 ; x-shift RASTER= $FA ; Rasterline for the interrupt DUMMY= $CFFF ; Dummy-address for timing (refer to missing_cycles-article) *= $C000 SEI ; Disable interrupts LDA #$7F ; Disable timer interrupts (CIA) STA $DC0D LDA #$01 ; Enable raster interrupts (VIC) STA $D01A STA $D015 ; Enable the timing sprite LDA #IRQ STA $0315 LDA #RASTER ; Set the raster compare (9th bit will be set STA $D012 ; inside the raster routine) LDA #RASTER-20 ; Sprite is situated 20 lines before the interrupt STA $D001 LDX #111 LDY #0 STY $D017 ; Disable y-expand LDA #$32 STA $01 ; Select Character ROM LOOP0 LDA $D000,X STA IMAGE0,Y ; Copy a part of the charset to be the graphics STA IMAGE0+112,Y LDA $D800,X STA IMAGE1,Y STA IMAGE1+112,Y INY ; Until we copied enough DEX BPL LOOP0 LDA #$37 ; Char ROM out of the address space STA $01 LDY #15 LOOP1 LDA XPOS,Y ; Take a half of a sinus and mirror it to make STA TECH,Y ; a whole cycle and then copy it as many times STA TECH+32,Y ; as necassary LDA #24 SEC SBC XPOS,Y STA TECH+16,Y STA TECH+48,Y DEY BPL LOOP1 LDY #64 LOOP2 LDA TECH,Y STA TECH+64,Y STA TECH+128,Y DEY BPL LOOP2 CLI ; Enable interrupts RTS ; Return to basic (?) IRQ LDA #$13 ; Open the bottom border (top border will open too) STA $D011 NOP LDY #111 ; Reduce for NTSC ? INC DUMMY ; Do the timing with a sprite BIT $EA ; Wait a bit (add a NOP for NTSC) LOOP3 LDA TECH,Y ; Do the x-shift STA $D016 FIRST LDX IMAGE0,Y ; Load the graphics to registers SECOND LDA IMAGE1,Y STA $3FFF ; Alternate the graphics STX $3FFF STA $3FFF STX $3FFF STA $3FFF STX $3FFF STA $3FFF STX $3FFF STA $3FFF STX $3FFF LDA #0 ; Throw away 2 cycles (add a NOP for NTSC) DEY BPL LOOP3 STA $3FFF ; Clear the graphics LDA #8 STA $D016 ; x-scroll to normal LDA #$1B STA $D011 ; Normal screen (be ready to open the border again) LDA #111 DEC FIRST+1 ; Move the graphics by changing the low byte of the BPL OVER ; load instruction STA FIRST+1 OVER SEC SBC FIRST+1 STA SECOND+1 ; Another graphics goes to opposite direction LDA LOOP3+1 ; Move the x-shift also SEC SBC #2 AND #31 ; Sinus cycle is 32 bytes STA LOOP3+1 LDA #1 STA $D019 ; Acknowledge the raster interrupt JMP $EA31 ; jump to the normal irq-handler XPOS BYT $C,$C,$D,$E,$E,$F,$F,$F,$F,$F,$F,$F,$E,$E,$D,$C BYT $C,$B,$A,$9,$9,$8,$8,$8,$8,$8,$8,$8,$9,$9,$A,$B ; half of the sinus