-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add multicolour support in VGA: Hardware and Emu #17
Comments
@MJoergen Thank you: 👍 This sounds awesome! 😃 It would be super-great, if you could implement this multicolor support in VGA! Additionally, it would be the cherry on the cake, if you could change the font ROM into a (preloaded) font RAM and add some register(s) to the VGA so that one can change the characters/font RAM. This would allow us to have (similar to a C64) character based graphics, which for example would allow me to program "Q-Chess" - a QNICE based visual chess program... ...something I planned to do since a while :-) EDIT: PS: It woul be cool, if we can avoid breaking existing application code by implementing your idea: two different palettes for foreground and background, where "0" selects green and black PS2: It would be even cooler, if you added some more additional VGA registers to be able to modify the standard palette: so that we have 16 foreground and 16 background colors (default, let's search for some nice 16 bit default palettes somewhere in the net and take those): BUT, the programmer can use these additional VGA registers to define his own palette from the 3 8-bit RGB values PS3: Just a side note: Please be aware that in |
@MJoergen As V1.6 might still take some weeks to complete, maybe you'd like to start with the VGA topic for V1.7? (But please including my above-mentioned comment :-). I think it is save to branch from EDIT: Depending on your preferred style of working, you might want to enhance the emulator's VGA capabilities first, before doing the hardware - and write some test programs in |
I'm a bit hesitant to start on this issue right now, with all the loose branches. I'd prefer making a new branch out from the develop branch, when the current pending changes (dev-io-addr and dev-int) have been merged into develop. Do you have an estimate for when you expect to update the develop branch (and remove dev-io-addr and dev-int)? EDIT: I'm quite keen on updating the VGA hardware part, and I have further ideas as well. But I might just have a look at the emulator as well - I'm always keen to learn new stuff! |
re Emulator and Hardware: Sounds good! Learning new stuff is fun! :-) Then I suggest that you do both: VGA Emulator Update plus VGA hardware, i.e. this issue is for the hardware part and for the emulator part. I renamed the issue accordingly. re Timing estimate: I will be on a 2,5 weeks vacation starting on August, 21st and I strive to have merged back |
Off-topic: Hey Michael, when I wrote the above mentioned comment about Q-Chess, I was absolutely not aware, that you did your own mchess and mchess2 engines already 👍 Today I stumbled over it. So here is a suggestion: In a a bit more distant future (winter, spring, summer next year) - for sure no hurry - you could port a heavily simplified version of mchess or mchess2 to QNICE. Or write a new one: "simple-mchess". Either using C (vbcc) or assembler. The latter one might be preferred as we do not have that much RAM as you know and it might also be a nice intellectual challenge to write a chess engine in pure assembler ;-) But hey - they were able to have chess engines on the C64 back in the days ;-) ... and I would then volunteer for the graphics part using sprites and/or character-based-graphics... |
That's an awesome idea! Nice challenge indeed to write a chess engine in assembler. I haven't done that before :-) |
Here are some more thoughts about color VGA: As we want to be able to do some nice graphics stuff, we need a fast way to access the color VGA's memory. I propose a set of registers in the VGA plus more smarts in the mmio_mux to enable the mapping of VGA memory into the address space of the CPU:
This is elegant and fast and does not need a MMU. Michael, your thoughts? |
Doesn't this collide with the more general MMU we are hoping to write one day ? I mean, this is essentially an MMU functionality, and when we get the "real" MMU then we should probably deprecate this VGA-MMU, so as not to have duplicate functionality. Alternative: How about we postpone this idea until we start on the "real" MMU? I see your suggestion as an optimization, and I see nothing wrong with using the existing approach with VGA_CHAR to access video memory. My current plan for multi color support will not significantly increase the usage of video memory. Instead, the value written to VGA_CHAR will contain BOTH color and character information, so the same number of writes as before. |
I agree with Michael - that is ideally implemented by means of a (simple) MMU. :-) I, too, would postpone this until we have decided on an MMU implementation. :-) |
(For now) I am OK with it :-) I was just pondering about text based graphics by changing the font in realtime (as on C64) and using the VGA_CHAR is quite slow... |
Michael, thank you for the great phone call today! And thank you for opening up the list of additional wishes :-) So here they are:
When it comes to the QNICE default palette, an inspiration might be taken from PCs VGA: (taken from https://www.fountainware.com/EXPL/vga_color_palettes.htm)*But for sure, when we google a bit, there might be even nicer ideas for a 16-color palette. As soon as you would like to brainstorm more about that: On Saturdays I am very flexible. The two of us can also make a GoToMeeting session in September (I am back on 10th) |
List of changes to hardware
Font RAM and Palette RAMI propose the following additional registers:
To access either the Font RAM or the Palette RAM, the CPU must first write the desired address and then either read or write the data. Both the Font RAM and the Palette RAM can be reset to the default Font and default Palette by setting bit 15 of the corresponding address register. This bit will automatically clear when the reset is complete. |
Sounds perfect 👍 |
Tested on hardware:
The command sequence:
changes the background colour to dark gray! |
I am stunned :-) This is amazing, particularly because I thought this would be something until Christmas 🎄 ... and now you did it already now ;-) Congratulations 🎉🍾🎊🎈 What default palette did you choose? Regarding my comment from the other day, I guess some testing around the Monitor's scrolling makes sense because your page amount is smaller... Your speed of progress is breathtaking 😀 |
Actually, I've preserved the 20 screen buffer. My previous comment about 9 screens was a calculation error. So there is 64k words of screen memory, just like before. |
Notes about graphics modeI propose two different graphics modes: Low-resolution and High-resolution. Low-resolutionThis will be 320x200 pixels, i.e. 64k pixels in total. Using the Display RAM of 64k words gives exactly one word (i.e. 16 bits) for each pixel. The low 12 bits could go straight to the RGB output, and the high 4 bits are leftover. They could e.g. be used for layering information (i.e. depth) when used together with sprites, see below. In the Low-resolution mode, the Font RAM and the Palette RAM are not used. High-resolutionThis will be 640x400 pixels. We now have just 4 bits per pixel, and this gives 16 different values. This value could then be used as index into the Palette RAM, giving a total of 16 colours for the entire screen. When raster line interrupt is implemented, then the Palette RAM could potentially be updated for every raster line, giving many more colours on the screen. But still a maximum of 16 different colours on any raster line. Memory Bandwidth consideration in graphics modeIn graphics mode (both high-resolution and low-resolution) I propose to add a new register to the VGA module:
To write to the Display RAM amounts to writing the address to the address register Basically, a loop around the instruction Optionally, we could re-use VGA$CR_X instead of using a new register. Furthermore, we could make the auto-increment configurable: For instance, the value in VGA$CR_Y can be the amount to auto-increment with. So a value of 0 disables auto-increment, while a value of 320 (or 640) can be used to draw vertical lines, and a value of 0xFFFF would do an auto-decrement. Perhaps this is not very useful, so the initial implementation could just hard-code the auto-increment value to 1. Regarding auto-increment, we (probably) need to fix the Issue #55 , where memory accesses tend to last several (consecutive) clock cycles rather than just one clock cycle. Finally, we need two bits in the Comments and suggestions are very much appreciated. |
Notes about spritesHere are some ideas I have about sprites. Resolution and colour depthI propose two different resolutions for each sprite: High-resolution and Low-resolution. Low-resolution (4 colours)In this mode, there are 2 bits per pixel, and the sprite is 8x8 pixels in size. The sprite bitmap takes up 8 words of memory. Each pixel has a colour value of 2 bits, which gives an integer value 0 - 3. This value is used as an index into a Sprite Palette consisting of 4 RGB colours (4 words of memory). High-resolution (2 colours)In this mode, there is 1 bit per pixel, and the sprite is 16x16 pixels in size. The sprite bitmap takes up 16 words of memory. Each pixel has a colour value of 1 bit, which gives an integer value 0 - 1. This value is used as an index into the Sprite Palette. Palette colours 2 and 3 are not used in this mode. TransparencyEach sprite can have transparency enabled or disabled. If transparency is enabled, then Palette Index 0 is treated as a transparent colour. MagnificationEach sprite can be magnified to twice the size in either X and/or Y direction. Layering / depthEach sprite has an associated depth that determines whether the sprite goes in front of or behind the text/graphics. For now I propose just a single bit indicating Front or Behind. Number of spritesThere is the issue of memory bandwidth (i.e. whether we can read all the sprite information needed for a given raster line), and the amount of memory needed, but I'm pretty confident we can implement 256 sprites in total. Memory usageEach sprite needs 16 words for bitmap information as well as 8 words for configuration/position/palette. With 256 sprites this makes a total of 6k words, which should fit into 4 BRAM's in the FPGA. Register Map for each sprite
Comments and suggestions are very much appreciated. |
Just a quick note to let you know how impressed I am by your work and the sprite implementation! :-) I am sorry that I cannot contribute here anything since I have never even used sprites (apart from a few experiments on my ZX Spectrum in the 1980s :-) ). I am looking forward to see them in action and am curious what demo Mirko will write using them. :-) |
Thank you for your great specification work. I think thanks to you, QNICE-FPGA will become a super fun to work with retro-graphics-machine :-) Here are some thoughts: Graphics Modes
Sprites
Default Palettes
Some hints a about the emulatorIndependent of my comments about gfx and sprites, here are some hints about how the VGA emulator works today to help you find your way through the code when enhancing it to support the new hardware capabilities.
|
Thank you for your comments, I really appreciate that! I have some additional feedback to your comments:
Thank you very much for your hints on the emulator! I've now got the VGA Multicolour text mode working in the emulator. |
Yes. The MEGA65 for example is having a VDAC and full 24bit RGB.
Sounds great. 👍
Cool - I guess I want to write a sidescroller Jump'n'Run 😃
Yes it would require double the RAM. For Artix 7 and newer no problem I guess. For older chips (in case I manage to get ISE up and running again): Maybe you can make a
Thank you - this is a very important topic. So we should have enough sprite RAM to be able to have animated sequences such as these: https://moose-stache.itch.io/rpg-asset-pack (and others ;-) )
IMHO 32x32 pixel with a lot of colors, such as 256 colors in a palette.
My proposal would be this palette, as it is an "optimum 16 color palette in terms of expressiveness" 😄 http://alumni.media.mit.edu/~wad/color/palette.html Selection Rationale In order to maximize the use of the color palette for classification, I started by selecting the colors "by name" using the eleven colors that Boynton says are almost never confused: black, gray, white, red, blue, green, yellow, orange, brown, purple, and pink. To these, I added another gray (giving four achromatic colors). The remaining four colors I selected (still only by name) as follows:
Here are the values: http://alumni.media.mit.edu/~wad/color/numbers.html We might want to have the names in For preserving the 100% retro feeling in Q-TRIS I would manually change one of the values to "100% green" as today :-) |
That's a nice palette. I've updated the design and emulator to use the new palette in 15-bit mode. |
@sy2002 @bernd-ulmann : I'm looking into adding scan line interrupt, and I don't know how to implement that in the emulator. As far as I understand the emulator, a separate thread is displaying the entire screen at a time, at a roughly constant FPS. With the 640x480x60 resolution, the scanline gets incremented at a frequency of 31.5 kHz, i.e. every 31 microseconds. Ideally, I would like two registers:
But the question is whether the concept of "current scan line" even makes any sense in the context of the emulator, and if not, how to "emulate" that behaviour. I'm a bit low on good ideas right now ... |
That is a very good question. The VGA-emulation was done by Mirko, so we might have to ask him. :-) Issuing an interrupt in the emulator is simple as its basically just setting to global (argl) variables as in timer.c, but I do not know of a line-interrupt concept fits into the VGA implementation... |
One more thing about sprites: We need collision detection as well. I presume we want to have detection of both sprite-sprite collision as well as sprite-text collision. I'm not sure what a good register map interface would be. Here is my initial suggestion for sprite collision detection register map.
The above register map will ignore three sprites colliding, as well as ignore multiple simultaneous collisions. In both cases, only a single collision is detected. Perhaps we could include a collision mask bit for each sprite to indicate whether this particular sprite should be included in any sprite collisions. Ideas and feedback are as always most welcome. |
On the Emulator
Indeed this is quite a challenge, because currently, the emulator is not cycle accurate when it comes to the CPU and it also does not have any concept of a scan line: The video memory is written "as you go" (when writing the registers) and then the whole memory is copied using streaming textures. I suggest, that you implement the scan line interrupt in hardware and add the larger task - something like "turn the emulator into a cycle accurate system including VGA" - to About the hardware implementation: I think your ideas about the registers On Sprite collisionsThank you for bringing this up! You are right and this is an important topic. I am currently not fully aware, how you want to implement sprites. Are we working with a color palette / transparacy layers? How many colors (I opt for many ;-)). Anyway: Your above-mentioned ideas a great. Here are some more thoughts. I think, that we might want to consider the concept of sprite groups (just as the Amiga had it). Reason: It happens quite often, that you are using multiple (animated) sprites to form "the player's character" (e.g. a space-ship in a shoot-em-up). This is then considered as one sprite group. Some ideas from the Amiga here: http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node015A.html |
Pixel scrolling is now implemented in hardware (not yet emulator). Try out on hardware using the file Next thing up is the scan line interrupt! |
Ok, just a quick calculation on the memory requirements. Total number of bits needed would be: This corresponds to 64 BRAMs. I don't think we have that many free BRAMs remaining, I need to check. Anyway, if we add double-buffering of the DIsplay RAM, then we most surely don't have the memory needed. So I think we should prioritize. Perhaps reduce number of sprites to 128 ? |
I think 128 sprites or even 64 would be absolutely OK. Can you make all these amounts (and maybe also other constants that are responsible for eating a lot of memory) configurable in EDIT: Thinking about all these constants and porting: We might need a general "system info" register somewhere, where the software can get info about all these constants (amount of register banks (256 by default) amount of sprites (128 by default) amount of pages in scroll back buffer (20 by default) and so on). I will make a new issue for that. |
@MJoergen about your above mentioned #17 (comment) section "Register Map for each sprite": Can you add horizontal and vertical mirroring of the sprite? Reason: I just bought some sprite assets for a vertical space shooter, since I am contemplating to write a very simple space shooter as a demo for your new VGA capabilities. The movement of the ship is only available for "moving left", so it is up to the programmer (or the hardware) to do the mirroring: Frame 1 Frame 2 Frame 3 Frames 1,2,3 are for moving the ship to the left. Horizontally mirroring them will allow movements to the right. |
Done! |
Basic sprites are now implemented. Only thing left is to add the remaining functionality described in the sprite's CSR register. However, the topic of layering (background/foreground) is causing some problems in my thinking, particularly striking a compromise between features we want to support, and the resources (e.g. BRAM's in the FPGA) needed to implement them. Consider a few different cases: Case 1.
This will display a RED color, because the sprite is in front of the text. Case 2.
This will display GREEN text on RED background, because the sprite is behind the text but in front of the background. The above two cases deal with the interaction between TEXT and SPRITE, and describes the basic motivation for this feature, where e..g a tree is rendered using text, and a sprite can move either in-font-of or behind the tree. With multiple overlapping sprites things get more complicated: Case 3.
This will display a RED color, because sprite 0 is always in front of sprite 1. Case 4.
This will again display a RED color, because sprite 0 is in-front-of the text. Case 5.
This will display GREEN text on RED background, again because sprite 0 is in front of sprite 1. The tricky part is the following: Case 6.
What will this display? I see at least two possible interpretations:
Option After some more thinking, I start to wonder whether this situation (Case 6) is ever likely to occur. Essentially, this situation is trying to describe an impossible situation where sprite 0 is both in-front-of sprite 1 (because lower numbered sprites always have higher priority) AND is behind sprite 1 (because of the configuration in the sprite's CSR registers). So one way around this paradox is this option: This introduces an artificial boundary at sprite number 64, and prevents having all 128 sprites in front of the text. To get around this limitation, we might propose this solution: So this brings up the question of do we need such a boundary register? Or do we already have "enough" sprites? Thoughts, comments, ides, suggestions, etc are - as always - highly appreciated. |
What I have seen so far is absolutely great! So congrats for what has already being achieved! Two side questions as I am currently admitably only following the progress from a certain eagle's perspective (I did not try anything yet, by myself):
Feedback to the situation you describe:
I like option (b) a lot, as it will lead to a clean implementation that saves resources. This slight "non-intuitiveness" for the programmer is fully acceptable for me: We just need to document it somewhere and then all will be good. The overall idea of having the sprite number being an implicit prioritization is something that also my beloved retro-computers very often did exactly like this - so we are in good company! 😄 So my suggestion is clear: Go for the clean resource friedly option (b). And: We do need to have 128 sprites because of animation: Consider we wanted a rotating globe in |
Yes, that is already implemented. I don't have a demo of it though. Maybe I should do that .....
No, that is not done yet.
Even though it is expensive, software can update the sprite bitmap in real-time, i.e. copy the 256 words needed for a 32x32x4 sprite bitmap. I'm still contemplating whether to implement the auto-increment feature. Let me explain: Currently, to access the Sprite RAM, software must first write the address to |
Fully understood. Highly appreciated and I am a big fan of this idea because it boosts performance. You would need to well-document this and even have a small "best practices for sprite performance" section in our best practices programming guide. |
As of today, (nearly) all sprite-related features are implemented in both hardware and emulator. Have a look at the following demos:
Features still not implemented are:
I've looked into sprite magnification, and within the current design adding this feature will cost an additional 16 BRAMs. This is due to how sprites are rendered within the VGA hardware module. So, is this feature very important? If not, I suggest we drop this feature. I've also briefly looked into sprite collision, and this seems like quite a complicated feature. This will require some more thought. Finally, there is a task of optimizing the emulator. Currently, no such effort has been made; the emulator re-draws the entire screen every time a sprite is updated. |
Hi Michael - this is incredibly great news! I will be able to play with it and test it probably not before Saturday - but I am already today really looking forward to it! 🚀 👍 🎆 😃
Let's drop this feature. The price of 16 BRAMs is too high.
Don't worry. Take your time. We are not at all in a hurry and your speed is already now breathtaking :-)
About Sprites: Yes, might make sense to optimize. And also about font gfx: Let's not forget issue #103.
Did you implement it? I think it is an excellent idea. |
Auto-increment is not yet implemented, but I will do it soon. However, if you use the library functions in sprite.c, you won't know the difference, other than better performance. My intention is that a user will only ever use the library functions, and not directly manipulate the hardware registers. |
The auto-increment is implemented now. |
The only thing left in this issue is to implement the bitmap graphics. I'm assuming @sy2002 wants to do this at some time, so I've assigned it to him :-) |
:-) Thank you. If you are OK with a timeline like "after Christmas", then this sounds great for me ;-) |
Changes to programming model:
The print character at VGA$CHAR will be extended to read/write a full 16-bit value instead of as today an 8-bit value. The lower 8 bits will remain the same as before, i.e. the ASCII character code. The upper 8 bits will be divided into two groups of 4 bits as follows:
In other words, each character may have a separate colour, but no more than 16 (or possibly 32) different colours on the screen in total.
The clear screen function will reset all characters to 0x20 (space) and will assign a default foreground and background colour to each character.
The cursor blink mode will swap foreground and background colour of the character.
Changes to VHDL code:
The video_bram module will have 16-bit data bus instead of 8-bit. This will double the amount of video RAM needed, going from 32 Block RAMs to 64 Block RAMs. Minor changes in vga_textmode.vhd. Most of the changes will be in the vga80x40.vhd module.
Changes to application code:
As written, all application code will break, because the value written to VGA$CHAR must now contain colour information as well in the upper 8 bits. One possible way to avoid this break, is to have different palettes for foreground and background. In other words, selecting both foreground and background colour as 0 would lead to the current behaviour of green on black.
The text was updated successfully, but these errors were encountered: