sy2002 / QNICE-FPGA

QNICE-FPGA is a 16-bit computer system for recreational programming built as a fully-fledged System-on-a-Chip in portable VHDL.
http://qnice-fpga.com
Other
69 stars 15 forks source link

VGA: Add fractional pixel scaling #187

Open MJoergen opened 3 years ago

MJoergen commented 3 years ago

This issue is about enabling fractional scaling of the display. The idea is to allow easy implementation of almost-3D games like Wolfenstein or to make smooth 3D text scrolling like in the Star Wars intro! This is for you @sy2002 !!

The idea is to add two new registers that contain the ratio of the pixel width in the screen buffer versus the pixel width on the monitor display, i.e.

VGA$PIXEL_X_SCALE
VGA$PIXEL_Y_SCALE

Each register value contains the size of a screen buffer pixel in units of monitor display pixels, scaled by a constant value of 256. So the default value is 0x0100, corresponding to a scaling factor of 1. The value 0x0080 would mean only half the screen buffer is used to fill the entire monitor display. In other words, this leads to a magnification by a factor of 2.

The implementation could be as easy as expanding the module vga_pixel_counters and adding another set of pixel counter outputs. So one set gives the coordinates on the monitor display (and counts by 1 every clock cycle), and the other set gives the coordinates on the screen buffer (and counts by VGA_PIXEL_X_SCALE/256 every clock cycle). The outputs could be called monitor_pixel_* and buffer_pixel_*, respectively. Then the monitor_pixel_* signals are fed into the vga_sync module, while the buffer_pixel_* signals are fed into vga_text_mode and vga_sprite etc.

This issue includes writing a cool demo!

sy2002 commented 3 years ago

@MJoergen Great job! I could not resist to quickly trying your latest commit in branch dev-vga-fractional on real hardware: As you wrote in your email, the whole thing was a bit slow and jerky. But then I removed the line that syncs with the 60 Hz of the screen:

    //while ((y = MMIO(VGA_SCAN_LINE)) >= 480)

And then everything starts to look totally smooth and pretty. I made a small video. Download, unpack and look at this:

IMG_4995.MOV.zip

Now it is a bit too fast.

The solution: I think what we (reads "you?" :-)) might want to add to the monitor (misc library) and then also via qmon.h to C is a simple and synchroneous delay function in microseconds.

Simple and synchroneous means that we do not need the timer interrupt for that. Since we have switched on the cycle-counter be default, it is as easy as counting some cycles (and making sure that we cover wrap arounds). The counters are 48bit or so, so that wrap arounds would only happen every ~65 days if I calculated correctly ;-)

And then we could do a qmon_delay(100000) (which is 0,1 seconds in microseconds) as the function would be defined like qmon_delay(unsigned long microseconds) and then all would be good?

P.S. As your new fractional pixel scaling is not breaking anything, you might want to get rid of the branch and put everything in develop?

MJoergen commented 3 years ago

I'm updated the scrolling to make it more smooth, and I've merged into the develop branch.

Still missing is to update documentation.

It would be nice to make a port of the Wolfenstein game using this new fractional scaling, but I'll leave that to you @sy2002 :-D

MJoergen commented 3 years ago

Regarding the delay, that would be a nice function to have, but I don't see how it can be used in the little demo program. The point is that the demo program is running entirely synchronuous with the VGA scan line. It could be possible to rewrite the demo program using interrupts to control the VGA port, and then have a main loop with delays controlling the parameters for the interrupt. That would probably - in some sense at least - be more cleaner code.

sy2002 commented 3 years ago

@MJoergen I just quickly tried it on hardware: Great job! I love it! ❤️

MJoergen commented 3 years ago

TODO:

sy2002 commented 3 years ago

Make another demo program, showing how to display a checker board in 3D perspective. Perhaps using interrupts?

WOW - that sounds great! Can't wait to see that! :-)