Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 12 of 12

Thread: Bit banging VGA output with Teensy 4.0

  1. #1
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110

    Bit banging VGA output with Teensy 4.0

    I've finally managed to get something working, so I thought it would be a good idea to post my progress just in case it is of use to others.

    Theory
    The VGA display consists of 525 line of 800 pixels, updated every 16.6667ms. This means that each line is drawn within 31,777.30ns... dividing by 800 means that a pixel needs to be drawn every 39.72ns.

    A VGA line is made up of 4 parts, a "Front Porch", a "Sync Pulse", a "Back Porch", and finally an "Active Pixel block", these are sized like this:
    Front Porch = 16 pixels
    Sync Pulse = 96 pixels
    Back Porch = 48 pixels
    Active Pixel Block = 640 pixels

    My idea was to divide these blocks by the lowest common denominator, and generate an interrupt at that frequency. In this case all sections are divisible by the Front Porch time. The Front Porch duration is 635.55ns, and all other sections are a multiple of that.

    To use the interval timer, I had to edit the IntervalTimer.h code to remove the 17 bus cycle limit (which limits the interval to around 700ns, and seems oddly arbitrary since the CPU frequency is much faster than the bus frequency), I then implemented an interval timer callback as a simple state machine, to update the display as required. The vertical stuff is is handled in the front porch state and is not doing much more than generating a vSync pulse for two lines when needed.

    Currently only a single pixel is drawn per active pixel block interrupt (alternating black and white), which should draw 40 vertical lines on the screen, but the hope will be to use this time to draw 16 pixels, this will block the interrupt for the duration, but since only the visible (where vLine is greater than 44) active pixel blocks will be blocking, this should still be useable. Or if I figure out how to DMA or some other I/O method, then pixel drawing won't need any CPU time

    You will notice that the interval timer is not super stable (which messing with my monitor...), which I guess is due to the PIT being based on the 24MHz bus clock (for VGA the ideal clock frequency is 25.1752Mhz)... If we can increase this freq it might be better?

    So, Please comment, point out what I've done wrong... or could improve? Thanks,

    As always, my quest is to make the code as simple as possible and to use as few nonstandard features (editing the IntervalTimer.h is not something I'm happy about, but I think Paul should remove this limit) to ensure upward compatibility.

    Code:
    #define H_FRONT_PORCH_START 0
    #define H_SYNC_START 1
    #define H_BACK_PORCH_START 7
    #define H_PIXEL_BLOCK_START 10
    #define H_LAST_PIXEL_BLOCK 50
    
    
    IntervalTimer hEvent;
    
    int state = 0;
    int hSyncPin = 13;
    int vSyncPin = 14;
    int pixPin = 16;
    int hState = 0;
    int vLine = 0;
    
    void hRun(){
    
      if(hState == H_FRONT_PORCH_START){
        digitalWriteFast(pixPin,LOW);  
    
        //update vertical state
        if(vLine == 11){
          digitalWriteFast(vSyncPin,HIGH);    
        }
    
        if(vLine == 13){
          digitalWriteFast(vSyncPin,LOW);    
        }
    
        vLine++;
        if(vLine == 526){vLine = 0;};
        
        hState++;
        return;
      }
    
      if(hState == H_SYNC_START){
        digitalWriteFast(hSyncPin,HIGH);  
        hState++;
        return;
      }
    
      if(hState == H_BACK_PORCH_START){
        digitalWriteFast(hSyncPin,LOW);  
        hState++;
        return;
      }
    
      if(hState == H_LAST_PIXEL_BLOCK){
        hState= 0;
        state = 0;
        digitalWriteFast(pixPin, state);    
        return;
      }
    
      //DRAW PIXELS!!
      if(hState >= H_PIXEL_BLOCK_START && vLine >= 44){
        state = 1 - state;
        digitalWriteFast(pixPin, state);    
      }
      
        hState++; 
    }
    
    void setup() {
      // put your setup code here, to run once:
      pinMode(hSyncPin,OUTPUT);
      pinMode(vSyncPin,OUTPUT);
      pinMode(pixPin,OUTPUT);
      hEvent.begin(hRun,0.63555); // need to edit IntervalTimer.h and remove the 17 cycle limit
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }
    Last edited by bloodline; 02-27-2020 at 12:01 PM.

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,910
    You probably may want to look here - it does all with DMA in the background, so the CPU can other things

  3. #3
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110
    Quote Originally Posted by Frank B View Post
    You probably may want to look here - it does all with DMA in the background, so the CPU can other things
    Hi Frank, thanks for sharing this, defragster has already pointed me to this thread.

    I love what you have done with integrating your C64 emu on the Teensy 3.6...

    As you can probably guess I have similar plans, now we have a microcontroller running at 600Mhz, I want to see if I can integrate the Amiga Emulator I wrote in a similar fashion!

    I need minimum, 12bits per pixel... so I may have to wait until a pjrc release a larger form factor Teensy 4...

  4. #4
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,910
    I am very curious about your emulator

  5. #5
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110
    Quote Originally Posted by Frank B View Post
    I am very curious about your emulator
    You can find the source code here: https://github.com/h5n1xp/Omega

  6. #6
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,910
    I watched the video - great!

  7. #7
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110
    Quote Originally Posted by Frank B View Post
    I watched the video - great!
    It took a huge amount of work just to get it this far... The Blitter is still missing a couple of modes, sprites arenít emulated at all (except sprite 0, which is emulated outside of the DMA sequencer)... But it runs system friendly software well, and quite a few games amazingly work too!

  8. #8
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,910
    I don't know much about the technical details of Amiga.
    If the sprites are nearly as complex (i guess they are more complex) as on C64...
    It was an incredible amount of work to get them work on the 3.6 (they need to be incredible fast) with 50Hz - and it took its time to get their collision detection working. Finally, I used a "screen buffer" for one single rasterline, rendered one line of the sprites there with "on the fly" collision detection. A "hair puller" to get this fast enough... since they can be behind the background or in the front (or in front of sprites with lower priority..) ,pixel by pixel.

    In the 80ies, the sound and graphics of AMIGA was incredible.

    I hope I can play a AMIGA game on the Teensy 4.x this year

  9. #9
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110
    Quote Originally Posted by Frank B View Post
    I don't know much about the technical details of Amiga.
    If the sprites are nearly as complex (i guess they are more complex) as on C64...
    If you have any free time have a read of this.

    http://amigadev.elowar.com/read/ADCD.../node00AE.html

    The C64 and Amiga sprite systems are somewhat similar (though the Amigaís Sprite system heritage is, amusingly, from the 8bit Atari line). What makes the Amiga really difficult to emulator is the beam synchronised Co-processor...

    It was an incredible amount of work to get them work on the 3.6 (they need to be incredible fast) with 50Hz - and it took its time to get their collision detection working. Finally, I used a "screen buffer" for one single rasterline, rendered one line of the sprites there with "on the fly" collision detection. A "hair puller" to get this fast enough... since they can be behind the background or in the front (or in front of sprites with lower priority..) ,pixel by pixel.
    Hence why Iíve avoided sprites in my Amiga Emualtor for the time being... they are a lot of work, and I didnít need them for my original project goal.

    In the 80ies, the sound and graphics of AMIGA was incredible.

    I hope I can play a AMIGA game on the Teensy 4.x this year
    Well, I hope so too... I think Iím going to need a lot of help from everyone here, I donít have as much time as I used to have

  10. #10
    Senior Member
    Join Date
    Jan 2014
    Location
    London, UK
    Posts
    110
    I think the scope of my project is too ambitious at this time. It’s a real struggle to switch the IO fast enough to generate the pixel data, not to mention the DA conversion... I’ve looking into using Shift registers like the 74HC595 and R2R DACs but this is increasingly drifting out of my project scope, for a “single chip” solution. It looks like the T4 does actually have some kind of display generation hardware on the die, but I can’t really figure out if it’s useful for this.

    I have pondered if using a chip like the BT816 might be a sensible way forward, but the form factor of the package make it difficult for me to use.

    https://www.mouser.co.uk/datasheet/2...-R-1483592.pdf
    Last edited by bloodline; 03-17-2020 at 08:43 PM.

  11. #11
    Senior Member
    Join Date
    May 2018
    Posts
    105
    Hi bloodline,

    Just discovered this mail thread today.
    Nice project to restart an amiga emulator from scratch.

    I was also my dream to run an Amiga on the T4.0
    I had troubles to port UAE for some time by I finally managed.
    The main issue was to have a contiguous 512k of RAM for the chipram in the T4. The malloc memory of the Teensy 4 is exactly 512K but with the overhead of the allocator and some memory allocated by some libraries, it was problematic.
    At the end I used the same technique as on the Atari ST castaway port I had done. Split in 2 times 256k.
    It is problematic in UAE as blitter, copper and bitmap routines are using pointers directly.
    By ignoring this, some games are working as long the 68000 PC is handled properly.
    Also, what is an amiga with 512k only nowadays...
    At the end it kind of worked on the T4.0 without audio. Audio libs needed some extra memory that was not available.


    When the T4.1 was released, all was resolved with the PSRAM driver MMAPED
    I think Frank did some of the work there?

    Last w-e I posted a video+code about the status in another thread (Amiga emulation on T4.1 in general discussion)
    You can put comments there.

    https://youtu.be/LE4IWPG5z6g

    https://github.com/Jean-MarcHarvengt...ster/README.md

    I hope to restart from a more recent version of UAE to fix some issues...

  12. #12
    Junior Member
    Join Date
    Mar 2017
    Posts
    15
    I still have an Amiga 2000 at home. Haven't turned it on in 30 or so years.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •