Our almost-4yo went to sleep on the way home at 16h30 today, and so as a result is now up at 02h00. While I'd rather be sleeping, being up with him for a while gives me the chance to try the latest change that I left synthesising when I went to bed. That change was to make the VIC-II sprites honour the border.
My favourite way to test sprites at the moment is to run Lemmings. This confirmed that the sprites were now honouring the border. I also finally remembered the controls for Lemmings to start a game, and was pleasantly surprised to find that the game works, with little lemmings walking around the place as they should. The game is raster interrupt driven, so the speed was more or less correct as well, as you can see from the following screen shot:
I also learned two extra things:
1. Lemmings apparently uses sprites for the main display.
2. I have a bug where the bottom row of each sprite appears first.
I was also unable to see the cross-hairs, which I assume must be done with characters or bitmap data.
A quick check in VICE confirmed that this is indeed how the cross-hairs are drawn. So now I need to find out what is going wrong with this on the C65GS. I do at least now know that it is in characters $FC and $FD, and the screen is at $4000 for half the frames.
A quick bit of poking around has revealed the problem: I haven't implemented sprite background priority yet, so the sprites are hiding the cross hairs.
In theory, I should be able to use the joystick to move the cross-hairs to a blank section so that I can see it, however, for some reason joystick control isn't working. Maybe I have messed up the joystick CIA input in some way. I'll have to investigate this further, along with the sprite display problem.
Thursday, 16 October 2014
Wednesday, 15 October 2014
Confirmed that I have fixed the sneaky CPU bug
This morning after synthesis of the fix for the sneaky CPU bug fix, I had the chance to test it out.
Rayne's interlace test program now works, and his MUIFLI program is also closer to working, although it isn't showing the right data. But that could be due to FLI not working on the VIC-IV -- yet to be confirmed.
However, what it did also fix is BoulderMark. So I can now present the latest result for the C65GS with this benchmark:
Notice that now the sprite appears (and that the sprite sitting in the border is also visible because sprites currently sit in front of the border). Otherwise the display is just about perfect. This image was captured via the VNC server video streaming interface (search previous posts to find out more about this).
Anyway, this all equates to 94x NTSC C64 or almost exactly 100x PAL C64. Of course as I have mentioned before, BoulderMark is non-linear with fast accelerators, and so the real performance is much more likely to be the roughly 44x that SynthMark64 reports.
Rayne's interlace test program now works, and his MUIFLI program is also closer to working, although it isn't showing the right data. But that could be due to FLI not working on the VIC-IV -- yet to be confirmed.
However, what it did also fix is BoulderMark. So I can now present the latest result for the C65GS with this benchmark:
Notice that now the sprite appears (and that the sprite sitting in the border is also visible because sprites currently sit in front of the border). Otherwise the display is just about perfect. This image was captured via the VNC server video streaming interface (search previous posts to find out more about this).
Anyway, this all equates to 94x NTSC C64 or almost exactly 100x PAL C64. Of course as I have mentioned before, BoulderMark is non-linear with fast accelerators, and so the real performance is much more likely to be the roughly 44x that SynthMark64 reports.
Tuesday, 14 October 2014
Found a sneaky CPU bug
While trying to run some graphic test programmes supplied by Rayne, I found that the CPU was mis-behaving in a way that reminded me of the bug I was seeing with BoulderMark, and probably Lemmings as well. Basically all was fine until a raster interrupt occurred, and then things would go odd or outright crash. What was extra odd was that BoulderMark would still run on the FPGA at work, but not on the one here at home, which shouldn't happen -- FPGAs shouldn't be picky like that.
Anyway, Rayne's programmes are much simpler, and offered the prospect of easily debugging what was going on.
So after a bit of poking around I discovered that the C65GS would go to lala-land after INC $D019.
This got me thinking, because $D019 is special in my CPU, because it adds a dummy write for RMW instructions that touch $D019, but not any other address. This is to avoid wasting a CPU cycle on the dummy write of the original value back to memory, except when required for C64 compatibility.
The lack of this dummy write on the C65, that acts to clear the VIC-II interrupt on a C64, is one of the major sources of incompatibility between the C65 and C64, and stops the majority of software from running on it. Thus I had gone to special effort to make sure it wouldn't be a problem on the C65GS, but without the CPU speed penalty of doing it on every address.
However, I had messed up the dummy write state in the CPU: it was not setting the target address on the bus, and so instead was writing to the last accessed memory location, which was the first byte of the following instruction. The net result is that the old contents of $D019, usually $F0 or $F1, would get written to the next byte in the instruction stream. I confirmed this in simulation, where the dummy write and final write can be seen marked in bold. Note that the dummy write is going to $F60F, not $D019!
gs4510.vhdl:1685:11:@700ns:(report note): MEMORY reading $FFFF60C = $EE
gs4510.vhdl:1004:7:@700ns:(report note): MEMORY long_address = $FFFF60D
gs4510.vhdl:1685:11:@780ns:(report note): MEMORY reading $FFFF60D = $19
gs4510.vhdl:1004:7:@780ns:(report note): MEMORY long_address = $FFFF60E
gs4510.vhdl:1685:11:@860ns:(report note): MEMORY reading $FFFF60E = $D0
gs4510.vhdl:1004:7:@860ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@940ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1004:7:@940ns:(report note): MEMORY long_address = $FFD3019
gs4510.vhdl:1685:11:@1020ns:(report note): MEMORY reading $FFD3019 = $70
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1304:9:@1100ns:(report note): writing to shadow RAM via chipram shadowing. addr=$000F60F
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $000F60F <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0 inc $D019 A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00 ..E-.I..
Oops.. not actually all fixed. It is now writing to $D019 in RAM, not IO. Lucky I decided to write this blog post, or I wouldn't have spotted that I still had the memory write flags slightly messed up. Specifically memory_access_resolve_address wasn't asserted, so the 16-bit address was not being translated to the physical 28-bit address. Fix that and try again:
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $FFD3019 <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0 inc $D019 A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00 ..E-.I..
Ah, that's better!
Now to resynthesise, and see if BoulderMark, Lemmings and Rayne's MUIFLI all work properly.
Anyway, Rayne's programmes are much simpler, and offered the prospect of easily debugging what was going on.
So after a bit of poking around I discovered that the C65GS would go to lala-land after INC $D019.
This got me thinking, because $D019 is special in my CPU, because it adds a dummy write for RMW instructions that touch $D019, but not any other address. This is to avoid wasting a CPU cycle on the dummy write of the original value back to memory, except when required for C64 compatibility.
The lack of this dummy write on the C65, that acts to clear the VIC-II interrupt on a C64, is one of the major sources of incompatibility between the C65 and C64, and stops the majority of software from running on it. Thus I had gone to special effort to make sure it wouldn't be a problem on the C65GS, but without the CPU speed penalty of doing it on every address.
However, I had messed up the dummy write state in the CPU: it was not setting the target address on the bus, and so instead was writing to the last accessed memory location, which was the first byte of the following instruction. The net result is that the old contents of $D019, usually $F0 or $F1, would get written to the next byte in the instruction stream. I confirmed this in simulation, where the dummy write and final write can be seen marked in bold. Note that the dummy write is going to $F60F, not $D019!
gs4510.vhdl:1685:11:@700ns:(report note): MEMORY reading $FFFF60C = $EE
gs4510.vhdl:1004:7:@700ns:(report note): MEMORY long_address = $FFFF60D
gs4510.vhdl:1685:11:@780ns:(report note): MEMORY reading $FFFF60D = $19
gs4510.vhdl:1004:7:@780ns:(report note): MEMORY long_address = $FFFF60E
gs4510.vhdl:1685:11:@860ns:(report note): MEMORY reading $FFFF60E = $D0
gs4510.vhdl:1004:7:@860ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@940ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1004:7:@940ns:(report note): MEMORY long_address = $FFD3019
gs4510.vhdl:1685:11:@1020ns:(report note): MEMORY reading $FFD3019 = $70
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1304:9:@1100ns:(report note): writing to shadow RAM via chipram shadowing. addr=$000F60F
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $000F60F <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0 inc $D019 A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00 ..E-.I..
So a quick fix and re-run simulation and suddenly we can see that it is all fixed:
gs4510.vhdl:1685:11:@700ns:(report note): MEMORY reading $FFFF60C = $EE
gs4510.vhdl:1004:7:@700ns:(report note): MEMORY long_address = $FFFF60D
gs4510.vhdl:1685:11:@780ns:(report note): MEMORY reading $FFFF60D = $19
gs4510.vhdl:1004:7:@780ns:(report note): MEMORY long_address = $FFFF60E
gs4510.vhdl:1685:11:@860ns:(report note): MEMORY reading $FFFF60E = $D0
gs4510.vhdl:1004:7:@860ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@940ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1004:7:@940ns:(report note): MEMORY long_address = $FFD3019
gs4510.vhdl:1685:11:@1020ns:(report note): MEMORY reading $FFD3019 = $70
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1304:9:@1100ns:(report note): writing to shadow RAM via chipram shadowing. addr=$000D019
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $000D019 <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0 inc $D019 A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00 ..E-.I..
gs4510.vhdl:1004:7:@1020ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1100ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:1689:11:@1140ns:(report note): MEMORY writing $FFD3019 <= $70
gs4510.vhdl:1689:11:@1180ns:(report note): MEMORY writing $FFD3019 <= $71
gs4510.vhdl:1004:7:@1180ns:(report note): MEMORY long_address = $FFFF60F
gs4510.vhdl:1685:11:@1260ns:(report note): MEMORY reading $FFFF60F = $AD
gs4510.vhdl:738:9:@1260ns:(report note): $F60C EE 19 D0 inc $D019 A:11 X:22 Y:33 Z:00 SP:01FF P:24 $01=3F MAPLO:0000 MAPHI:8F00 ..E-.I..
Ah, that's better!
Now to resynthesise, and see if BoulderMark, Lemmings and Rayne's MUIFLI all work properly.
More work on sprites
I don't have any nice screen shots to put in here (but I might add some in later), but I have been working on VIC-II sprites.
These sprites are now displaying properly, apart from the lack of border/foreground priority and hardware collision detection. Sprite positions are now correct with regard to the text/bitmap screen.
Unfortunately, adding the extra logic to the VIC-IV memory access paths has thrown FPGA timing closure out the window for now.
The 192MHz pixel clock requires timing within about 5.1ns, but is currently sitting around 7.3ns. It is an amazing testimony to the Artix7 FPGAs that the system still seems to run flawlessly. This is partly because the FPGA is speed rated for operation at 85 degrees Centigrade and an operating voltage of 0.95 Volts instead of the nominal 1.00 Volt supply.
In any case, I want to get the timing at least close to meeting closure (i.e., being fast enough), so that I can avoid problems later, and also to make sure that everything else that I want to add will still fit.
My approach to this at the moment is to unify the VIC-II compatibility sprite data fetches so that there is only one extra data stream that has to be plugged into the chipram/fastram. I am part way through this, and have already improved timing to about 6.6ns, and it looks like it shouldn't be too hard to further improve on this.
I have also started thinking about the design for the new sprites. This is all subject to change, but here is what I am thinking about at the moment:
The basic design of the new VIC-IV sprites, is that each sprite will have a dedicated 4KB memory buffer, and will be strictly one byte per pixel. This allows for sprites of up to 64x64 256 colour pixels.
Like with the VIC-II, one physical sprite can be used multiple times on a frame without reloading the data by altering the data offset within the 4KB block, and possibly the height and width of the sprite. I am also thinking about allowing sprites to be much wider.
Foreground/background priority will be by applying a bit mask to the character/bitmap data to decide whether it should appear in front of the sprite or behind the sprite. This will allow sprites and the background to perform many of the functions of Amiga-style bit planes, although the way it will be done will be rather different.
Bit masks are also provided to allow modification of the colours of sprites. For example applying and AND mask of $1f and an OR mask of $80 will translate all colours to $80-$9F. This can be used to allow a common image to be used for different characters in a game, with selected colours being altered. The 256 colour sprite palette can be separated from the bitmap palette, so there is improved flexibility compared to just applying bit masks to a flat 256 colour palette shared by all on-screen elements. If I get really excited it might even be possible to use the other two 256 colour palettes for different sprites.
Finally, I intend to provide hardware scaling and rotation support. I thought about having simple angle and zoom factor settings, but currently I am thinking that I will simply provide a linear 2D transformation matrix per sprite so that other effects can also be used.
The registers for the VIC-IV sprites are currently planned to live at $D710-$D7FF, allowing for up to 15 of these sprites, but there may end up being less than these depending on how many I can wrangle in.
All this is subject to change, as is the register map, but here is the structure I am currently looking at:
$D7x0-$D7x1 - Enhanced sprite X position in physical pixels (lower 12 bits)
$D7x1.4-7 - Enhanced sprite width (4 -- 64 pixels)
$D7x2-$D7x3 - Enhanced sprite Y position in physical pixels (16 bits)
$D7x3.4-7 - Enhanced sprite height (4 -- 64 pixels)
$D7x4 - Enhanced sprite data offset in its 4KB SpriteRAM (x16 bytes)
$D7x5 - Enhanced sprite foreground mask
$D7x6 - Enhanced sprite colour AND mask (sprite not visible if result = $00)
$D7x7 - Enhanced sprite colour OR mask
$D7x8-$D7x9 - Enhanced sprite 2x2 linear transform matrix 0,0 (5.11 bits)
$D7xA-$D7xB - Enhanced sprite 2x2 linear transform matrix 0,1 (5.11 bits)
$D7xC-$D7xD - Enhanced sprite 2x2 linear transform matrix 1,0 (5.11 bits)
$D7xE-$D7xF - Enhanced sprite 2x2 linear transform matrix 1,1 (5.11 bits)
These sprites are now displaying properly, apart from the lack of border/foreground priority and hardware collision detection. Sprite positions are now correct with regard to the text/bitmap screen.
Unfortunately, adding the extra logic to the VIC-IV memory access paths has thrown FPGA timing closure out the window for now.
The 192MHz pixel clock requires timing within about 5.1ns, but is currently sitting around 7.3ns. It is an amazing testimony to the Artix7 FPGAs that the system still seems to run flawlessly. This is partly because the FPGA is speed rated for operation at 85 degrees Centigrade and an operating voltage of 0.95 Volts instead of the nominal 1.00 Volt supply.
In any case, I want to get the timing at least close to meeting closure (i.e., being fast enough), so that I can avoid problems later, and also to make sure that everything else that I want to add will still fit.
My approach to this at the moment is to unify the VIC-II compatibility sprite data fetches so that there is only one extra data stream that has to be plugged into the chipram/fastram. I am part way through this, and have already improved timing to about 6.6ns, and it looks like it shouldn't be too hard to further improve on this.
I have also started thinking about the design for the new sprites. This is all subject to change, but here is what I am thinking about at the moment:
The basic design of the new VIC-IV sprites, is that each sprite will have a dedicated 4KB memory buffer, and will be strictly one byte per pixel. This allows for sprites of up to 64x64 256 colour pixels.
Like with the VIC-II, one physical sprite can be used multiple times on a frame without reloading the data by altering the data offset within the 4KB block, and possibly the height and width of the sprite. I am also thinking about allowing sprites to be much wider.
Foreground/background priority will be by applying a bit mask to the character/bitmap data to decide whether it should appear in front of the sprite or behind the sprite. This will allow sprites and the background to perform many of the functions of Amiga-style bit planes, although the way it will be done will be rather different.
Bit masks are also provided to allow modification of the colours of sprites. For example applying and AND mask of $1f and an OR mask of $80 will translate all colours to $80-$9F. This can be used to allow a common image to be used for different characters in a game, with selected colours being altered. The 256 colour sprite palette can be separated from the bitmap palette, so there is improved flexibility compared to just applying bit masks to a flat 256 colour palette shared by all on-screen elements. If I get really excited it might even be possible to use the other two 256 colour palettes for different sprites.
Finally, I intend to provide hardware scaling and rotation support. I thought about having simple angle and zoom factor settings, but currently I am thinking that I will simply provide a linear 2D transformation matrix per sprite so that other effects can also be used.
The registers for the VIC-IV sprites are currently planned to live at $D710-$D7FF, allowing for up to 15 of these sprites, but there may end up being less than these depending on how many I can wrangle in.
All this is subject to change, as is the register map, but here is the structure I am currently looking at:
$D7x0-$D7x1 - Enhanced sprite X position in physical pixels (lower 12 bits)
$D7x1.4-7 - Enhanced sprite width (4 -- 64 pixels)
$D7x2-$D7x3 - Enhanced sprite Y position in physical pixels (16 bits)
$D7x3.4-7 - Enhanced sprite height (4 -- 64 pixels)
$D7x4 - Enhanced sprite data offset in its 4KB SpriteRAM (x16 bytes)
$D7x5 - Enhanced sprite foreground mask
$D7x6 - Enhanced sprite colour AND mask (sprite not visible if result = $00)
$D7x7 - Enhanced sprite colour OR mask
$D7x8-$D7x9 - Enhanced sprite 2x2 linear transform matrix 0,0 (5.11 bits)
$D7xA-$D7xB - Enhanced sprite 2x2 linear transform matrix 0,1 (5.11 bits)
$D7xC-$D7xD - Enhanced sprite 2x2 linear transform matrix 1,0 (5.11 bits)
$D7xE-$D7xF - Enhanced sprite 2x2 linear transform matrix 1,1 (5.11 bits)
The attentive reader will note that nowhere does this address the 4KB data blocks for each sprite. This will be direct mapped in the 28-bit address space. I am tossing around the idea of over-mapping it with the 64KB colour RAM at $FF80000 (the first 1KB of which is also available at $D800 for C64 compatibility). The reason for this is that the 4KB sprite RAM will probably be write-only to simplify the data plumbing. However, to allow for freezing (and hence multi-tasking), I really do want some way to read the sprite data. The trade-off of course is that this means that you wouldn't be able to use all 64KB for colour RAM if it also being used as a proxy to the sprite RAM data.
Monday, 6 October 2014
Initial work on sprites
Last night I didn't sleep solidly, so I got up and did a bit more work on implementing VIC-II sprites in the C65GS's VIC-IV.
The focus here is on implementing "normal" C64/C128/C65 sprites for existing software. As such the focus is not on adding new functionality to these sprites, in particular allowing more colours or more than 8 sprites (although I am planning to relax the 21 pixel high limitation to allow taller sprites, and if all goes well, I may also allow wider sprites).
Along with the SID chip, it is the sprites that really made the C64 stand out from its competition in the early 1980s. Therefore it is important that I get them right, and so far as possible implement all required functionality. So let's just go over what the sprites are, and how they work on the VIC-II/VIC-III (they behave identically on the C64/128 VIC-II and C65 VIC-III).
Basically the sprites are bitmap objects that are drawn either on top or behind the background graphics in real-time as the frame is drawn raster by raster. This is done with dedicated hardware support in the VIC-II/III chips that allows the user to simply provide the X and Y coordinates at which to display each sprite, and a pointer to the start of the bitmap data. There are also some special flags to modify the priority of the sprites with regard to the rest of the display, so that they can appear "in front" or "behind" the main graphics -- and this can be controlled separately for each sprite. There is also hardware detection for sprite-to-sprite and sprite-to-foreground collision that can be used in games to detect when things touch. Altogether, this allows much more advanced games and graphics on the 1MHz CPU of a C64 compared to contemporary machines. The cost of this flexibility and power is that the sprites consume about 3/4 of the space in the VIC-II, however history has shown that this was a great investment.
Amongst the 8 sprites, they have a fixed priority with respect to one another, so that lower numbered sprites will always appear in front of higher numbered sprites. This can be easily implemented by creating a pipeline of 8 identical sprite blocks that draw over the output of the previous sprite.
There is some circumstantial evidence to suggest that this is exactly what the VIC-II/III does, as there is a 12 pixel latency in its video pipeline, and it is reasonable to suspect that 8 of those cycles are for the 8 sprite compositing stages. Also, by staging the sprites in a linear pipeline, it is easier to meet the timing requirements, because the sprite signals need only move to the next sprite in the pipe-line, instead of all having to be gathered together in some other way, for example, a tree structure, although this would be possible. This is especially relevant for the C65GS where the video dot clock is running at 192MHz, and so I have to keep the logic depth shallow, and avoid dependencies on distant signals.
This pipeline is what I have managed to get working at present, as can be seen in the following screen shot:
There are a couple of obvious things:
1. The red sprite is visible over the top border. This is because I don't have border masking active for sprites. This will be easy enough to do, but I will defer it until I have finished the rest of the work on the sprites, as it is convenient in the meantime to see the sprites wherever they are.
2. The sprites are showing a solid block of colour. This is because I haven't implemented the fetching of the bitmap data by the VIC-IV, and feeding it into the sprite pipeline (more on this in a moment).
There are also some things not working that you can't see right now, for example foreground/background priority, and the hardware collision detection stuff.
However, what is clear is that the sprites do work, and the synthesis results show that by using the pipelined approach I described above, the timing of the design in the FPGA is no worse than before. The sprites themselves are currently consuming about 5% of the entire FPGA, which is quite acceptable. The complete design is now consuming about 42% of the FPGA.
Now, back to feeding bitmap data into the sprite pipeline. As I mentioned earlier, at 192MHz it isn't actually possible to feed data into (or extract data out of) all 8 sprites in parallel, because the logic depth and physical distance on the FPGA die becomes too great.
To get around this, I have constructed a data delivery pipeline that allows the VIC-IV to feed bitmap data to any of the 8 sprites, and it is forwarded by each sprite to the following sprite. Thus in return for a latency of 8 cycles, we can deliver bitmap data to any sprite without messing up the timing closure of the design.
This allows the VIC-IV to feed data to the sprites, however, it needs to know what address to fetch the data from.
One of the rather strange tricks the VIC-II used to reduce the number of registers in the design, is that a few bytes at the end of screen RAM are used to hold the data pointers to the sprites. The Y position within each sprite is then multiplied by 3 and added to the base address from this pointer to work out which 3 bytes need to be fetched and buffered in each sprite.
On the VIC-IV, the sprites exist outside of the main design due to the timing issues described above. Thus there has to be a third data pipeline that allows the sprites to tell the VIC-IV the Y position they are currently drawing. The VIC-IV can then fetch the required bytes, and pass them through the data pipeline.
All of these extra paths are plumbed through the sprite pipeline, but a few important pieces are not finished, but hopefully I will be able to get to these things done in the not too distant future.
After that, it will be time to implement the VIC-IV enhanced sprites, for which I have a few ideas.
The focus here is on implementing "normal" C64/C128/C65 sprites for existing software. As such the focus is not on adding new functionality to these sprites, in particular allowing more colours or more than 8 sprites (although I am planning to relax the 21 pixel high limitation to allow taller sprites, and if all goes well, I may also allow wider sprites).
Along with the SID chip, it is the sprites that really made the C64 stand out from its competition in the early 1980s. Therefore it is important that I get them right, and so far as possible implement all required functionality. So let's just go over what the sprites are, and how they work on the VIC-II/VIC-III (they behave identically on the C64/128 VIC-II and C65 VIC-III).
Basically the sprites are bitmap objects that are drawn either on top or behind the background graphics in real-time as the frame is drawn raster by raster. This is done with dedicated hardware support in the VIC-II/III chips that allows the user to simply provide the X and Y coordinates at which to display each sprite, and a pointer to the start of the bitmap data. There are also some special flags to modify the priority of the sprites with regard to the rest of the display, so that they can appear "in front" or "behind" the main graphics -- and this can be controlled separately for each sprite. There is also hardware detection for sprite-to-sprite and sprite-to-foreground collision that can be used in games to detect when things touch. Altogether, this allows much more advanced games and graphics on the 1MHz CPU of a C64 compared to contemporary machines. The cost of this flexibility and power is that the sprites consume about 3/4 of the space in the VIC-II, however history has shown that this was a great investment.
Amongst the 8 sprites, they have a fixed priority with respect to one another, so that lower numbered sprites will always appear in front of higher numbered sprites. This can be easily implemented by creating a pipeline of 8 identical sprite blocks that draw over the output of the previous sprite.
There is some circumstantial evidence to suggest that this is exactly what the VIC-II/III does, as there is a 12 pixel latency in its video pipeline, and it is reasonable to suspect that 8 of those cycles are for the 8 sprite compositing stages. Also, by staging the sprites in a linear pipeline, it is easier to meet the timing requirements, because the sprite signals need only move to the next sprite in the pipe-line, instead of all having to be gathered together in some other way, for example, a tree structure, although this would be possible. This is especially relevant for the C65GS where the video dot clock is running at 192MHz, and so I have to keep the logic depth shallow, and avoid dependencies on distant signals.
This pipeline is what I have managed to get working at present, as can be seen in the following screen shot:
There are a couple of obvious things:
1. The red sprite is visible over the top border. This is because I don't have border masking active for sprites. This will be easy enough to do, but I will defer it until I have finished the rest of the work on the sprites, as it is convenient in the meantime to see the sprites wherever they are.
2. The sprites are showing a solid block of colour. This is because I haven't implemented the fetching of the bitmap data by the VIC-IV, and feeding it into the sprite pipeline (more on this in a moment).
There are also some things not working that you can't see right now, for example foreground/background priority, and the hardware collision detection stuff.
However, what is clear is that the sprites do work, and the synthesis results show that by using the pipelined approach I described above, the timing of the design in the FPGA is no worse than before. The sprites themselves are currently consuming about 5% of the entire FPGA, which is quite acceptable. The complete design is now consuming about 42% of the FPGA.
Now, back to feeding bitmap data into the sprite pipeline. As I mentioned earlier, at 192MHz it isn't actually possible to feed data into (or extract data out of) all 8 sprites in parallel, because the logic depth and physical distance on the FPGA die becomes too great.
To get around this, I have constructed a data delivery pipeline that allows the VIC-IV to feed bitmap data to any of the 8 sprites, and it is forwarded by each sprite to the following sprite. Thus in return for a latency of 8 cycles, we can deliver bitmap data to any sprite without messing up the timing closure of the design.
This allows the VIC-IV to feed data to the sprites, however, it needs to know what address to fetch the data from.
One of the rather strange tricks the VIC-II used to reduce the number of registers in the design, is that a few bytes at the end of screen RAM are used to hold the data pointers to the sprites. The Y position within each sprite is then multiplied by 3 and added to the base address from this pointer to work out which 3 bytes need to be fetched and buffered in each sprite.
On the VIC-IV, the sprites exist outside of the main design due to the timing issues described above. Thus there has to be a third data pipeline that allows the sprites to tell the VIC-IV the Y position they are currently drawing. The VIC-IV can then fetch the required bytes, and pass them through the data pipeline.
All of these extra paths are plumbed through the sprite pipeline, but a few important pieces are not finished, but hopefully I will be able to get to these things done in the not too distant future.
After that, it will be time to implement the VIC-IV enhanced sprites, for which I have a few ideas.
Friday, 19 September 2014
RR-NET emulation dramas
I have been working for the past couple of weeks on getting RR-NET ethernet emulation working so that existing ethernet-enabled programs for the C64 can use it.
This has been a fascinating process.
First, the 8-bit mode of the CS8900a chip that the RR-NET uses is a bit weird, as it still uses 16-bit registers, and so you have to write to the two halves of the registers in turn, among various oddities.
Once I figured out the semantics I managed to get packet receiving working relatively quickly. Packet transmission proved a little more frustrating, until I eventually removed the buggy code and wrote it again from scratch. This is often the case with software that it is faster to re-write something than it is to fully understand and fix it.
I wrote a little BASIC test program that just uses POKE to send an ethernet frame, and it works quite nicely.
However, I haven't had a fully positive response from any of the C64 network-enabled programs I could fine.
The Breadbox64 twitter client requires a supertweet account, and Twitter have disconnected supertweet, so no joy there.
udpslave runs, sees packets, and tries to send replies, but seems to mess up the checksum fields. I don't know whether this represents a bug in my RR-NET emulation, the C65GS more generally, or something else.
The Contiki webserver runs, but fails to actually service any request, even though it does respond to ARP, reply to PING (sometimes at least), and can see and parse HTTP requests given to it. It looks like there is something fishy going on with packet reception. I might need to improve buffer handling in my ethernet controller to stop packets getting stomped on while you are still reading them out. You can see what it does here:
The Contiki webserver and IRC client don't want to accept keyboard input on the C65GS for reasons I don't understand.
Most of the other ethernet-enabled software for the C64 requires either GEOS or a Final Replay or similar cartridge, neither of which are an option right now.
So I am at the frustrating point where it mostly works, and I can see very tantalising signs, but can't actually do anything useful with it.
The one program I have yet to test, but intend to, is the RR-NET version of 64NET/2. 64NET/2 is software that I wrote almost 20 years ago to interface C64s to PCs, and which was extended by some volunteers to support the RR-NET in place of a user port cable. The RR-NET version consists of a patched kernel, which will be easy enough to load on the C65GS, I just need to make a little program that can load a custom kernel and then restart the machine.
Friday, 12 September 2014
Remote keyboard access now works
It is now possible to connect to the C65GS via VNC, and type in the VNC window and havee it communicate to the C65GS automatically.
Let me explain that another way: with a VNC client you can now log into a C65 on the internet and play with it. It also happens to be the fastest C65 or C64 in existence.
See updated details in the previous post of how to connect, and play around with various things:
http://c65gs.blogspot.com/2014/09/remotely-view-and-interact-awkwardly.html
Already a few people have been trying it out, as the following screen shots show:
As I write this, someone is busily drawing some art, which began like:
Then:
And then:
And finally:
Part of the fun is that more than one person can connect at the same time, and all can type if they wish. Otherwise, you can just sit and watch other people use it.
Let me explain that another way: with a VNC client you can now log into a C65 on the internet and play with it. It also happens to be the fastest C65 or C64 in existence.
See updated details in the previous post of how to connect, and play around with various things:
http://c65gs.blogspot.com/2014/09/remotely-view-and-interact-awkwardly.html
Already a few people have been trying it out, as the following screen shots show:
As I write this, someone is busily drawing some art, which began like:
Then:
And then:
And finally:
Part of the fun is that more than one person can connect at the same time, and all can type if they wish. Otherwise, you can just sit and watch other people use it.
Subscribe to:
Posts (Atom)







