Skip to content
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

Open
MJoergen opened this issue Jul 30, 2020 · 42 comments
Open

Add multicolour support in VGA: Hardware and Emu #17

MJoergen opened this issue Jul 30, 2020 · 42 comments
Assignees
Labels

Comments

@MJoergen
Copy link
Collaborator

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:

  • Bits 15-12 background colour, selected from a fixed palette of 16 different colours.
  • Bits 11-8 foreground colour, selected from a (possibly different) fixed palette of 16 different colours.
    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.

@sy2002
Copy link
Owner

sy2002 commented Jul 31, 2020

@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 vga80x40.vhd the HDMI data enable is necessary for the MEGA65 version of QNICE (please see also https://github.com/sy2002/QNICE-FPGA/blob/develop/doc/README.md#hdmi) .

@sy2002
Copy link
Owner

sy2002 commented Aug 5, 2020

@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 dev-io-addr and do the VGA part there, because we will not touch the VGA part during the rest of the V1.6 release work.

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 test_programs for it. Or you do it the other way round. Is enhancing the emulator something, you'd like to do, or is C and SDL not that much your thing? (Then, I could do it, but then I would suggest you do the hardware, first and I do the emulator later during V1.7)

@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 6, 2020

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!

@sy2002
Copy link
Owner

sy2002 commented Aug 6, 2020

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 dev-int and dev-io-addr into develop until that point. I guess the release V1.6 will not be ready until then, because of the loose ends at the MEGA65 part, but at least we would have a good and semi-stable develop branch and the loose ends in the other branches would be gone. That could be a great starting point for your dev-color-VGA branch.

@sy2002 sy2002 changed the title Add multicolour support in VGA Add multicolour support in VGA: Hardware and Emu Aug 6, 2020
@sy2002
Copy link
Owner

sy2002 commented Aug 10, 2020

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...

@MJoergen
Copy link
Collaborator Author

That's an awesome idea! Nice challenge indeed to write a chess engine in assembler. I haven't done that before :-)

@sy2002
Copy link
Owner

sy2002 commented Aug 19, 2020

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:

VGA$STATE           .EQU 0xFF30 ; VGA status register <== enhance existing register
    ; Bit 12: Enable/disable memory mapping: 0=default=disabled, 1=enabled
    ; .... other bits stay as they are today
VGA$MM_VGA          .EQU 0xFF39 ; Start address of memory map: VGAs memory
VGA$MM_CPU          .EQU 0xFF3A ; Start address of memory map: QNICEs memory
VGA$MM_SIZE         .EQU 0xFF3B ; Size of the region that shall be mapped
  • So for example setting VGA$MM_VGA = 0 and VGA$MM_CPU = 0 and VGA$MM_SIZE = 32768 would remove the Monitor ROM from the CPU's address space and instead show the first 32k of the VGA RAM.
  • If you still need "the operating system", then you could do VGA$MM_CPU = 0xE000 and VGA$MM_SIZE = 0x1000 and still enjoy 4096 words of fast VGA memory access using the "upper memory area", and still have enough space for stack.
  • Obviously, a programmer would switch this on/off using bit 12 a lot, so it would be non-breaking and non-intrusive.
  • Interrupts would need to be paused during this kind of memory-mapping switches, because e.g. a timer interrupt might rely on certain Monitor functions, etc.: So we might need a CLI / STI instruction. Or in a step 1 - without CLI / STI - we would just say: If you want fast graphics, then programm your ISR's carefully ;-)

This is elegant and fast and does not need a MMU.

Michael, your thoughts?

@MJoergen
Copy link
Collaborator Author

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.

@bernd-ulmann
Copy link
Collaborator

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. :-)

@sy2002
Copy link
Owner

sy2002 commented Aug 19, 2020

(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...

@sy2002
Copy link
Owner

sy2002 commented Aug 20, 2020

Michael, thank you for the great phone call today! And thank you for opening up the list of additional wishes :-) So here they are:

  • True bitmapped graphics mode: 320x200 pixels, either 8bit index into palette or 16bits that describe the RGB colors
  • You told me that you might want to experiment with higher textmode resolutions than 80x40 characters in greater than 640x480 pixels
  • Sprites (in conjunction with the above mentioned "textmode-graphics" using fonts as "tiles"

When it comes to the QNICE default palette, an inspiration might be taken from PCs VGA:

grafik

(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)

@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 25, 2020

List of changes to hardware

Font RAM and Palette RAM

I propose the following additional registers:

  • 0xC : Font RAM address (bit 15: Reset. Bits 9-0: Address)
  • 0xD : Font RAM data
  • 0xE : Palette RAM address (bit 15: Reset. Bits 4-0: Address)
  • 0xF : Palette RAM data

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.

@sy2002
Copy link
Owner

sy2002 commented Aug 26, 2020

Sounds perfect 👍

@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 26, 2020

Tested on hardware:

  • The Q-Tris program runs without problems
  • Playing around in the Monitor, e.g. M C FF3F 00FF. This changes the text colour to cyan.

The command sequence:

M C FF3E 0010
M C FF3F 0444

changes the background colour to dark gray!

@sy2002
Copy link
Owner

sy2002 commented Aug 26, 2020

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 😀

@MJoergen
Copy link
Collaborator Author

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.

@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 27, 2020

Notes about graphics mode

I propose two different graphics modes: Low-resolution and High-resolution.

Low-resolution

This 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-resolution

This 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 mode

In graphics mode (both high-resolution and low-resolution) I propose to add a new register to the VGA module:

VGA$ADDRESS   .EQU 0xFF39

To write to the Display RAM amounts to writing the address to the address register VGA$ADDRESS and then writing the value to the data register VGA$CHAR, but with the additional feature: Each write (and read) to the data register will automatically increment the value of the address register. This auto increment will make copying data to the Display RAM just as fast as a regular memcpy().

Basically, a loop around the instruction MOVE @R0++, @R1, where R1=VGA$CHAR can be used to copy large amounts of data into the Display RAM, provided the data is placed sequentially. With this auto-increment feature I think we can postpone the MMU, and can make great graphics very soon!

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 VGA$STATE register to enable the two graphics modes.

Comments and suggestions are very much appreciated.

@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 27, 2020

Notes about sprites

Here are some ideas I have about sprites.

Resolution and colour depth

I 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.

Transparency

Each sprite can have transparency enabled or disabled. If transparency is enabled, then Palette Index 0 is treated as a transparent colour.

Magnification

Each sprite can be magnified to twice the size in either X and/or Y direction.

Layering / depth

Each 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 sprites

There 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 usage

Each 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

reg 0 : X position
reg 1 : Y position
reg 2 : Control (see below)
reg 3 : Pointer to sprite bitmap
reg 4 : Palette colour 0
reg 5 : Palette colour 1
reg 6 : Palette colour 2
reg 7 : Palette colour 3

Control register decoding:
Bit 0 : Resolution (0 = High-resolution, 1 = Low-resolution)
Bit 1 : Transparency enabled
Bit 2 : Magnify X
Bit 3 : Magnify Y
Bit 4 : Mirror X
Bit 5 : Mirror Y
Bit 6 : Depth (0 = Front, 1 = Behind)

Comments and suggestions are very much appreciated.

@bernd-ulmann
Copy link
Collaborator

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. :-)

@sy2002
Copy link
Owner

sy2002 commented Aug 27, 2020

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

  • Low res should use all 16 bits for color information in a RGB=5:6:5 bit pattern: The human eye is most sensitive for the green channel, so let us have 6 bits for green and 5 bits each for red and blue. More details on that scheme can be found here: https://en.wikipedia.org/wiki/High_color

  • I love your idea about this auto-increment mechanism that is saving us memory bandwidth and I would prefer your second thought, where you are reusing the VGA$CR_X and VGA$CR_Y registers. The idea of multiple step-witdths is great and can be (ab)used for nice graphics effects - and for vertical lines. By default, when entering the graphics mode, VGA$CR_Y should be set to 1, so that when the programmer does not care, he can expect that the increment is 1.

  • Smooth scrolling (maybe using the "8 pixel trick" similar to C64? Or even just by having more than 64kWords of VRAM?)

  • Flicker free animations by having the chance to do double buffering

Sprites

  • 256 sprites would be amazing! :-)

  • We need to think about animated sprites, i.e. have pointers to sprite bitmaps where the pointers can be quickly flipped to a new bitmap (during vertical retrace) so that we have smooth animations. For example: Look at this Mario Animation (scroll down a bit) which is made by three sprites in each direction

  • We need larger sprites. Consider that the 8x8 sprites are smaller than a single character on the screen in the high-res mode. You'd need a lot of sprites to have something nice looking on the screen. The C64 already had 24x21 sprites, the Amiga had 16xunlimited-height.

Default Palettes

  • Did you already choose default palettes for the color textmode and for high-res? If yes, which ones?

Some hints a about the emulator

Independent 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.

  • It makes sense to re-read emulator/README.md including the technical part described there to refresh the knowledge:

  • Everything you need for the purpose of enhancing the emulator to support your new hardware features is happening in vga.c. It might make sense to add the new textmode capabilities first, because you can leverage the existing textmode rendering.

  • The emulated VGA screen is an OpenGL streaming texture: A pixel buffer in main memory that represents the screen is repeatedly copied ("streamed") into a texture buffer in the GPU's RAM and from there copied to the screen. void vga_one_iteration_screen() in vga.c shows this mechanism.

  • For maximizing the VGA screen's performance, the pixel buffer is modified one character at a time in contrast to re-rendering it for each frame. This corresponds to the way how QNICE-FPGA's VGA hardware works: Also there, you can always only modify one character at a time in VRAM, because the VRAM is not mapped to QNICE-FPGA's RAM, but only accessible via memory mapped registers. void vga_render_to_pixelbuffer(...) in vga.c is doing the job of modifing the pixel buffer in the array screen_pixels.

  • As we are not having an MMU right now, the basic principle described above can be applied also for the newly enhanced text mode as well as for the low-res and high-res graphics mode: For maximizing VGA screen's performance, all pixel buffer modifications are done one write access at a time.

  • In native mode (on macOS, Linux), there are two threads: The CPU thread that writes the registers via void vga_write_register(...)and the display thread, that renders the streaming texture on the screen at (a maximum of) 60 FPS void vga_one_iteration_screen().

  • Also take notice of this harmless race condition (but no need to act on it): https://github.com/sy2002/QNICE-FPGA/blob/master/emulator/vga.c#L7

Repository owner locked and limited conversation to collaborators Aug 27, 2020
Repository owner unlocked this conversation Aug 27, 2020
@MJoergen
Copy link
Collaborator Author

MJoergen commented Aug 28, 2020

Thank you for your comments, I really appreciate that!

I have some additional feedback to your comments:

  • 16-bit colour: I was only considering the Nexys4DDR, which only has 12 bits of colour, but I guess we should be prepared for other platforms that have more colour bits (MEGA65?).
    However, I still think we should reserve 1 bit for layering. This is because I'm thinking about a background image showing a tree, and a sprite that moves behind the tree. In that case, some parts of the graphic is in front of the sprite, while other parts of the graphic is behind the sprite. So what do you say to this idea: 1 bit for layering, and 15 bits for colour (only 12 bits shown on the Nexys4DDR board)?

  • Smooth scrolling will be implemented in both text mode and graphics mode. I've called it "Pixel scrolling" in the comment above!

  • Double buffering. Wouldn't this require doubling the amount of Display RAM ? I was under the impression that Raster Interrupt would reduce the need for double buffering. Anyway, at the moment we have plenty of Block RAM in the Nexys4DDR, so it is certainly possible, unless we want to use that memory for something else.

  • Animated sprites: Good point, I hadn't thought about that. I've updated the Sprite Register Map to include "reg 3 : Pointer to sprite bitmap".

  • Larger sprites: Ok, could you give some suggestions (wish list) for sizes/dimensions of sprites? I don't think larger sprites are more difficult to implement, they just use more memory for the bitmaps and/or palettes. Would you like more colour bits per pixel as well, e.g. 4 bits per pixel to select one of 16 colours from a palette ? I don't have much experience with making graphics, so I'm not sure what is most useful, unless we just implement support for everything!

  • Palette: So far, I've just chosen eight pure RGB colours (e.g. 255, 255, 0) as well as eight lighter colours, e.g (255, 255, 128). I really have no idea what palette to choose, so feel free to come with something better (anything!). The default colours are defined in the file vhdl/vga/palette.txt.

Thank you very much for your hints on the emulator! I've now got the VGA Multicolour text mode working in the emulator.

@sy2002
Copy link
Owner

sy2002 commented Aug 29, 2020

16-bit colour: I was only considering the Nexys4DDR, which only has 12 bits of colour, but I guess we should be prepared for other platforms that have more colour bits (MEGA65?)

Yes. The MEGA65 for example is having a VDAC and full 24bit RGB.

So what do you say to this idea: 1 bit for layering, and 15 bits for colour (only 12 bits shown on the Nexys4DDR board)?

Sounds great. 👍

Smooth scrolling will be implemented in both text mode and graphics mode. I've called it "Pixel scrolling"

Cool - I guess I want to write a sidescroller Jump'n'Run 😃

Double buffering. Wouldn't this require doubling the amount of Display RAM ?

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 generic DOUBLE_VRAM : boolean somewhere where we can switch it on/off before synthesizing? Could be a constant that we nurture in env1_globals.vhd. Double Buffering is needed for flicker free animation, e.g. if we would do textmode graphics (using font as graphic elements).

Animated sprites: Good point, I hadn't thought about that. I've updated the Sprite Register Map to include "reg 3 : Pointer to sprite bitmap".

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 ;-) )

Larger sprites: Ok, could you give some suggestions (wish list) for sizes/dimensions of sprites?

IMHO 32x32 pixel with a lot of colors, such as 256 colors in a palette.

Palette: So far, I've just chosen eight pure RGB colours (e.g. 255, 255, 0) as well as eight lighter colours, e.g (255, 255, 128). I really have no idea what palette to choose, so feel free to come with something better (anything!). The default colours are defined in the file vhdl/vga/palette.txt.

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

grafik


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:

  • To provide for better skin tones, and also to provide another very desaturated color with a different hue than pink, I added Tan.
  • In order to provide a range of values, I added a light blue and a light green (pink already provides a very light red.)
  • The remaining color I assigned to cyan, mainly cause I like blue-greens.
  • When it came to actually assigning colors to names, I attempted to minimize saturation differences between colors. Hue is well sampled, at roughly eight points, and lightness has five values (the two extremes are achromatic.)

Here are the values:

http://alumni.media.mit.edu/~wad/color/numbers.html

We might want to have the names in sysdef.asm for convenient usage.

For preserving the 100% retro feeling in Q-TRIS I would manually change one of the values to "100% green" as today :-)

@MJoergen
Copy link
Collaborator Author

That's a nice palette. I've updated the design and emulator to use the new palette in 15-bit mode.

@MJoergen
Copy link
Collaborator Author

@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:

  • FF3A : R/W : Scan line to generate interrupt on.
  • FF3B : R/O : Current scan line being displayed by the VGA module.

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 ...

@bernd-ulmann
Copy link
Collaborator

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...

@MJoergen
Copy link
Collaborator Author

MJoergen commented Sep 1, 2020

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.

  • An overall single bit indicating ANY sprite-sprite or sprite-text collision. Or perhaps two separate bits for each occasion.
  • A single 16-bit register, where bits 15-8 contain the index of one of the sprites involved in a sprite-sprite collision, and bits 7-0 contain the index of the other sprite involved in the same collision. Again, maybe two registers, one for sprite-sprite collision, and the other for sprite-text collision.

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.

@sy2002
Copy link
Owner

sy2002 commented Sep 1, 2020

On the Emulator

I'm looking into adding scan line interrupt, and I don't know how to implement that in 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 TODO.txt.

About the hardware implementation: I think your ideas about the registers FF3A for scan line to generate interrupt on and FF3B for current scan line being displayed by the VGA module are absolutely perfect.

On Sprite collisions

Thank 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

@MJoergen
Copy link
Collaborator Author

MJoergen commented Sep 1, 2020

Pixel scrolling is now implemented in hardware (not yet emulator). Try out on hardware using the file vga_scroll.c.

Next thing up is the scan line interrupt!

@MJoergen
Copy link
Collaborator Author

MJoergen commented Sep 2, 2020

256 sprites would be amazing! :-)
IMHO 32x32 pixel with a lot of colors, such as 256 colors in a palette.

Ok, just a quick calculation on the memory requirements. Total number of bits needed would be:
256 * (32*32) * 8 = 2 Mbit.

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 ?

@sy2002
Copy link
Owner

sy2002 commented Sep 2, 2020

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 env1_globals.vhd? My rationale behind it is, that we might want to keep supporting "smaller" FPGAs than the Artix-7. I plan for example to port QNICE-FPGA to the DE-10 board some day, because it is a cheap starter kit which might inrease our fan-base at universities and the likes.

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.

@sy2002
Copy link
Owner

sy2002 commented Sep 3, 2020

@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

grafik

Frame 2

grafik

Frame 3

grafik

Frames 1,2,3 are for moving the ship to the left. Horizontally mirroring them will allow movements to the right.

@MJoergen
Copy link
Collaborator Author

MJoergen commented Sep 4, 2020

Can you add horizontal and vertical mirroring of the sprite?

Done!

@MJoergen
Copy link
Collaborator Author

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.

TEXT        (GREEN on BLACK)
SPRITE 0    (RED. configured as IN-FRONT-OF)

This will display a RED color, because the sprite is in front of the text.

Case 2.

SPRITE 0    (RED. configured as BEHIND)
TEXT        (GREEN on BLACK)

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.

TEXT        (GREEN on BLACK)
SPRITE 1    (BLUE. configured as IN-FRONT-OF)
SPRITE 0    (RED. configured as IN-FRONT-OF)

This will display a RED color, because sprite 0 is always in front of sprite 1.

Case 4.

SPRITE 1    (BLUE. configured as BEHIND)
TEXT        (GREEN on BLACK)
SPRITE 0    (RED. configured as IN-FRONT-OF)

This will again display a RED color, because sprite 0 is in-front-of the text.

Case 5.

SPRITE 1    (BLUE. configured as BEHIND)
SPRITE 0    (RED. configured as BEHIND)
TEXT        (GREEN on BLACK)

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.

SPRITE 0    (RED. configured as BEHIND)
TEXT        (GREEN on BLACK)
SPRITE 1    (BLUE. configured as IN-FRONT-OF)

What will this display? I see at least two possible interpretations:

(a) Just BLUE color, because sprite 1 is in front of the text.
(b) GREEN text on RED background, because sprite 0 takes precedence over sprite 1.

Option (a) sounds reasonable, but is expensive to implement (uses more BRAM). Option (b) is cheaper and easier to implement, but is perhaps not intuitive.

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:
(c) Define all low-numbered sprites (e.g. 0-63) as being IN-FRONT-OF text, and all high-numbered sprites (e.g. 64-127) as being BEHIND text.

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:
(d) Same as option (c), but with a global boundary register, instead of hardcoding the value 64.

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.

@sy2002
Copy link
Owner

sy2002 commented Sep 24, 2020

Basic sprites are now implemented. Only thing left is to add the remaining functionality described in the sprite's CSR register.

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):

  • Are we already able to animate sprites, i.e. change the "pointer" to a sprite in realtime, so that while a sprite moves, it is also animated?
  • Do we also already have the 16x16 HiColor sprites?

Feedback to the situation you describe:

Option (b) is cheaper and easier to implement, but is perhaps not intuitive.

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 world.c: We already need 16 sprites per frame of the rotation... So already today, such a rotating "large sprite" could only have 8 animation frames and then we have zero sprites left.

@MJoergen
Copy link
Collaborator Author

Are we already able to animate sprites, i.e. change the "pointer" to a sprite in realtime, so that while a sprite moves, it is also animated?

Yes, that is already implemented. I don't have a demo of it though. Maybe I should do that .....

Do we also already have the 16x16 HiColor sprites?

No, that is not done yet.

So already today, such a rotating "large sprite" could only have 8 animation frames and then we have zero sprites left.

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 VGA$SPRITE_ADDR and then write the value to VGA$SPRITE_DATA. Transferring 256 words to sprite bitmap takes a long time using this method, so I am thinking about having the VGA$SPRITE_ADDR register auto-increment on every access (read or write) to the VGA$SPRITE_DATA register. This will greatly reduce the time needed to transfer a sprite bitmap. I don't want this to be configurable, as there is no need for the added complexity. Just always auto-increment. This is completely backwards compatible, since all the demo programs so far always write the address register before accessing the data register.

@sy2002
Copy link
Owner

sy2002 commented Sep 24, 2020

I'm still contemplating whether to implement the auto-increment feature.

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.

@MJoergen
Copy link
Collaborator Author

As of today, (nearly) all sprite-related features are implemented in both hardware and emulator. Have a look at the following demos:

  • c/test_programs/demo_atom.c: Displays an animated atom. Demonstrates using bitmap pointer update.
  • c/test_programs/demo_tree.c: Displays a tree. Demonstrates low-resolution Hi-color sprites.
  • c/test_programs/vga_sprite_layering.c: Displays various combinations of layering between sprites and text.

Features still not implemented are:

  • sprite magnification
  • sprite collision

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.

@sy2002
Copy link
Owner

sy2002 commented Sep 29, 2020

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! 🚀 👍 🎆 😃

I've looked into sprite magnification, and within the current design adding this feature will cost an additional 16 BRAMs.

Let's drop this feature. The price of 16 BRAMs is too high.

I've also briefly looked into sprite collision, and this seems like quite a complicated feature. This will require some more thought.

Don't worry. Take your time. We are not at all in a hurry and your speed is already now breathtaking :-)

Finally, there is a task of optimizing the emulator.

About Sprites: Yes, might make sense to optimize. And also about font gfx: Let's not forget issue #103.

I'm still contemplating whether to implement the auto-increment feature.

Did you implement it? I think it is an excellent idea.

@MJoergen
Copy link
Collaborator Author

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.

@MJoergen
Copy link
Collaborator Author

The auto-increment is implemented now.

@MJoergen
Copy link
Collaborator Author

MJoergen commented Oct 3, 2020

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 :-)

@sy2002
Copy link
Owner

sy2002 commented Oct 3, 2020

:-) Thank you. If you are OK with a timeline like "after Christmas", then this sounds great for me ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants