Shop OBEX P1 Docs P2 Docs Learn Events
Help! Making a VGA graphics library in C — Parallax Forums

Help! Making a VGA graphics library in C

dantledantle Posts: 3
edited 2010-03-11 23:02 in Propeller 1
I'm trying to use ICCV7 to write a very light VGA driver; right now I'm just trying to put a color onto the screen. I've worked very hard to try to replicate the process taken by the creator of the PASM driver found here, but it still fails to make my VGA monitor react at all when I load the program.

Below is my port of the VGA driver. I'd certainly appreciate it if someone could take a look and offer suggestions. Thanks!

/***************************************
*  VGAdriver.c                         *
*   Implementations of VGA functions   *
****************************************/
#include <stdlib.h>
#include <string.h>
#include <propeller.h>
#include "VGAdriver.h"


#define pr 35  
// 'pixel rate in MHz at 80MHz system clock (5MHz granularity)
#define hp 512 
// 'horizontal pixels
#define vp 384 
// 'vertical pixels
#define hf 8   
// 'horizontal front porch pixels
#define hs 48  
// 'horizontal sync pixels
#define hb 88  
// 'horizontal back porch pixels
#define vf 1   
// 'vertical front porch lines
#define vs 3   
// 'vertical sync lines
#define vb 28  
// 'vertical back porch lines
#define hn 1   
// 'horizontal normal sync state (0|1)
#define vn 1   
// 'vertical normal sync state (0|1)

#define vscl_pixel ((1 << 12) + 32)


#define xtiles (hp / 32)
#define ytiles (vp / 32)

#define hv_inactive (((hn*2) + vn) * 0x0101)

/* other global variables
 */
//unsigned int hvis = hp;
//long hv = hv_inactive;

typedef struct info
{
     unsigned char x;
    unsigned int hvsync;
    unsigned int colormask;
} info_t;


/* a helper function to replicate the 'blank' subroutine from assembly
 */
void blank(info_t* info)
{
     unsigned int hvsink = info->hvsync;
    // executes x times.
    for(; info->x > 0; (info->x)--){
            // blank
            VSCL = hp;                         // hp = hvis
            asm("WAITVID %hvsink,#0");

            // hsync
            VSCL = hf;
            asm("WAITVID %hvsink,#0");
            VSCL = hs;
            asm("WAITVID %hvsink,#1");
            VSCL = hb;
            asm("WAITVID %hvsink,#0");
    }
}

/* void initVGA
 * Description: should initialize the VGA by setting the pins that it operates
 * on as well as tristate buffer settings and counter registers. 
 */
void initVGA()
{
      // set VCFG register.
     char BasePin = 16;
     int i,j;
     i = 0xFF << (BasePin & 0b011000);
     if((BasePin & 0b100000) == 0)
          j = 0;
     else
          j = -1;
     DIRA = i & j;
     DIRB = i & (~j);
     CTRA = 0b00001101 << 23;    // enable PLL in CTRA (VCO runs at 4x)
     FRQA = ((pr / 5) << 3) << 23;

     VCFG = 0x200000FF + ((BasePin & 0b111000) << 6);// start video generator
}

/* void VGA_write
 * Description: reads video memory, outputs to the RGB bits that go to the screen.
 * INPUT: row - a pointer to the line in 
 *          info - a structure that contains cog-specific data.
 * SIDE-EFFECTS: Writes a line of video memory to the VGA.
 */
void VGA_write(short row, info_t* info)
{
      unsigned int i,j, colors, pixels;
     unsigned int hvsink = info->hvsync;
     VSCL = vscl_pixel;
     
     /* * * * * * * * * * * * * * * * *
      * Colors: contains 4 of these:  *
      *    Bit   07 06 05 04 03 02 01 00 *
      *    Data   R  R  G  G  B  B HS VS *
      * * * * * * * * * * * * * * * * */
     colors = 0x00203300 & info->colormask;    // [noparse][[/noparse]1: BLUE] [noparse][[/noparse]0: GOLD]
     colors |= hv_inactive;
     pixels = 0x00000000;     // Should queue gold pixels to the screen.
     
     // This loop needs to repeat when the cog is ready to push the colors out.
     for(i = 0; i < xtiles; i++){
             asm("WAITVID    %colors, %pixels");
    }

    VSCL = hf;
    asm("WAITVID %hvsink,#0");
    VSCL = hs;
    asm("WAITVID %hvsink,#1");
    VSCL = hb;
    asm("WAITVID %hvsink,#0");

}



/* startVGA()
 * Description: A pointer to this function should be passed as an argument to
 * cognew or coginit.  I never want the cog that is given this function to
 * ever return. 
 */
void startVGA()
{
     // initialize data
      info_t info;
     short q;
     unsigned int hvsink;
     info.hvsync = (hv_inactive) ^ 0x200;
     info.colormask = 0x0000FCFC;
     initVGA();

     // main loop, the cog should never return!
     while(1){
              // write the whole screen
              for(q = 0; q < vp*2; q++)
                    VGA_write(q,&info);

             // visible done, write non-zero to sync.
             asm("WRLONG %hvsink,par");
             info.colormask = hvsink;
             
             // get ready to write again
             info.x = vf;
             blank(&info);
             
             // vsync
             info.x = vs;
             info.hvsync ^= 0x101;
             blank(&info);
             
             // vsync
             info.x = vb;
             info.hvsync ^= 0x101;
             blank(&info);
    }
}

/* This calls the test function. */
void main()
{
      int z;
     cognew_native((void*)&startVGA, 0);
      while(1){
            // do other stuff
        }
    }
}



Some miscellany:
- Everything that is not in void main() should be ran on one cog.
- In lieu of somehow defining cogspace globals, I've instead opted for passing a reference to an info_t struct that contains my bookkeeping information that needs to be used in multiple function scopes.

Post Edited (dantle) : 3/11/2010 6:49:02 AM GMT

Comments

  • KyeKye Posts: 2,200
    edited 2010-03-11 07:00
    You should do what Jazzed does.

    I believe he just takes the compiled asm code and calls cognew on that using C. He then just writes all the stuff that would be in spin in C.

    The vga driver requires precision timing. At the HEART of the loop that displays video you usually have only room for 6-4 or so instructions in ASM. Thus using LMM in C will fail.

    I recomend trying this process out on my flexibile VGA bitmap drivers (http://obex.parallax.com/objects/category/3/ at the top).

    I don't know how you can extract the ASM in ICC7 but maybe Jazzed would be willing to show you what to do.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • dantledantle Posts: 3
    edited 2010-03-11 07:19
    Kye,

    Thanks for the suggestion. I was worried about the latency that C introduces between waitvid calls and the lack of optimization in the ICC compiler. I looked into it and it seemed a little bit ambiguous to me. I did create a hexdump of the driver I'm emulating, but once it came time to run the code, I figured the cog would probably just get lost. I don't know what kind of bootstrapping/magic-numbered code has been tacked onto the thing, or whether or not I need to worry about it from my environment. I figured the generated hex came from the context of the Spin tool and it still needs to do whatever assembly routines are involved in a cognew function (so I'd want to remove that). I'd certainly like to talk to Jazzed about it, but it would be cool if all my work wasn't in vain, too. [noparse]:)[/noparse]
  • RossHRossH Posts: 5,519
    edited 2010-03-11 10:23
    @dantle,

    I agree with Kye - it is extremely unlikely you could ever get a C driver to execute fast enough to do anythign useful. And why bother? There are many PASM VGA or TV drivers that can be used with C already.

    Ros.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Catalina - a FREE C compiler for the Propeller - see Catalina
  • KyeKye Posts: 2,200
    edited 2010-03-11 15:47
    Usually the VGA driver is passed the address of some stuff and from there it does things. In my drivers I load the address of variables to use in the ASM and then launch the driver.

    To learn to read spin if you cannot just print out the handy dandy spin quickference sheet and look over the driver source code. It should be very easy then to translate that into C.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Nyamekye,
  • jazzedjazzed Posts: 11,803
    edited 2010-03-11 17:10
    @dantle,

    First welcome to the forum.

    Second, I applaud your attempt for writing a VGA C driver. Your effort is not a total loss since you learned something about the way Propeller works during your research. Unfortunately, as you have gathered by now, the speed of C for Propeller in all the current forms can not keep up with the needs of the VGA output. VGA,TV,etc... drivers must run as fast as possible in a COG. The main reason C is slow for Propeller today is that C binaries are interpreted by a "kernel" COG using the LMM method. One could have C compiled to PASM for a COG to run, but the effectiveness of that method is questionable because of COG memory size.

    Now, if you want to "port" a current PASM driver, you can scrape the bytes out of the binary, create an ASCII-HEX array in C and use ICC's cognew_native for starting the PASM in a COG. It gets a little tricky because the number of Spin methods in the file impact the PASM start address. I wrote a utility attached as spinc.zip that automatically extracts the PASM driver to an ASCII-HEX array. If the program does not run, I will provide a 32 bit source. The syntax is spinc <SpinPASMdriver.binary>

    Catalina uses an entirely different approach from ICC where the actual Spin/PASM drivers are included in the image ... being able to add a custom driver in Catalina however seems a bit difficult to me though not impossible. One must also understand how the driver is included in Catalina; to this end, Ross has provided many fine examples.

    One of the more exciting on going developments in Propeller C is ZOG by heater. This excites me because it uses a GNU tool chain and should produce smaller binary images. Smaller is better in Propeller or any micro-controller with limited memory resources. ZOG code is run on a Propeller resident 8080 CPU class emulator. Such emulation is not one of my favorite things, but I'll hold my nose in this case to enjoy the quality and consistency I've come to expect from GNU tools. To date it is not clear how ZOG will be including drivers like VGA. I believe in the end it will use the Catalina model although the ways in which the drivers are included should be a lot more user friendly.

    In summary, you have several C related options for Propeller and VGA or other drivers. This may cause confusion to the experienced C programmer and produce rationalization for those who fear C or loathe C out of ignorance. Having a good breadth of existing and emerging C options is quite useful.

    I have to vote for the ZOG GNU tool-chain as the most useful potential option and encourage you to and others to contribute there.
    http://forums.parallax.com/showthread.php?p=878273

    Good luck with your journey.
    --Steve

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Short answers? Not available at this time since I think you deserve more information than you requested.
  • dantledantle Posts: 3
    edited 2010-03-11 21:32
    Thanks all for the suggestions.

    Jazzed, I've tried doing what you've said with using the HEX dump (thanks for the program, by the way) but I am still not really understanding the context of what's going on in this process. I think it takes the DAT section of the .asm and runs it from the init function. All I've done is gone through and set the things that were previously set by the spin code's PUB initialization and allocate some space for the colors and pixels to reside in. I guess I'm having a lot more trouble with this linkage than I thought it would be when I started. Is there something else I could be forgetting that wont let my code run?

    Attaching my code below just in case its super obvious to someone.

    #include <propeller.h>
    
    #define pr 35 
    
    long VGAdriver[noparse][[/noparse]] =
    {
        0xa0bfec30, 0xa0bfee31, 0x58fff00d, 0x58fff438, 
        0xa0bffc32, 0xa0bc7433, 0xa0bc7634, 0xa0fc7e0c, 
        0xa0fc8020, 0xa0fc8202, 0xa0fc7c10, 0xa0bffe35, 
        0x04bc783a, 0x60bc7836, 0x68bc7838, 0x08bc7a3b, 
        0xfc3c783d, 0x80fc7402, 0x80fc7604, 0xe4fc7c0c, 
        0x84fc7420, 0x84fc7640, 0xa0fc7c01, 0x5cfc5e28, 
        0xe4fc820a, 0x80fc7640, 0xe4fc8009, 0x80fc7420, 
        0xe4fc7e08, 0x083c6df0, 0xa0fc7c01, 0x5cfc5e26, 
        0xa0fc7c03, 0x5cfc5e25, 0xa0fc7c1c, 0x5cfc5e25, 
        0x5c7c0005, 0x6cfc7301, 0xa0bffe37, 0xfc7c7200, 
        0xa0fffe08, 0xfc7c7200, 0xa0fffe30, 0xfc7c7201, 
        0xa0fffe58, 0xfc7c7200, 0xe4fc7c26, 0x5c7c0000, //44-47
        0x00000000, 0x00000000, 0x00000000, 0x00000000, //[noparse][[/noparse]DIRA][noparse][[/noparse]DIRB][noparse][[/noparse]VCFG][noparse][[/noparse]CB]
        0x00000000, 0x00001020, 0x0000fcfc, 0x00000200, //[noparse][[/noparse]PB]
        0x00000303, 0x00000103,  0,0,0,0,0,0,0,0,0        /* added 8 more for 
                                                            uninitialized data */
    
    };
    
    short colors[noparse][[/noparse]192];
    
    long pixels[noparse][[/noparse]6144];
    
    void main()
    {
          // set VCFG register.
         char BasePin = 16;
         int i,j, syncptr;
         i = 0xFF << (BasePin & 0b011000);
         if((BasePin & 0b100000) == 0)
              j = 0;
         else
              j = -1;
         DIRA = VGAdriver[noparse][[/noparse]48] = i & j;
         DIRB = VGAdriver[noparse][[/noparse]49] = i & (~j);
         
         VGAdriver[noparse][[/noparse]51] = (long)&colors[noparse][[/noparse]0];
         VGAdriver[noparse][[/noparse]52] = (long)&pixels[noparse][[/noparse]0];
         
         CTRA = 0b00001101 << 23;    // enable PLL in CTRA (VCO runs at 4x)
         FRQA = ((pr / 5) << 3) << 23;
    
         VCFG = VGAdriver[noparse][[/noparse]50] = 0x200000FF + ((BasePin & 0b111000) << 6);
                   // start video generator
         cognew_native((void (*)())VGAdriver,&syncptr);
        
         while(1)
                  syncptr  = 0;
    }
    
  • jazzedjazzed Posts: 11,803
    edited 2010-03-11 21:47
    Hi. It's hard to tell what's going to happen without your PASM source, so you should post that too.
    I assume you have seen one of the examples of my method in the OBEX.
    If not here is a VGA module link obex.parallax.com/objects/365/

    I just noticed that you may be depending on the main to set the DIRA bits for your driver.
    You have to set all registers for your driver in the driver. By setting registers in main, you
    are only affecting the main COG and not the driver COG.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Short answers? Not available at this time since I think you deserve more information than you requested.

    Post Edited (jazzed) : 3/11/2010 9:53:54 PM GMT
  • RossHRossH Posts: 5,519
    edited 2010-03-11 23:02
    Hi dante,

    I think you may have a misunderstanding about how C code is executed on the Propeller. You might want to read about how LMM works to understand how your C code is executed (e.g. see http://propeller.wikispaces.com/Large+Memory+Model).

    If you look at jazzed's driver, you'll see that his C program contains no driver code at all - it is a set of driver access functions that communicate (via Hub RAM) with a self-contained PASM driver started in another cog - the actual PASM driver is represented as a binary "blob" - i.e. a bunch of longs in an array. Your code adopts the "blob" approach, but appears to be written as if you are expecting the C code to be executing in the same cog as the "blob", which is not the case. This is why your manipulation of DIR, CTRA and FRQA registers will not have the desired effect.

    I think you should take the approach that both Catalina's drivers and jazzed's driver takes - i.e. drivers are best written in PASM, with each driver running in its own dedicated cog. You can't manipulate that cog directly in C other than by starting and stopping it - you can only interact with it via manipulating Hub RAM. C code would access the driver via a set of predefined functions (Jazzed's mechanisms differs from Catalina's, but the concept is the same).

    By the way - the main difference between Jazzed's approach and the one taken by Catalina is that in Catalina the PASM driver is compiled each time along with the PASM generated by the C program - so you don't normally need to use "blobs" for device drivers (although you can use them for other things). Catalina drivers are derived from the standard Parallax drivers, modified to remove the need for a VAR segment - instead they get passed a pointer to the memory segment they should use on startup (which is a common technique, as Kye pointed out).

    Ross.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Catalina - a FREE C compiler for the Propeller - see Catalina
Sign In or Register to comment.