Shop OBEX P1 Docs P2 Docs Learn Events
16 bit NTSC video Driver - Page 2 — Parallax Forums

16 bit NTSC video Driver

2»

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-11-03 11:26
    You can do it in one cog. The trick is to start an NCO counter that outputs on P8 to toggle the LSB of the RAM's address. That way, the program has to increment the rest of address only once for every two fetches. The other trick is to combine the bytes read into a long, so four bytes can be written to the hub at once:
    '                          Read ina: *   P8:  0  1
    
    :loop         movi      pix,ina     '*        |
                  shr       pix,#8      '         |
                  movi      pix,ina     '*           |
                  shr       pix,#8      '            |
                  add       outa,:incr  '         |
                  movi      pix,ina     '*        |
                  shl       pix,#1      '            |
                  mov       pixl,ina    '*           |
                  shl       pixl,#24    '         |
                  add       outa,:incr  '         |
                  shr       pix,#8      '            |
                  or        pix,pixl    '            |
                  wrlong    pix,hubaddr '         |
                                        '         |
                  add       hubaddr,#4  '            |
                  djnz      reps,:loop  '            |
    
    :incr         long      $200
    
    Because of the additional instruction between the second and third read, the NCO timing is critical. You want the output on P8 to be settled when ina is fetched during the source read cycle.

    -Phil
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-11-03 15:08
    Ah PhipPi, now that is clever.

    Well that has me thinking about two more things (which may have applications for cache drivers for C as well). In addition to your counter trick -

    1) read values into cog variables rather than to hub, and then do a burst write to hub using longs rather than bytes. ? 16 or 32 byte blocks
    2) unroll the loop to, say, 16 or 32 bytes, and that saves the djnz.
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-03 16:45
    Because of the additional instruction between the second and third read, the NCO timing is critical. You want the output on P8 to be settled when ina is fetched during the source read cycle.
    In that case why not throw another counter at it? :) If anything we have enough of them.
    DAT
    '                          Read ina: *   P8:  0  1  P9:  0  1
    
    :loop         movi      pix,ina     '*        |          |      %00
                  shr       pix,#8      '         |          |      
                  movi      pix,ina     '*           |       |      %01
                  shr       pix,#8      '            |       |      
                  movi      pix,ina     '*        |             |   %10
                  shl       pix,#1      '         |             |
                  mov       pixl,ina    '*           |          |   %11
                  shl       pixl,#24    '            |          |
                  nop                   '         |             |
                  add       outa,:incr  '         |             |
                  shr       pix,#8      '            |          |
                  or        pix,pixl    '            |          |
                  wrlong    pix,hubaddr '         |          |  
                                        '         |          |  
                  add       hubaddr,#4  '            |       |  
                  djnz      reps,:loop  '            |       |  
    
    :incr         long      $400
    
    While not strictly necessary let's make some more room for whatever may be required. The version below does what I know as "pulling a lonesock". I'm sure other clever people came up with the same thing, point is I know it under that name.
    DAT
    '                          Read ina: *   P8:  0  1  P9:  0  1
    
    :loop         movs      pix, ina    '*        |          |      %00
                  ror       pix, #8 wc  '         |          |      
                  movs      pix, ina    '*           |       |      %01
                  ror       pix, #8     '            |       |      
                  movs      pix, ina    '*        |             |   %10
                  rol       pix, #15    '         |             |
                  movi      pix, ina    '*           |          |   %11
                  rcl       pix, #1     '            |          |
                  nop                   '         |             |
                  nop                   '         |             |
                  nop                   '            |          |
                  add       outa, :incr '            |          |
                  wrlong    pix,hubaddr '         |          |  
                                        '         |          |  
                  add       hubaddr, #4 '            |       |  
                  djnz      reps, :loop '            |       |  
    
    :incr         long      $400
    
  • PerryPerry Posts: 253
    edited 2011-11-23 12:56
    OK! my internet is back !

    Thanks for all the comments guys.

    I am now looking into adding "saturation" to this circuit. With just a few more parts and no code changes this setup should produce 4 levels of saturation.

    Please take a look at the schematic in the first spin file, did I get the diodes right !

    P.S. I can now upload files so zip archive is included.
    {{
    
     16bit NTSC video  modifications  November 23 2011   Perry James Mole pjm@hey-hello.ca
    
    ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
    │                               NTSC 8bpp bitmap TV driver demo (C) 2011-08-19 Eric Ball                                       │                                                            
    ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
    │                                    TERMS OF USE: Parallax Object Exchange 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.                         │
    └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
    }}
    CON
      ' Set up the processor clock in the standard way for 80MHz
      _XINFREQ = 13_500_000 + 0000
      _CLKMODE = XTAL1 + PLL8X
    
    '  _clkmode = xtal1 + pll16x
    '  _clkfreq = 80_000_000
    
      ' Demoboard TV output on pins 13 and 15
      
      TV_PIN   = 12
      biWidth  = 64 '92 '92 '98
      biHeight = 128 '72
    
    {
    
    
    This driver's concept is to produce an HSL video circuit
    
      hue ,saturation, luminance
    
    hue        16 values
    saturation  4 values
    luminance 128 values
    
                             ┌───────────────┐
                             │   470Ω        │
                             └────────┐  │
    saturation0..... pin 12 ─────────────┘  │
                                 470Ω        │
    hue............. pin 13 ──────────────┫
                             ┌───────────────┫                                    
                             │   240Ω        │
                             └────────┐  │
    saturation1..... pin 14 ─────────────┘  │
                                               0.001µF
                                  124Ω       │
    luminance....... pin 15 ──────────────┻──────┳───┳───── Composite Video output
                                               191Ω     470pf
                                                       
    
    Notes from Grey TV driver:
    This driver uses PWM to generate output so a low pass filter is recommended:
    
     The resistor values (124Ω series, 191Ω to ground) have an output impedance of
     75 ohms and will drive a 75-ohm load at 1V P-P. The cap is there to filter the
     DUTY doody. - Phil Pilgrim
     http://forums.parallax.com/forums/default.aspx?f=25&m=340731&g=342216
    
    However, in my experience, the RC network is not required. I have tested
    successfully using any of the Demoboard TV DAC resistors (although the higher
    resistance yields darker text) and with no resistors at all (although this
    is not recommended).
    }
    OBJ
       tv : "Pixelator_II_NTSC" '"NTSC 8bpp"
    
    VAR
    ' NTSC8bpp input parameters - 7 contiguous longs, reloaded each frame
    ' Note: NTSC8bpp input parameters not validated, minimum clockspeed 16.364MHz
      LONG i_ptr          ' pointer to screen (bytes, long aligned), 0 = inactive
      LONG i_width        ' number of columns, must be divisible by 4 (i_width*i_clocks < 3192-3894MHz/CLKFREQ)
      LONG i_height       ' number of rows (max i_lines) 
      LONG i_clocks       ' pixel width in 16x colorburst clocks (256 => i_clocks > 515MHz/CLKFREQ) 
      LONG i_lines        ' number of active lines (max 482, does not need to be integer multiple of i_height)
      LONG i_pin          ' must be divisible by 4, i_pin = 1.1k&#937;, i_pin+1 = 560&#937;, i_pin+2 = 270&#937;
      LONG i_frqa         ' 0 = calculate FRQA using CLKFREQ=long[0], MSB set = mask of calculated value, MSB clear = value to use
      long i_gain         ' output video gain Added for Pixelator
      long i_colors       ' color video flags Added for Pixelator
      long ix_colors
    ' For "square" pixels 3*i_height*i_clocks = 14*i_lines
    ' i_clocks < 10 and i_lines < 2*i_height will generate color artifacts instead of extra detail
    ' Due to overscan not all of the screen is visible on all TVs.
    ' "Action Safe" i_width*i_clocks < 2750, i_lines < 433; "Title Safe" i_width*i_clocks < 2444, i_lines < 385
    
      word bitmap[biWidth*biHeight]
      
      word hue, saturation, intensity
      
    PUB start  | color,i,j,ver
    
      tv.start( @i_ptr )
    
      i_width  := biWidth
      i_height := biHeight
      i_lines  := 480
      i_clocks := 33 '34
      i_pin    := TV_PIN
      i_frqa   := 0
      i_ptr    := @bitmap
      i_gain   := 24
      i_colors := $0_00
    
      repeat j from 0 to biHeight-1 step 1
        repeat i from 0 to biWidth-1 step 1
    
          intensity :=  (j*2)  & $7F
          bitmap[i+j*biWidth] :=  intensity
         
          case intensity&3
    
            0:   saturation :=  $0
    
            1:   saturation :=  $1
    
            2:   saturation :=  $4
    
            3:   saturation :=  $5
           
          hue := (i/4 & $F )<<4
         
          if j > 64
              bitmap[i+j*biWidth]  |= (hue | saturation | 8 )  << 8 
    '      if j == 64
    '         bitmap[i+j*biWidth]  := $0
          if j > 118
             bitmap[i+j*biWidth] :=   $d830 
    
    

    and the TV driver updated
    {{
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                 NTSC 8bpp bitmap TV driver (C) 2011-06-16 Eric Ball                                          &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;
        modifications for 16 bit November 23   Perry James Mole
        most changes should be commented with  '    PJM
        added concept for saturation 
                                                                                                                                     &#9474;
    &#9474; The NTSC8bpp TV driver displays a i_width x i_height image on a TV using standard 3pin TV DAC.                               &#9474;
    &#9474; The bitmap is padded horizontally and scaled vertically to i_lines.                                                          &#9474;
    &#9474; Pixels are stored in display order (left to right, top to bottom).                                                           &#9474;
    &#9474;                                                                                                                              &#9474;                                                                                                                              &#9474;                                                                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;                                    TERMS OF USE: Parallax Object Exchange License                                            &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }}
    
    VAR
    
    '  long cog
    
    {{
    ' NTSC8bpp input parameters - 7 contiguous longs, reloaded each frame
    ' Note: NTSC8bpp input parameters not validated, minimum clockspeed 16.364MHz
      LONG i_ptr          ' pointer to screen (bytes, long aligned), 0 = inactive
      LONG i_width        ' number of columns, must be divisible by 4 (i_width*i_clocks < 3192-3894MHz/CLKFREQ)
      LONG i_height       ' number of rows (max i_lines) square pixel = (14*i_height)/(3*i_lines)
      LONG i_clocks       ' pixel width in 16x colorburst clocks (256 => i_clocks > 515MHz/CLKFREQ) 
      LONG i_lines        ' number of active lines (max 482, does not need to be integer multiple of i_height)
      LONG i_pin          ' must be divisible by 4, i_pin = 1.1k&#937;, i_pin+1 = 560&#937;, i_pin+2 = 270&#937;
      LONG i_frqa         ' 0 = calculate FRQA using CLKFREQ=long[0], MSB set = mask of calculated value, MSB clear = value to use 
      LONG l_Gain         ' PJM added to control greyscale output dynamicaly ie contrast
    ' For "square" pixels 3*i_height*i_clocks = 14*i_lines
    ' i_clocks < 10 and i_lines < 2*i_height will generate color artifacts instead of extra detail
    ' Due to overscan not all of the screen is visible on all TVs.
    ' "Action Safe" i_width*i_clocks < 2750, i_lines < 433; "Title Safe" i_width*i_clocks < 2444, i_lines < 385
    }}
    
    PUB start( parmptr )
    
      stop
      RESULT := COGNEW( @cogstart, parmptr)
      cog := RESULT + 1
      RETURN
    
    PUB stop
       COGSTOP( cog~ - 1 )
    
    DAT
    {{
    The purpose of this code is twofold.  First, it can be used to display a 
    byte per pixel (TV color) bitmap.  Second, this code is intended to be a 
    template which may be used by others to develop Propeller video drivers.
    Note: this code creates an 29.97Hz 525 line interlaced display.
    
    Rules for developing video drivers:
    1. Start simple.  Hardcode values and static display.
    2. Add complexity and changes incrementally.  Verify at each step.
    3. If something doesn't work it's either because you have made an incorrect
       assumption or made a coding error.
    }}
    
                              ORG      0
    cogstart                  RDLONG   i_ptr, PAR               WZ
                IF_Z          JMP      #cogstart
                            
                              MOV      taskptr, #taskone
                              MOV      numlines, #4
    :dotasks                  JMPRET   taskptr, taskptr
                              DJNZ     numlines, #:dotasks
    
    {{
    An interlaced NTSC frame is made up of 525 lines:
    9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses)
    12 lines blank
    241 lines active (top field / first line)
    half line (with sync pulse)
    9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses)
    half line (without sync pulse)
    12 lines blank
    241 lines active (bottom field / last line)
    
    A non-interlaced NTSC frame is made up of 262 lines:
    9 lines vertical sync (6 equalizing pulses, 6 serration pulses, 6 equalizing pulses)
    12 lines blank
    241 lines active
    }} 
    
    frame                     MOV      numlines, numblank0      WZ        ' overscan blank lines
                              MOV      taskptr, #taskone                  ' reload parameters
                IF_NZ         CALL     #blank
    
                              MOV      nxtptr, i_ptr
                              MOV      linenum, i_lines
                              MOV      numlines, numodd
                              CALL     #active
    
                              MOV      numlines, numblank1      WZ        ' overscan blank lines
                IF_NZ         CALL     #blank
    
                              MOV      VSCL, vscl_sync                    ' half line
                              WAITVID  dblank, #0
                             MOV      FRQB, #0    ' PJM
    
                              MOV      VSCL, vscl_serr
                              WAITVID  dblank, sblank
                             MOV      FRQB, pwmlut   ' PJM
    
                              CALL     #vsync
    
                              MOV      VSCL, vscl_half                    ' extend blank for 1/2 line
                              WAITVID  dblank, sblank
                             MOV      FRQB, #0   ' PJM
    
    {
    One of the limitations of TV.spin is it only allows integer vertical
    scaling.  This calculation (and the matching calculation inside the active
    loop) scales a i_height bitmap to i_lines.  Note: the first line is
    always displayed on the top field.  
    }
                              MOV      nxtptr, i_ptr
                              MOV      linenum, i_lines
                              SUB      linenum, i_height        WZ,WC     ' set scaling for 2nd line
                IF_Z_OR_C     ADD      linenum, i_lines
                IF_Z_OR_C     ADD      nxtptr, i_width                    ' go to next line (single line res)
                IF_Z_OR_C     ADD      nxtptr, i_width                    ' go to next line (single line res)
    
                              MOV      numlines, numblank2      WZ        ' overscan blank lines
                IF_NZ         CALL     #blank
    
                              MOV      numlines, numeven
                              CALL     #active
    
                              MOV      numlines, numblank3      WZ        ' overscan blank lines
                IF_NZ         CALL     #blank
    
                              CALL     #vsync                             ' moved here to reduce instructions 
                                                                          ' from active to equalizing
                              JMP      #frame                             
    
    {{
    As each pixel may be any color, the pixels are effectively the color values.
    Therefor the code uses a "reverse WAITVID" which uses the pixel value for the
    color (destination) parameter and #%%3210 to output the colors in LSB first
    order.  The downside of this method is it requires more RAM and the maximum
    resolution is clockspeed limited (which isn't a problem for composite NTSC
    which is bandwidth limited by the colorburst frequency). 
    }}
    active                    MOV      VSCL, vscl_sync                    ' 4.7usec @ -40 IRE (sync pulse)
                              WAITVID  dblank, #0
                             MOV      FRQB, #0   ' PJM
    
                              MOV      pixptr, nxtptr                     ' precalculate pointer
                              SUB      linenum, i_height        WZ,WC     ' vertical scaling calculations
                IF_Z_OR_C     ADD      linenum, i_lines
                IF_Z_OR_C     ADD      nxtptr, w_width                    ' go to next line
                              SUB      linenum, i_height        WZ,WC     ' do twice for interlace
                IF_Z_OR_C     ADD      linenum, i_lines
                IF_Z_OR_C     ADD      nxtptr, w_width                    ' go to next line
                              MOV      numpixels, i_width
    
                              MOV      VSCL, vscl_s2cb                    ' 0.6usec @ 0 IRE (blank)
                              WAITVID  dblank, sblank
                             MOV      FRQB, pwmlut   ' PJM
    
                              MOV      VSCL, vscl_burst                   ' 9 cycles of colorburst
                              WAITVID  dblank, sburst
                             MOV      FRQB, pwmlut   ' PJM
    
                              MOV      VSCL, vscl_edge
                              WAITVID  dblank, sblank
                             MOV      FRQB, black   ' PJM
    
                              mov      vs4,vscl_active   ' PJM
                              shr      vs4,#2   ' PJM
                              MOV      VSCL, vs4 'vscl_active   ' PJM
                                               ' i_clocks PLLA per pixel, 4 pixels per frame
    
    '                          WAITVID  pixel, #%%0
    :loop                     RDword   pixel, pixptr   ' PJM'RDLONG   pixel, pixptr 7 - 22  ' 22 CLK
               '               WAITVID  pixel, #%%3210                       
               '               ADD      pixptr, #4                         
                              mov      luminance,pixel  ' PJM      ' +4 CLK  26
                              rol      luminance,l_Gain  ' PJM     ' +4 CLK  30
                              add      luminance,black  ' PJM      ' +4 CLK  34
    
                              WAITVID  pixel, #%%1     ' PJM      ' +5 CLK   39
                              MOV      FRQb,luminance  ' PJM      ' +4 CLK   43
                              ADD      pixptr, #2  '    PJM       ' +4 CLK    47
    
                              DJNZ     numpixels, #:loop         ' +4 CLK = 51 to 44 CLKs
    
                              MOV      VSCL, vscl_edge
                              WAITVID  dblank, sblank
                             MOV      FRQB, black      ' PJM
    
                              DJNZ     numlines, #active                  ' next line
    active_ret                RET
    
    {{
    Video drivers are constrained by WAITVID to WAITVID timing.  In the inner
    active display loop, this determines the maximum resolution at a given clock
    frequency.  Other WAITVID to WAITVID intervals (e.g. front porch) determine
    the minimum clock frequency.
    
    For example the inner :loop in this code is 36 CLK and displays 4 pixels.
    Therefore with an 80MHz clock the minimum VSCL.PixelClocks (i_width) is 7.
    36 / 80MHz * 57.272727MHz / 4 = 6.44
    
    Similar calculations should be used between each pair of WAITVIDs to determine
    the minimum clock frequency (including initialization tasks).
    
    Note: the first HUBOP after a WAITVID should be counted as 22 CLK as it cannot
    be synchronized.  WAITVID should be counted as 6 CLK as 5 CLK can cause
    intermittent errors with very tight timing.
    }}
    
    vsync                     MOV      numlines, #6
    :equal1                   MOV      VSCL, vscl_eqlo                   ' equalizing pulse (sync/2)
                              WAITVID  dblank, #0
                              mov      frqb,#0  ' PJM
    
                              MOV      VSCL, vscl_eqhi
                              WAITVID  dblank, sblank
                              mov      frqb,pwmlut  ' PJM
                              DJNZ     numlines, #:equal1
    
                              MOV      numlines, #6
    :serration                MOV      VSCL, vscl_serr                    ' serration pulse (sync)
                              WAITVID  dblank, #0  ' PJM
                              mov      frqb,#0
    
                              MOV      VSCL, vscl_sync
                              WAITVID  dblank, sblank
                              mov      frqb,pwmlut  ' PJM
                              DJNZ     numlines, #:serration
    
                              MOV      numlines, #6
    :equal2                   MOV      VSCL, vscl_eqlo                    ' equalizing pulse (sync/2)
                              WAITVID  dblank, #0
                              mov      frqb,#0  ' PJM
    
                              MOV      VSCL, vscl_eqhi
                              WAITVID  dblank, sblank
                              mov      frqb,pwmlut  ' PJM
                              DJNZ     numlines, #:equal2
    
    vsync_ret                 RET
    
    blank                     MOV      VSCL, vscl_sync                    ' 4.7usec @ -40 IRE (sync pulse)
                              WAITVID  dblank, #0  ' PJM
                              mov      frqb,#0
    
                              MOV      VSCL, vscl_s2cb                    ' 0.6usec @ 0 IRE (blank)
                              WAITVID  dblank, sblank
                              mov      frqb,pwmlut
    
                              MOV      VSCL, vscl_burst                   ' 9 cycles of colorburst
                              WAITVID  dblank, sburst
                              mov      frqb,pwmlut  ' PJM
    
                              MOV      VSCL, vscl_half
                              WAITVID  dblank, sblank
                              mov      frqb,pwmlut  ' PJM
                              JMPRET   taskptr, taskptr
    
                              MOV      VSCL, vscl_blank
                              WAITVID  dblank, sblank
                              mov      frqb,#0  ' PJM
    
                              DJNZ     numlines, #blank                   ' next line
    blank_ret                 RET
    
    d1                        LONG     |<9                                ' destination = 1
    
    taskone                   MOV      pixptr, PAR                        ' load input parameters
                              MOVD     :i_loop, #i_ptr                    ' 242 CLK
                              MOV      numpixels, #8 '  ' PJM #7 add l_Gain to inputs
    :i_loop                   RDLONG   i_ptr, pixptr
                              ADD      pixptr, #4
                              ADD      :i_loop, d1
                              DJNZ     numpixels, #:i_loop
    
                              MOV   W_width,i_width ' PJM now using words for pixel depth
                              shL   W_width,#1
    
                              CMP      i_ptr, #0                WZ        ' 0 = blank
                IF_Z          JMP      #standby                           ' 8 CLK
    
                              MOV      vscl_active, i_clocks              ' vscl_active = i_clocks<<12 + i_clocks*4
                              SHL      vscl_active, #12                   ' 16 CLK
                              ADD      vscl_active, i_clocks
                              SHL      vscl_active, #2
    
                              MOV      vscl_edge, vscl_edge0              ' vscl_edge = 1<<12 + (3192 - i_clocks*i_width)/2
                              MOV      numerator, i_width                 ' 120 CLK                                                    
                              MOV      divisor, i_width
                              MAX      numerator, i_clocks
                              MIN      divisor, i_clocks
    :mul                      SHR      divisor, #1              WZ,WC     ' maximum 6 iterations
                IF_C          SUB      vscl_edge, numerator
                              SHL      numerator, #1
                IF_NZ         JMP      #:mul
                              SHR      vscl_edge, #1
    
    {
    The calculations here accomplish two tasks: ensure the blank lines are
    distributed correctly and add on the blank lines to the top of each field.
    }                          
    ' numeven := rounddown(i_lines / 2)
                              MOV      numeven, i_lines                   ' 60 CLK
                              SHR      numeven, #1                          
    ' numodd := roundup(i_lines / 2)
                              MOV      numodd, i_lines
                              SUB      numodd, numeven      
    ' numblank2 := numblank0 := int( (484-i_lines)/4 ) + 12
                              MOV      numblank0, #484
                              SUB      numblank0, i_lines
                              SHR      numblank0, #2
                              ADD      numblank0, #12
                              MOV      numblank2, numblank0
    ' numblank1 := int( (482-i_lines)/4 )
                              MOV      numblank1, #482
                              SUB      numblank1, i_lines
                              SHR      numblank1, #2
    ' numblank3 := numblank1 + i_lines // 2
                              MOV      numblank3, i_lines
                              AND      numblank3, #1
                              ADD      numblank3, numblank1
    
                              JMPRET   taskptr, taskptr                   ' 242+8+16+120+60 = 446 CLK 
    
                              add      i_pin,#3 '  ' PJM for Grey duty cycle
                              MOVS     CTRb, i_pin
                              MOV      pixel, #1
                              SHL      pixel, i_pin
                              mov       DIRA, pixel
                              sub      i_pin,#3       '  PJM set pin back
                              MOVI     CTRb, #%0_00110_111 '  PJM use PLL%1 was  #%0_00110_000             ' turn on PWM
    
                              AND      i_pin, #28                         ' 16 CLK
                              MOV      pixel, #%0010 'PJM use only color pin '7 summing network output
    ' PJM use origional value when   saturation circuit is in place
                              SHL      pixel, i_pin              
                              or      DIRA, pixel                        ' set pin mask
    
                              MOV      pixel, i_pin                       ' 36 CLK 
                              SHR      pixel, #3
                              MOVD     VCFG, pixel                        ' set VGroup
                              AND      i_pin, #4                WZ
                              MOV      pixel, #7
                              SHL      pixel, i_pin
                              MOVS     VCFG, pixel                        ' set VPins
                IF_Z          MOVI     VCFG, #%0_10_1_0_1_000             ' composite baseband on VPins[2..0]
                IF_NZ         MOVI     VCFG, #%0_11_1_0_1_000             ' composite baseband on VPins[6..4]
    
                              RDLONG   divisor, #0                        ' CLKFREQ
                              MOV      quotient, l9090909                 ' 306 CLK
                              MOV      numerator, l7159090
    :shlmax                   SHL      quotient, #1             WC        ' maximize the two values
                              RCL      numerator, #1                      ' maximum 17 iterations (based on minimum 16MHz CLK)
                              SHL      divisor, #1              WC
                IF_NC         JMP      #:shlmax
                              RCR      divisor, #1                        ' undo overshoot
    
                              JMPRET   taskptr, taskptr                   ' 16+36+306 = 358 CLK
    
                              MOV      numpixels, #16                     ' do division (first half)
    :div1                     CMPSUB   numerator, divisor       WC        ' 264 CLK
                              RCL      quotient, #1
                              SHR      divisor, #1              WZ
                              DJNZ     numpixels, #:div1
    
                              JMPRET   taskptr, taskptr                   ' 264 CLK
    
    :div2                     CMPSUB   numerator, divisor       WC        ' complete division
                              RCL      quotient, #1                       ' 264 CLK
                              SHR      divisor, #1              WZ
                IF_NZ         JMP      #:div2
    
    {
    See http://www.linusakesson.net/programming/propeller/pllsync.php
    for synchronizing multiple video output cogs
    Otherwise just be aware that due to PLL startup time and the initial
    value of the internal frame counter being unknown that the timing
    and stability of the first frame is not predictable.
    } 
    
                              MOV      VSCL, ivscl                        ' set VSCL to instant reload
                              MOVI     CTRA, #%0_00001_110                ' PLL internal mode, x8 CTRA = 57.2727272MHz (colorburst * 16)
                              SHL      i_frqa, #1               WC,WZ,NR  ' test MSB/zero
                IF_NZ_AND_NC  MOV      quotient, i_frqa
                IF_C          AND      quotient, i_frqa          
                              MOV      FRQA, quotient                     ' set CTRA to 7.159MHz, start the counters
    
    
    lasttask                  JMPRET   taskptr, taskptr                   ' 256+24 = 280 CLK
                              JMP      #lasttask
    
    standby                   MOV      DIRA, #0
                              MOV      VCFG, #0
                              MOV      CTRA, #0
                              MOV      CTRb, #0
                              JMP      #cogstart
    
    l7159090                  LONG     $6D3D32                            ' 7159090 Hz
    l9090909                  LONG     $E8BA2E8B                          ' 0.909090909 Hz
    ivscl                     LONG     1<<12+1                            ' initial delay
    
    vscl_sync                 LONG     1<<12+269                          ' 4.7usec
    vscl_s2cb                 LONG     1<<12+304-269                      ' sync to colorburst
    vscl_burst                LONG     16<<12+16*9                        ' 16 PLLA per cycle, 9 cycles of colorburst
    vscl_eqlo                 LONG     1<<12+135                          ' sync/2
    vscl_eqhi                 LONG     1<<12+1685                         ' H/2 - sync/2
    vscl_serr                 LONG     1<<12+1551                         ' H/2 - sync
    vscl_half                 LONG     1<<12+1820                         ' H/2
    vscl_blank                LONG     1<<12+1372
    dblank                    LONG     $8A0200
    sblank                    LONG     %%1111_1111_1111_1111              ' 16 pixels color 1
    sburst                    LONG     %%2222_2222_2222_2222              ' 16 pixels color 2
    vscl_edge0                LONG     1<<13+3192                         ' initial value (pre shifted)
    pwmlut                    LONG     $3fff_ffff '$3824_4924 '$45555555 '$3d249249  ' PJM
    black                     LONG     $4fff_ffff '$3daaaaaa '$3e93B84A  ' PJM
    'burst                     LONG     $4e44ffff '$3f000000  ' PJM
    'color_mask                long     $FFFFFFF0  ' PJM
    cog       long      0
    luminance      long
    lum0      byte          0
    lum1      byte          0
    lum2      byte          0
    lum3      byte          0
    vscl_edge                 RES      1                                  ' border, 1 PLLA per pixel
    vscl_active               RES      1                                  ' 4 pixels @ i_clocks PLLA per pixel
    
    i_ptr                     RES      1                                  ' pointer to screen (bytes) 0 = inactive
    i_width                   RES      1                                  ' number of longs (4 pixels per long)
    i_height                  RES      1                                  ' number of rows
    i_clocks                  RES      1                                  ' pixel width
    i_lines                   RES      1                                  ' number of active lines
    i_pin                     RES      1                                  ' pin number
    i_frqa                    RES      1                                  ' FRQA value / mask
    l_gain                    res     1
    W_width                   RES      1                                  ' number of longs (4 pixels per long)
    
    numeven                   RES      1                                  ' number of even active lines
    numodd                    RES      1                                  ' number of odd active lines
    numblank0                 RES      1
    numblank1                 RES      1
    numblank2                 RES      1
    numblank3                 RES      1
    
    quotient                  RES      1
    numerator                 RES      1
    divisor                   RES      1
    
    taskptr                   RES      1                                  ' initialization task jmpret pointer
    numlines                  RES      1                                  ' overall line counter
    
    linenum                   RES      1                                  ' line scaling counter
    numpixels                 RES      1                                  ' pixel counter
    pixptr                    RES      1                                  ' pointer to pixels
    nxtptr                    RES      1                                  ' pointer to next line
    pixel                     RES      1                                  ' pixel value
    
    vs4            res     1
                              FIT      496
    
Sign In or Register to comment.