I've seen cool C64 demos showing sprites in the border area of the screen. It shouldn't be possible; I think they managed to fool the graphics chip somehow. How exactly did they do it?
-
to get more than the allowed sprites, you have to move the sprite fast enough in relation to the TV refresh rate: the sprite is drawn on the screen in location A, you move it to location B and it gets redrawn again, etc and then when the tv screen is done being drawn, back to location A or where ever A has moved to now. – KM. Sep 25 '09 at 14:19
-
4I just added c64 to my list of Interesting Tags. +1 from me! – Paolo Tedesco Sep 25 '09 at 14:23
-
on it's bigger brother(sister?) the Amiga they added a whole coprocessor (the copper) to define what video hardware changes had to be done at what screenrasterlines. Removing the need to complex assembly interrupt programs – Toad Sep 25 '09 at 14:24
-
3how can this question be "not constructive"? How do I show stuff in the border and the answer contains the code to do that. How can it be more constructive than that? – Peter Kofler Jan 15 '13 at 16:26
-
@TylerCrompton Unlike "how do I print text near the edge of the screen", sprites "aren't supposed to" be on the border. – wizzwizz4 Apr 13 '17 at 22:45
-
1@KM (sorry to dig up old comment) but you're not really "moving" a sprite at a faster relation to the TV. Instead, you tell the VIC-II to generate an interrupt on a specific line and when the raster beam reaches that line, you then set the sprite pointers again. Repeat for as many lines as needed. Just leave a little space between each sprite height (unless you get into sprite crunching). The VIC-II will handle the rest. – cbmeeks Jan 15 '19 at 20:38
6 Answers
Yes, you need assembler. It's an interrupt timing trick. The VIC is able to show sprites in the border, but the frame is just hiding them, so the sprites can slide behind it. It's connected to scan lines displayed by the VIC. For lower/upper borders it's quite simple:
- Programm an interrupt, synced to start at a certain scan line, 7 pixel or something like that before the lower border.
- Set the register in VIC to make the border smaller. (There is a register that can do that.)
- VIC now believes that the border already started and does not start to paint it.
- -> No border at the bottom.
- Programm another interrupt after the real border to set it back to original.
For sprites in the left/right borders, it's more complicated because the process has to be repeated for every scan line:
- Programm an interrupt, synced to start at a certain scan line.
- Then do some NOPs until you are 7 pixels before the right border.
- Set the register in VIC to make the border smaller.
- -> No border at the right side.
- Do some NOPs until you are after the real border and set the register back to original value.
- Again do some NOPs until step 2.
The problem is that all these NOPs are busy waits and steal the cycles you have for your stuff.
I was able to find some code for you, from an sprite scroller in the lower border. Here is the code. (It was ripped from some demo.)
C198 78 SEI
C199 20 2E C1 JSR C12E # clear sprite area
C19C 20 48 C1 JSR C148 # init VIC
C19F A9 BF LDA #BF # set up IRQ in C1BF
C1A1 A2 C1 LDX #C1
C1A3 8D 14 03 STA 0314
C1A6 8E 15 03 STX 0315
C1A9 A9 1B LDA #1B
C1AB 8D 11 D0 STA D011
C1AE A9 F7 LDA #F7
C1B0 8D 12 D0 STA D012
C1B3 A9 01 LDA #01
C1B5 8D 1A D0 STA D01A
C1B8 A9 7F LDA #7F
C1BA 8D 0D DC STA DC0D
C1BD 58 CLI
C1BE 60 RTS
----------------------------------
# init VIC
C148 A2 00 LDX #00
C14A BD 88 C1 LDA C188,X
C14D 9D 00 D0 STA D000,X # set first 16 values from table
C150 E8 INX
C151 E0 10 CPX #10
C153 D0 F5 BNE C14A
C155 A9 FF LDA #FF
C157 8D 15 D0 STA D015
C15A A9 00 LDA #00
C15C 8D 1C D0 STA D01C
C15F A9 FF LDA #FF
C161 8D 17 D0 STA D017
C164 8D 1D D0 STA D01D
C167 A9 C0 LDA #C0
C169 8D 10 D0 STA D010
C16C A9 F8 LDA #F8
C16E A2 00 LDX #00
C170 9D F8 07 STA 07F8,X
C173 18 CLC
C174 69 01 ADC #01
C176 E8 INX
C177 E0 08 CPX #08
C179 D0 F5 BNE C170
C17B A9 0E LDA #0E
C17D A2 00 LDX #00
C17F 9D 27 D0 STA D027,X
C182 E8 INX
C183 E0 08 CPX #08
C185 D0 F8 BNE C17F
C187 60 RTS
----------------------------------
# data set into VIC registers
C188 00 F7 30 F7 60 F7 90 F7
C190 C0 F7 F0 F7 20 F7 50 F7
----------------------------------
# main IRQ routine
C1BF A2 08 LDX #08
C1C1 CA DEX
C1C2 D0 FD BNE C1C1
C1C4 A2 28 LDX #28 # 40 or so lines
C1C6 EA NOP # "timing"
C1C7 EA NOP
C1C8 EA NOP
C1C9 EA NOP
C1CA CE 16 D0 DEC D016 # fiddle register
C1CD EE 16 D0 INC D016
C1D0 AC 12 D0 LDY D012
C1D3 88 DEY
C1D4 EA NOP
C1D5 98 TYA
C1D6 29 07 AND #07
C1D8 09 18 ORA #18
C1DA 8D 11 D0 STA D011
C1DD 24 EA BIT EA
C1DF EA NOP
C1E0 EA NOP
C1E1 CA DEX
C1E2 10 E4 BPL C1C8 # repeat next line
C1E4 A9 1B LDA #1B
C1E6 8D 11 D0 STA D011
C1E9 A9 01 LDA #01
C1EB 8D 19 D0 STA D019
C1EE 20 00 C0 JSR C000 # call main code
C1F1 4C 31 EA JMP EA31 # finish IRQ

- 4,738
- 23
- 27
- 42

- 9,252
- 8
- 51
- 79
-
I think the "init VIC" code at 0xC148 mainly sets up sprites in the border for the sprite scroller (It's connected to disabling the border.) But the sprites have to be there otherwise the timing of VIC/raster lines is different and the whole thing does not work. And you want to be sprites there anyway :-) – Peter Kofler Sep 28 '09 at 09:25
It all relied on timing. The C64 had a method to query the exact vertical location of the electron beam while it was drawing the screen. When a new line started, you had to wait a few cycles (you could time this using the NOP instruction) and then you had to set a hardware register of the videochip which was responsible for setting the screenmode (and the border width). By timing it exactly right, and doing it every scanline again, the whole sideborder disappeared.
The bottom border went away with a similar trick. On the exact scanline where the vertical border started you too had to set the videomode which disabled the bottom border for that frame.
Indeed this whole thing had to be done in assembly. Otherwise you could never get the timing exactly right.
As a side note, I think the sideborder trick was credited to the 1001 Crew (a Dutch group). I'm not sure who pulled off the first bottom border trick.

- 4,738
- 23
- 27
- 42

- 15,593
- 16
- 82
- 128
-
yes but that was not the register you had to set to change the screenmode – Toad Feb 04 '13 at 15:26
-
I know, but you wrote it rather "unspecifically". So I just wanted to add some details. I believe $d011 was the one selecting if you were using 25 or 24 lines, which made the border contract or not. http://en.wikipedia.org/wiki/MOS_Technology_VIC-II – BerggreenDK Feb 06 '13 at 15:15
For a good tutorial on the topic of opening the borders on the C64, check out Pasi Ojala's excellent article in C=Hacking Issue 6.
Without getting too technical, the trick uses a feature of the VIC chip to let you switch between 25/24 rows and 40/38 columns of text/graphics, and involves making this switch at the exact right moment to fool the VIC into thinking it has already switched the borders on when in fact it hasn't. Check out the above article for a more thorough explanation with code examples.

- 14,721
- 2
- 45
- 49
That is a long time ago.
I know there was a solution that relied on the frequency of the monitor.
With a CRT, the current pixel is known even if it was outside of the normal screen. So you could manipulate the ray.
Somewhere in my junkpile there must be some C64 books.
Offtopic, but graphics with the VIC20 (the predecessor of the C64) was fun. There was no way to manipulate each pixel, but you could change the existing characters. So you filled the screen with all characters from 0 to ... and changed the characters to set pixels to the screen. ;-).

- 52,876
- 38
- 145
- 202
-
1Also on C64 wasn't very simply to change a pixel. The graphical mode essentially emulated a character mode screen filled with 0, 1, ... characters. :-) – peterh May 23 '17 at 18:20
-
You can manipulate every single pixel using the hi-res bitmap mode. Or pairs of them in the multicolor bitmap mode, but that always uses 8000 Bytes (not 8k) and you don't always have those. Btw, you don't change the existing characters, they're in a ROM so you can't do that, but you can copy them to RAM and then manipulate them. – Nov 26 '20 at 13:16
As it was already said, you have to fool the VIC to think that the border already started, but the reason why I write this is because the top answer is a little bit inprecise: I was totally unable to find a register to make the border smaller, so this is the way you do it (at least for top and bottom): You wait until the VIC reached the 25th character row and then you enable 24 rows ($D011, bit 3). You can do the same thing for the left and the right border, just with 38 cols ($D016, bit 3), but to do that you need very precise timing and you also need to eliminate the bad lines by setting the vertical scroll register, so the scanline mod 8 is never equal to the scroll value. Of course, you can't use the normal display anymore because the bad lines actually aren't just bad, they are used to load character data I think, stuff that repeats for every 8th line in the non-border area. I personally was a little bit confused when I read the top answer, I hope that can help. (Also, the top answer has a mistake: You don't make the border smaller, you make it bigger)
Timing was the key. The image was created in the border by changing the overscan (border) colour as the CRT's beam moved from left to right. There are two timing signals required to produce an image - vertical refresh and horizontal refresh. By detecting when the horizontal and vertical refresh occurs you can start a sequence of assembler instructions to change the border colour to produce an image. You need to work out the number of CPU clock ticks per border pixel and use that to create code that changes the border colour at the right point.
It doesn't work very well when it comes to writing games as the CPU overhead is too great for there to be any time lift to process user input and game state.

- 69,698
- 10
- 71
- 108
-
this is not how it was done... The processor was too slow to change the color fast enough so that any pixel alone could be lighted. At best. The way to remove the border was to time the exact moment when the border began/stopped and change the displaymode. (see my answer) – Toad Sep 26 '09 at 09:46
-
I've just done some calculations: 25 frames at 250 lines a frame is 6250 lines a second or 0.00016 seconds a line. At 1Mhz this is 160 instructions so the most you can get using this method is 160 pixels accross the screen. I have seen images in the border done this way and they were a quite blocky. – Skizz Sep 28 '09 at 09:20
-
Once you get the timing right and get the vertical border open/disabled, the sprites (if any) placed out there becomes visible. thats how you get graphics out there. – BerggreenDK Feb 03 '13 at 23:09
-
1Not sure your calculations are right there Skizz. For example the instruction to increment a memory location not in zero page is 6 cycles. Or the instruction to store the accumulator to memory is 4 cycles. And one machine cycle is 8 hires pixels (or 4 multicolor pixels) of rastertime. – Lorraine Aug 30 '17 at 14:51