Shop OBEX P1 Docs P2 Docs Learn Events
NEW 640 x 480 2-4 Color VGA Bitmap Driver — Parallax Forums

NEW 640 x 480 2-4 Color VGA Bitmap Driver

KyeKye Posts: 2,200
edited 2011-12-03 23:58 in Propeller 1
So, I merged all of my 2 and 4 color multiple resolution VGA bitmap drivers into one that can do it all at runtime. (The old versions have been deleted).

So..... Now you can do anything you want!!! The API is listed below.

Here's a link to the object. Also, the demo is really, really, nice.

http://obex.parallax.com/objects/535/ - Overwrote the old object I had here with this new one.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VGA64 Bitmap Engine
//
// Author: Kwabena W. Agyeman
// Updated: 7/15/2010
// Designed For: P8X32A
// Version: 1.1
//
// Copyright (c) 2010 Kwabena W. Agyeman
// See end of file for terms of use.
//
// Update History:
//
// v1.0 - Original release - 9/28/2009.
// v1.1 - Merged and rewrote code and added more features - 7/15/2010.
//
// For each included copy of this object only one spin interpreter should access it at a time.
//
// Nyamekye,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Circuit:
//
//     0   1   2   3 Pin Group
//
//                     240OHM
// Pin 0,  8, 16, 24 ----R-------- Vertical Sync
//
//                     240OHM
// Pin 1,  9, 17, 25 ----R-------- Horizontal Sync
//
//                     470OHM
// Pin 2, 10, 18, 26 ----R-------- Blue Video
//                            |
//                     240OHM |
// Pin 3, 11, 19, 27 ----R-----
//
//                     470OHM
// Pin 4, 12, 20, 28 ----R-------- Green Video
//                            |
//                     240OHM |
// Pin 5, 13, 21, 29 ----R-----
//
//                     470OHM
// Pin 6, 14, 22, 30 ----R-------- Red Video
//                            |
//                     240OHM |
// Pin 7, 15, 23, 31 ----R-----
//
//                            5V
//                            |
//                            --- 5V
//
//                            --- Vertical Sync Ground
//                            |
//                           GND
//
//                            --- Hoirzontal Sync Ground
//                            |
//                           GND
//
//                            --- Blue Return
//                            |
//                           GND
//
//                            --- Green Return
//                            |
//                           GND
//
//                            --- Red Return
//                            |
//                           GND
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Object "VGA64_BMPEngine" Interface:
PUB  plotCharacter(characterValue, xPixel, yPixel, displayBase)
PUB  plotPixel(pixelValue, xPixel, yPixel, displayBase)
PUB  displayState(state)
PUB  displayRate(rate)
PUB  displayWait(frames)
PUB  displayPointer(newDisplayPointer)
PUB  displayColor(pixelNumber, newColor)
PUB  displayClear(patternValue, displayBase)
PUB  BMPEngineStart(pinGroup, colorMode, horizontalResolution, verticalResolution, newDisplayPointer)
PUB  BMPEngineStop
Program:     236 Longs
Variable:      0 Longs
_______________________________________________________________
PUB  plotCharacter(characterValue, xPixel, yPixel, displayBase)
 7 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Plots a 16x32 pixel character on screen.
 //
 // CharacterValue - The ASCII character to plot on screen from the internal character ROM. BG = %%0. FG = %%1
 // XPixel - The X cartesian pixel coordinate, will be forced to be multiple of 16. Y will be forced to be a multiple of 32.
 // YPixel - The Y cartesian pixel coordinate. Note that this axis is inverted like on all other graphics drivers.
 // DisplayBase - The address of the display buffer to draw to.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_______________________________________________________
PUB  plotPixel(pixelValue, xPixel, yPixel, displayBase)
 7 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Plots a 1x1 pixel on screen.
 //
 // PixelValue - The pixel to plot on screen. Between %%0 and %%1 or between %%0, %%1, %%2, and %%3 depending on color mode.
 // XPixel - The X cartesian pixel coordinate, will be forced to be multiple of 1. Y will be forced to be a multiple of 1.
 // YPixel - The Y cartesian pixel coordinate. Note that this axis is inverted like on all other graphics drivers.
 // DisplayBase - The address of the display buffer to draw to.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
________________________
PUB  displayState(state)
 4 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Enables or disables the BMP Driver's video output - turning the monitor off or putting it into standby mode.
 //
 // State - True for active and false for inactive.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
______________________
PUB  displayRate(rate)
 4 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Returns true or false depending on the time elasped according to a specified rate.
 //
 // Rate - A display rate to return at. 0=0.234375Hz, 1=0.46875Hz, 2=0.9375Hz, 3=1.875Hz, 4=3.75Hz, 5=7.5Hz, 6=15Hz, 7=30Hz.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
________________________
PUB  displayWait(frames)
 4 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Waits for the display vertical refresh.
 //
 // The best time to draw on screen for flicker free operation is right after this function returns.
 //
 // Frames - Number of vertical refresh frames to wait for.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
______________________________________
PUB  displayPointer(newDisplayPointer)
 4 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Changes the display pointer for the whole screen.
 //
 // In 1 bit per pixel mode each pixel can have a value of 0 or 1 which map to the 0 and 1 colors for the whole screen.
 // In 2 bits per pixel mode each pixel can have a value of 0 to 3 which map to the 0-3 colors for the whole screen.
 //
 // In 1 bit per pixel mode the display buffer must be of size ((horizontalPixels * verticalPixels) / 32) in longs.
 // In 2 bits per pixel mode the display buffer must be of size ((horizontalPixels * verticalPixels) / 16) in longs.
 //
 // In 1 bit per pixel mode each long holds 32 pixels.
 // In 2 bits per pixel mode each long holds 16 pixels.
 //
 // The LSB/LBSs of every long is/are the left most pixel while the MSB/MSBs of every long is/are the right most pixel.
 //
 // NewDisplayPointer - The address of the new display buffer to be displayed after the vertical refresh.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
________________________________________
PUB  displayColor(pixelNumber, newColor)
 5 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Changes a pixel color for the whole screen.
 //
 // PixelNumber - The pixel number to change for the whole screen. Between 0 and 1 or 0 to 3.
 // NewColor - A color byte (%RR_GG_BB_xx) describing the pixel's new color for the whole screen.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
____________________________________________
PUB  displayClear(patternValue, displayBase)
 5 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Clears the whole screen.
 //
 // PatternValue - The pattern to plot on screen.
 // DisplayBase - The address of the display buffer to draw to.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
_____________________________________________________________________________________________________
PUB  BMPEngineStart(pinGroup, colorMode, horizontalResolution, verticalResolution, newDisplayPointer)
 11 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Starts up the BMP driver running on a cog.
 //
 // Returns true on success and false on failure.
 //
 // PinGroup - Pin group to use to drive the video circuit. Between 0 and 3.
 // ColorMode - Color mode to use for the whole screen. Between 1 bit per pixel or 2 bits per pixel.
 // HorizontalResolution - The driver will force this value to be a factor of 640 and divisible by 16 or 32. 16/32 to 640.
 // VerticalResolution - The driver will force this value to be a factor of 480. 1 to 480.
 // NewDisplayPointer - The address of the new display buffer to be displayed after the vertical refresh.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
__________________
PUB  BMPEngineStop
 3 Stack Longs
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Shuts down the BMP driver running on a cog.
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                  TERMS OF USE: MIT License
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
«1

Comments

  • hinvhinv Posts: 1,252
    edited 2010-11-07 10:37
    Hi Kye,

    That sound's great, but how did you do it? 640x480 1bit (2 color) is 38400bytes doesn't fit in the hub.

    Is the vga circuit different than the DemoBoard?

    Thanks,
    Doug
  • KyeKye Posts: 2,200
    edited 2010-11-07 10:45
    Its not actually 640x480. That's what the driver drivers the screen at. So, you can have whatever resolution you want as long as it satisfies a few rules.

    1: The resoltuion is no bigger than 640x480 pixels.
    2: The display buffer fits in the hub ram.
    3: The vertical resoltuion is a factor of 480.
    4: The horizontal resoltuion is a factor of 640.
    5: If the color mode is 1 bit per pixel then the horizontal resolution must be divisible by 16.
    6: If the color mode is 2 bit per pixel then the horizontal resolution must be divisible by 32.

    Example: You can have a resolution of 32x1 using one long in 1 bit per pixel mode. This would be displayed on screen at the resoltuion of 640x480.
  • Bill HenningBill Henning Posts: 6,445
    edited 2010-11-07 10:54
    Nice work!

    320x240 x4 color = 19200 byte frame buffer, and is a quite usable resolution.

    I've done 640x240 x2 color in the past - it also uses 19200 bytes for a frame buffer.
  • KyeKye Posts: 2,200
    edited 2010-11-07 11:52
    My driver is fully dynamic to now. So, you can change resolutions at runtime now and all other kinds of stuff. Page flipping is supported by default.
  • jazzedjazzed Posts: 11,803
    edited 2010-11-07 11:59
    Nice. Do you have a demo with more colors? How about mouse cursor support?
  • SapiehaSapieha Posts: 2,964
    edited 2010-11-07 12:26
    Hi KYE.

    Have You tested this driver with other frequencies to Propeller that only 5MHz-PLLx16


    Kye wrote: »
    My driver is fully dynamic to now. So, you can change resolutions at runtime now and all other kinds of stuff. Page flipping is supported by default.
  • KyeKye Posts: 2,200
    edited 2010-11-07 13:16
    The driver works with any clkfreq above 80Mhz. You can just change the color in the demo by going to the line that says displayColors and chaning the color constant there to any other color constant or any VGA 8 bit color number.

    This driver was done in my spare time. You can add mouse cursor support to it however... I'm not coding that complexity in again. Getting the mouse cursor working for my other driver was a TASK!!!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-07 17:10
    This is very nice.

    What is the highest color resolution? Back of an envelope, 19200 byte frame buffer is 160x120 giving one byte per pixel, which is 2 bits per pixel using 6 bits with two spare.

    That gives a 4x4 pixel block at 640x480 - probably acceptable on some of the smaller screens. Is this resolution supported by any chance?
  • KyeKye Posts: 2,200
    edited 2010-11-07 20:40
    VGA hardware supports at max 2 bits per pixel. So, you can have 4 colors for the whole screen.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-07 20:47
    From a hardware perspective, 2 bits per pixel with three colors is 00,01,10,11 for each color. Is that not 32 colors? Or is there a software limitation as well?
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-07 21:06
    He's doing a 4 or 2 color waitvid. Hardware limitation, in that longer frame sizes, like 8, 16, 32 pixels are only possible at 2 and 4 color bit per pixel.

    The longer frame sizes make higher driver resolutions possible, because there is more time in the waitvid loop.

    To do byte / pixel on the prop, means running the waitvid backward, doing a 4 pixel frame, such that the color long ends up just being pixels, in that one byte of that long is assigned to each of the 4 pixels in the frame.

    Those typically require shorter loops, and or special code to manage anything over 320 pixels on TV graphics. VGA has similar limits, depending on the horizontal sweep frequency used.

    Instead of "waitvid colors, pixels" where colors = 4 bytes of color definition distributed among the pixels in the pixels long, it runs like this: "waitvid pixels, %%3210" The same four pixels are always output, leaving the programmer to vary the color of them.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-07 21:14
    Ah, that explains things. So this really is pushing the limits of what the prop can do with video.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-07 22:31
    Yeah, given the flexibility. It's a great driver!

    All things video cost something on the prop. If computation is needed, either it's pre-computed taking space, fed to the cog, costing portability and requiring SPIN, or another cog, or done in-line, costing waitvid time.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-10 16:11
    Hi Kye,

    Just trying out the demo, I added a couple of lines but I don't seem to be getting anything on the screen. Is my syntax wrong?
    PUB demo | randomSeed, displayPointer, currentDisplay, extraCounter
    
      ifnot(bmp.BMPEngineStart(_pinGroup, 1, _horizontalResolution, _verticalResolution, @displayBuffer))
        reboot
    
      bmp.displayColor(0, bmp#black)
      bmp.displayColor(1, bmp#blue)
    
      'bmp.plotCharacter(65,16,32,10)  ' *** this crashes the program ***
      bmp.plotPixel(3,5,5,5) ' *** this does not seem to do anything ***
    
      randomSeed := cnt
      repeat result from 0 to constant(_linePoints - 1) step 1
    


    Some more thoughts. I started increasing the screen resolution from 160x120 to 320x240 and the pixels duly got smaller. But when I went to 640x480, it gave an error - out of memory.

    Of course, this is totally expected. (640x480)/8 is more than the propeller ram.

    So this is a design challenge. How could you fit a screen into a propeller? Consider a typical Windows screen. Most of it is white or gray. So you can do this with tiles as long as most of the tiles were white or gray.

    But can we do better? I have been pondering an algorithm for storing graphics. Consider a top row that has 100 white pixels, then 20 red ones, then 50 gray ones. Instead of 170 bits, you could store that as three longs, eg a long that contains the number 100, and contains the binary value for white. The display driver then duly puts these on the screen. When a counter gets to zero, it gets the next color/number, resets the counter to the number of pixels and starts putting these on the screen.

    So there will be less reads from hub memory.

    What else? Well, you might not have enough time to do this on the fly, as there will be timing differences at the transition. So - is it possible to build up a row of pixels in the background while another row is being displayed? Would you need one or two cogs to do that?

    What is the catch? Well, if you change one pixel, you have to edit the entire list. It might be quicker to rebuild the list rather than edit it. So this may not be suited to faster moving graphics.

    And complex graphics will fill the entire memory with the list, as storing pixels in a list of changes is less efficient than storing as a bitmap. But this may not be so important if most of a screen is the same color.

    Kye, you have been deep inside video code, do you think some sort of compression algorithm like this, with decompression on the fly, would be possible?
  • KyeKye Posts: 2,200
    edited 2010-11-10 17:12
    Only six instructions can be put in the video loop. No time do do anything other than stream out data from memory. Srry,

    Also, you seem to be passing incorrect arguments to the display functions. You need to pass the base address of the video memory and you are passing an integer...
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-10 23:41
    6 instructions in the video loop? Yikes!

    But I'm not giving up quite yet.

    Ok, consider the average performance of some code that needs to build a vga line in the background (a 2 cog vga solution). Also consider a screen where mostly it is one color.

    So the first step is to read a long, and determine that the color is white. You then work out whether the number of pixels is going to be more than the screen width, and if so subtract a number so the counter finishes at the end of the line (640). Then you add a value so the counter is pointing to the correct memory location. And you also store the finish location. So maybe we are going from pixel 30 to 130, the value is white, the memory location in cog is 500, so the start location is 530 and we have the value 630 stored somewhere.

    This is the setup and this takes time. Maybe it is 20 instructions. But at the same time, these changes don't happen very often.

    But now we can enter a very tight loop. Write a byte to location 530, add one, compare with 630, write a byte to location 531, add one, compare with 630 etc.

    I'm not sure of the exact pasm instructions, but that ought to be close to 6 instructions. Possibly even less.
  • KyeKye Posts: 2,200
    edited 2010-11-11 05:31
    The problem is that you can never exceed the 6 instructions... The best example of pushing this system to the limit is in my 640x480 tile driver object I put online. It does some crazy stuff to get past the 6 instruction count overhead and make video work.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-11 07:10
    IMHO, what is needed here is a scan line buffer based VGA driver. Have the VGA cog, just read pixels from a scan line buffer. When it's run on it's own, it would draw color bars, from the contents of that buffer, and that's it. It also writes the state of the display to the HUB, and I recommend two states. One is end of graphics screen. When the last scan line is drawn, write a 1 to the HUB. The other is end of scan line. Write a 1 to the hub when the scan line is finished.

    With those three things, graphics cogs can be written. Graphics COGs can write zeros to that same location, "locking" the two COG's in sync. When the graphics cog is started, it writes a 0 to the state location assigned to "end of screen", and it waits... When it sees the 1, it's then set to generate the first scan line worth of data, putting it into the buffer for the VGA cog, which just draws that buffer to the screen.

    When it's done with that scanline, it writes a 0 to the scan state location, and also another 0 to the vertical state location end of screen, waiting again.

    When end of scan gets set to a 1, then it builds another scan line worth of data, writing a 0 when finished.

    When the graphics cog sees a 1 at end of screen, it resets it's own counters, and cycles back for the next frame.

    That works for a single scan line buffer, such that the graphics cog will always be a little ahead of the beam, yielding most of the scan line for compute time.

    The graphics cog outputs no video, reading the state bits, and doing the work to fill the scan line buffer, while the VGA cog is reading from it. This works best with a double buffer, but can be made to work with a single one. Potatotext does a single buffer, with just timing used to sync up the graphics generation and the screen display. Literally, the buffer is being filled just ahead of the beam on the screen.

    If more compute time is needed, double buffering the scan line can free up the entire scan line to build the next scan line.

    Add one more state to the mix, which is the working buffer address, so the graphics cog knows to fill the other one, if double buffering is used. Double buffering can help where the compute time varies some, or where the entire scan line worth of time is needed to assemble that display line; otherwise, a single scan line buffer is sufficient, and simpler.

    Add graphics COGs, if there is not sufficient compute time, each COG either working on it's part of the buffer (bitmap or character tile graphics), or overlaying it's work product into the buffer (sprites).

    If lots of colors are needed, then a color buffer can be used, just like the scan line buffer, the goal of which is to get both the colors and pixels arguments to the waitvid on time, ideally only doing a hub fetch, and a index update in the video loop. Or... have the TV COG be modal, in that it's got the option to either run waitvid by the book, with a standard 4 color display tile option (colors, pixels), or backward full color mode (pixels, %%3210). The graphics cog could either read the mode bit, and act accordingly, or probably better, just set the mode bit so the TV COG switches it's waitvid mode, depending on the need the graphics COG has. (That's the route I'm gonna take, so the users can more easily fetch and use graphics COGs)

    That puts the higher resolutions on the table, and separates compute / masking tasks from drawing tasks.

    With the code Linus has posted up, syncing video cogs is on the table now too... I'm in the mood to start mucking around with overlay again. Good things could happen!

    Edit: One other advantage to this is to get a single TV cog done, which is the hardest bit to do, IMHO. Once it's done, various graphics cogs can be written against it, depending on the need. That's what I'm working on right now for TV. The Potatotext driver works well, but is overly complex. Not all graphics cogs are possible, due to me putting too much into the TV cog, just for that task, and not making the TV cog modal enough to handle more general graphics cases. Better to make the TV COG, or VGA COG kind of dumb, with a few modes to use the buffer in ways favorable to the graphics COG, and or handle various screen resolutions, etc...

    Chip demonstrated this idea with his TV cog, which powers both the general case graphics_demo.spin, optional graphics library COG, and the TV_Text.spin, both using the same TV COG in different ways. No scan line buffer on that, but the idea is the same.

    The first real game drivers that happened, used scan line buffers to deal with the video loop. Video loop only, limits one to either a tile or bitmap display, as there isn't time for anything else. If anything else is needed, the cost is resolution, because the waitvid loop gets too long.

    Another Edit: When it's all done, the cogs can be binary BLOBS, fetched from eeprom, SD card, etc... only requiring a start address for the mode, buffer and state information. Populate those, COGNEW with the start address, and pass it to the COGs. Also, if the TV / VGA cog, populates it's buffer with known data, like a color bar, or alternating stripes, it can be started on it's own, displaying just that data without corrupting the display. Doing that would allow for sequential fetches, and or changing out the graphics COGs, again depending on need, using only a 2K COG buffer.

    My plan is to use .cog files on the SD card. TV.cog, text.cog, bitmap.cog, sprites.cog, etc... Fetch them from some storage, launch them, and kill them off when not needed, from any language that can do that, extending the other programming options. I personally am happy with SPIN + PASM, but others are doing more, and always want video stuff, which takes a LONG time to get sorted. (true for me anyway)
  • KyeKye Posts: 2,200
    edited 2010-11-11 14:37
    Oh!!! So much coding. I'll just be happy with what I have.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-11 15:40
    Seriously! Don't blame you one bit. If you don't need the features, video is easier and a lot more fun as a single cog affair.

    Really, part of the post was to leave the how-to out there, and let Dr_A, know there is a path --just not a easy one.

    (nice driver, BTW!! your code is great to read)
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-11 15:54
    You think there is a path?! *pricks up ears*

    Yes I can't see how with the one cog driver, but with the two cog vga driver...

    It is a matter of, on average, taking 6 instructions or less per pixel. At the very simplest level, you are writing the same value of a byte to an incrementing memory location in cog memory. My feeling is that might be even faster than reading a byte from hub ram. Might that not be possible with only 3 or 4 instructions?
  • ElectronegativityElectronegativity Posts: 311
    edited 2010-11-11 16:41
    Thank you very much Kye for taking the time to do this and being generous enough to share it.

    I'm sure you saved me at least a month of work.
  • KyeKye Posts: 2,200
    edited 2010-11-11 17:02
    Thank you very much,

    Please note however that drawing on the screen using SPIN code is painfully slow. You may have to recode your drawing functions in assembly.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-13 01:28
    @Dr_A Well... It seems to me, one cog is kind of out of the question, just because the extra indexing and decisions would probably render the video loop too long, meaning one would have to make several cases... That gets complex very quickly.

    However, a graphics cog could very easily be filling a scan buffer, based on the screen list data... Two cogs are probably necessary, which means building out the basic connection outlined above.

    The Spacewar game was written with a higher resolution display that operates at a greater resolution than RAM can handle. Perhaps that makes sense? Worth a look.

    Then there are tiles. The Parallax drivers are tile based, and get the benefit of the graphics.spin COG. Some simple display management could yield a nice, high resolution display, using the tiles, and some code to manage them too. Depends on what the display is. That's probably easier than dealing with rebuilding the list, and graphics can be shared too.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-13 03:16
    Hi Potatohead,

    Tiles sound interesting.

    I've just spent some time deep inside the code for the two cog hi_res vga driver. Now, this is very cunning code, but I do not understand all of it. I am trying to understand where the data gets stored before it is displayed. There is a buffer "scanbuff[128]" which is 512 bytes, but elsewhere it says it stores four lines of data. 640x4 is more than 512 bytes, so I am thinking it only stores bits, not bytes, and maybe because it only has two colors for that particular line it only needs to store bits.

    In which case, how is your spacewar code storing colors? (6 bits per pixel for max resolution).

    So the first problem one might face is that for 6 bits per pixel, one has to store 640x6 = 480 bytes or nearly 1/4 of the cog memory for just one line. So clearly you can't store 4 lines.

    You do save some considerable space with respect to the hi_res vga driver in that you don't need to store all the character bitmaps.

    The other bit of code that I can't understand is
    scanline                mov     vscl,vscl_chr           'set pixel rate for characters
                            jmp     #scancode               'jump to scanbuff display routine in scancode
    scanret                 mov     vscl,#hs                'do horizontal sync pixels
    

    where #scancode does not seem to exist anywhere as a label in the code.

    I guess this comes back to a generic question - what is the highest vga resolution the propeller can drive? Limit maybe to 640x480 display, can one do 4x4 pixels as a block with individual colors? Can one go to 2x2 pixels? What is the best resolution one can do with tiles assuming that most tiles might be all white? Can tiles take you to a solution where most of the screen is white, but at an arbitrary location is a really detailed tile with all 6 bits of color per pixel?

    I'm particularly interested in this as Catalina can be configured to work in external ram, so that pretty much frees up all the hub ram for video.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-13 11:01
    I've not looked at that code much.

    But... Let's talk pixels for a moment. Basically, we can get 1 bit per pixel, 2 bits per pixel, and 8 bits per pixel out of a waitvid. Waitvids work with the concept of a 'frame' which is a pixel group.

    The first line of code you asked about is setting a waitvid frame timing. That's your pixels over time. Another line somewhere sets the size of a pixel, in terms of time, with the two of them together determining what a waitvid does with the pixels.

    eg: Let's say a pixel is 3 clocks, and we set a frame size of 24 clocks. Really that should be PLLA, not clocks, because the waitvid works off of PLLA.

    So, that's 8 pixels, three PLLA each. That waitvid would then draw 8 pixels, from the pixels long, assuming 1 or 2 bits per pixel. It would not use all the data given to it.

    Perhaps that parameter you cannot locate is a constant, or is computed and written at run time?

    For higher resolutions, you want the whole frame, and you want the pixel clock as large as it can be, so that the waitvid spends the max time drawing things, so your COG has the max time fetching things for it to draw. That means you really want the full 16 or 32 pixel frame, at 1 or 2 bits per pixel.

    Higher resolutions then mean 1 or 2 bits per pixel, because the 8 bits per pixel is fixed at a rather fast 4 pixel frame. "waitvid colors, #%%3210"

    Seems to me, a 160 byte scan line buffer would hold one scan line. Typically a scan line buffer exists in the HUB, so that all the working COGs can contribute to it, not the COG. COG indexing, fetching, and bit manupulation, if one wants bytes, is actually not too much faster than just fetching from the hub, because computation is generally needed anyway. One has to add indexes, subtract from counters, maybe perform a bit operator, etc... so that get done between hub fetch cycles.

    If you don't mind TV, and feel you can map over to VGA, I've got a old version of potatotext that isn't text yet, and that is a two COG scan line buffer graphics engine. I'll get it, and post it, so you can see much simpler code. VGA requires more sophisticated timing, and runs at higher sweep frequencies, putting more demands on the prop. TV runs rather easily at 640 pixels, by comparison. I think exceeding 640, takes multiple COGs just to draw the signal, and that's the resolution I personally would target. 1280 can actually be done, though it took Chip 5 COGs to do it :)

    How fast is external RAM fetch? I think it's got to be on the order of 3Mhz to make any sense for refreshing video, and TV is the slowest and easiest video to refresh.

    If you got a tile display running, and just FYI, the Parallax driver does those tiles, and colors at 640 pixels on the VGA, one could simply maintain a lot of tiles in external RAM, moving them into and out of the HUB as needed for a lot of display variety. If you do the moving during VSYNC, it will be tear and flicker free too. Or, just don't do it faster than say, 15Hz, and it will look nice too.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-13 15:55
    Hi Potatohead,

    I've just downloaded every vga driver I could find on the obex. I suspect there are more out there in the wild too, but the one that does look intriguing is the 1280x1024 tile driver. This uses 3 cogs.

    I'm still not sure about tiles, but let us assume you have a display and 80% of the display is white, ie you have a white tile and you replicate this many times. So this saves a huge amount of hub ram. But, right in the middle of the screen, you have some tiles with lots of data - each pixel is the full color range (6 bits). I wonder if this is possible?

    What I'm thinking of is buttons that use bitmaps for the button. So the prop is a bit like an ipad, really easy to use with picture buttons that you press.

    I just did an experiment in paint shop taking a picture and decreasing the color depth to 64 bits. It does not look too bad. Not as gorgeous as an ipad, but certainly faces are recognisable.

    With an sd card, it is easy to move bitmaps into hub ram.

    What I'm not entirely sure is the color depth of tiles, and whether there is code out there that can do a tile in full 64 bit color (even if it is only a tiny tile in the middle of the screen and the rest of the screen is black).
  • jazzedjazzed Posts: 11,803
    edited 2010-11-13 16:13
    Dr_Acula wrote: »
    What I'm thinking of is buttons that use bitmaps for the button. So the prop is a bit like an ipad, really easy to use with picture buttons that you press.
    Having a driver that does this would be a great thing. When you can't tell the difference between Propeller generated GUI and a modern phone GUI you can say something *really cool* has been done.
  • potatoheadpotatohead Posts: 10,253
    edited 2010-11-13 16:28
    Well, tiles are generally 2 color, or 4 color, because of how the waitvid works with pixel and color data.

    If you want "full color", then it all comes down to running the waitvid backward, for a 4 pixel frame.

    "waitvid colors, #%%3210" colors become pixels done that way, otherwise there are more pixels than colors, imposing the familiar 4 color / tile limits.

    In a normal waitvid, there are four colors possible, and up to 32 pixels. It all gets loaded in there, for the waitvid to crunch on, while the cog gets the next data set.

    So tiles at full color, really are multiples of 4 pixels. And full color displays operate at a lower resolution, because each waitvid is only four pixels.

    I've done over 320 pixels at full color on TV graphics, with one COG. I think that actually can hit 512 at 80Mhz, but it's been a while since I did that. The code is actually on the Wiki, under "hi-color bitmap" in the old list of drivers there. Never did because of lack of RAM.

    Anyway, your are on the right track with the tiles and saving space. Basically, all white, or all black tiles can be represented by a single tile definition in HUB RAM, which is basically where the video has to live, unless you are able to fetch data fast enough from external RAM.

    It is entirely possible to do what you want to do. IMHO, what is required is to find a driver that runs the waitvid backward, like I put above, or recode one so that it does that. That means redoing the video loop to use 4 pixel frames, and adding a counter or two for your tile sizes.

    Have you considered 16 color?? Baggars has done sprite drivers that do that, and that have palettes! Look at the VCS driver he did. It can do a hell of a lot of sprites.

    Either way, say you setup 16 pixel tiles at full color. You divide the display into 16 pixel zones, horizontal and vertical. Each tile is 4 longs wide, by 64 longs deep.

    Tiles can be done two basic ways. There is the Parallax way, shown in the Chip VGA drivers. I really like that way, because it's flexible. Basically your "screen" is just this array of words that contain tile addresses, one address per tile in the display. You fill the array with the addresses where the data lives, and the driver then draws out the pixels, fetching addresses, and computing offsets based on the scanline being drawn.

    The other way is basically the "text mode" way, seen in older computers, and my text drivers. Tiles occupy a chunk of HUB ram, and have a start address. The "screen" is a series of bytes, each one referencing a tile number. Again, the driver computes addresses and offsets based on the byte read, and the scan line drawn.

    Advantage of the text mode way is a more efficient means to store the screen. It's just bytes, and for larger screen sizes can be a nice savings. Disadvantage is there are only 256 tiles possible. At your tile sizes, that would be enough.

    Advantage of the Parallax way, when using less than full color, is a very easily exploited waitvid, for lots of colors and tiles, and the use of the graphics.spin library. For your idea, graphics.spin won't work because you would be operating at 8 bits per tile.

    One thing to consider is time spent bit mashing. Propellers are slow at this, because the COG addressing is longs only. It is best to just operate at 8 bits per pixel, knowing there is some waste, because expanding the 6 bits into 8, to feed the waitvid will cost you too much compute time.

    Eric Ball wrote a sprite driver that's seriously worth a look. It's TV, and it's 224 pixels, but... it draws a lot of sprites. The sprite engine part of that could probably be used on a VGA driver.

    Sprites can be freely placed, but there can only be so many of them.

    I don't think anybody has done a text + sprite driver in full color yet. The few full color drivers there are, either are bitmap, or sprites, but not both, and most of them are for TV, or were coded for a specific game.

    IMHO, having both would be sweet, but it's going to be pushing the edge of what has been done. (possible though, just not done for lack of RAM)

    Did you find a driver that does full color for VGA? I don't recall seeing one. There are TV drivers for bitmaps, most recent being the one I posted to draw fractals at full color, that can easily be made to do tiles. I don't recall seeing one for VGA yet. Probably because the resolutions are high enough to require some work, and nobody yet has come along asking to do what you are thinking of doing.

    Would you be able to live with 320 pixels horizontal?? TV maybe to prototype it? I'm asking, because there is a lot of close TV code. A 16 pixel wide tile driver for TV would not be too difficult at one byte per pixel.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-11-13 16:45
    This is an intriguing discussion.

    So - would it ever be possible to build a tile that has all 64 colors in one tile? Even if the tile was tiny and the rest of the screen was black?

    Just to see if this is worth pursuing, I hand coded a palatte in paint shop. There are three colors and each color has the value 0, 85, 170 or 255. 64 colors in all.

    The first attachment is the full palatte. There are some interesting colors in there, including skin tone colors.

    There is a picture of me (to demonstrate skin tones), a lorikeet, an outdoor shot, a shot in the snow, and I think the color depth is enough to recognise the pictures.

    But it is the last one pic6 that I find the most intriguing. This is a screen capture of the prop tool (I saved it as a bitmap as jpg blurs the text) The graded color bar along the top window has been converted to discrete colors, but the rest of the screen looks almost identical to the real thing.

    Consider the data in this screen. There are a lot of tiles the same color (white/yellow), and if you coded this screen by describing each line in terms of the change in color (50 white pixels, 25 gray pixels, 100 yellow pixels) I wonder if that data could be decoded on the fly?
    290 x 73 - 8K
    624 x 651 - 72K
    640 x 647 - 105K
    640 x 480 - 116K
    640 x 480 - 103K
Sign In or Register to comment.