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

16 bit NTSC video Driver

PerryPerry Posts: 253
edited 2011-11-23 12:56 in Propeller 1
Thanks to Eric Ball's work with his 8bpp http://forums.parallax.com/showthread.php?134384-NTSC-8bpp-bitmap-display-driver&highlight=8bpp

I am offering for evaluation a 16bit NTSC video driver

This driver only uses 2 pins, on the demo board they would likely be 13 and 15.
pin 13 is the color signals capacitivly coupled to a Duty Dac from pin 15 to deliver luminence.

the demo shows 16 colors and 119 shades


Thanks to any spinner-heads who might try this


Perry

P.S.

having trouble with attachment so here are the two spin files.

first main test program: NTSC_16bit_demo.spin
{{

 16bit NTSC video  modifications  Octber 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

{
Notes from Grey TV driver:

This driver uses PWM to generate output so a low pass filter is recommended:

        0.001µF
 pin 13 ─────┐
 pin 15 ──┳─╋── Composit Video output
                  value of this cap is small I'm unsure
              
 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_16bit_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
' 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[80*120]
  
PUB start  | color,i,j,ver

  tv.start( @i_ptr )

  i_width  := 80
  i_height := 120
  i_lines  := 480
  i_clocks := 36
  i_pin    := TV_PIN
  i_frqa   := 0
  i_ptr    := @bitmap
  i_gain   := 24

  repeat j from 0 to 119 step 1
    repeat i from 0 to 79 step 1
      bitmap[i+j*80] :=  ((((i/4 & $F )<<4) + 8) << 8)  + j*2


and secondly the actual video driver: Pixelator_16bit_NTSC.spin
{{
&#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 Octber 2011   Perry James Mole
    most changes should be commented with  '    PJM
                                                                                                                                 &#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, burst '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                      ' 22 CLK
           '               WAITVID  pixel, #%%3210                     ' +6 CLK
           '               ADD      pixptr, #4                         ' +4 CLK
                          mov      luminance,pixel  ' PJM
                          shl      luminance,l_Gain  ' PJM
                          add      luminance,black  ' PJM

                          WAITVID  pixel, #%%1     ' PJM
                          MOV      FRQb,luminance  ' PJM
                          ADD      pixptr, #2  '    PJM

                          DJNZ     numpixels, #:loop                  ' +4 CLK = 36 CLK

                          MOV      VSCL, vscl_edge
                          WAITVID  dblank, sblank
                         MOV      FRQB, pwmlut      ' 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,#0  ' 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
                          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_000 '  PJM use PLL%1 was  #%0_00110_111             ' turn on PWM

                          AND      i_pin, #28                         ' 16 CLK
                          MOV      pixel, #%0010 'PJM use only color pin '7 summing network output
                          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     $4924a249 '$3d249249  ' PJM
black                     LONG     $4a24a249 '$3e93B84A  ' PJM
burst                     LONG     $4f24a249 '$3f000000  ' PJM
'color_mask                long     $FFFFFFF0  ' PJM
cog       long      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
luminance      res     1
vs4            res     1
                          FIT      496
«1

Comments

  • BaggersBaggers Posts: 3,019
    edited 2011-10-23 04:08
    Hi Perry,

    I'm surprised I'm the first to respond to this driver!

    Fantastic colour range in this driver :D ( although at 5Mhz I get a lot of black flickery lines scrolling up the screen, so I swapped to a 6Mhz crystal on my demo board, and WOW, instant fix! )

    Looks brilliant Perry, now to get some free time and figure out some cool stuff to do with it :D, what with this, and PhiPi's Colour Video Grabber!

    This looks for interesting times ahead!

    Baggers.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-10-23 04:35
    Yes flickering black lines here too at 5mhz.

    No R/C network here, and I see that it TVPin was given a value of 12 and my TV resistors are on pins 16,17,18 so I tried a variety of values from 15 to 18. With 15 I got a nice white to black fade, and with 17 I got some color bars.

    Which is the one we are trying to display?

    This is a very intriguing piece of code.
    1024 x 768 - 104K
  • PerryPerry Posts: 253
    edited 2011-10-23 09:01
    Dr_Acula wrote: »
    Yes flickering black lines here too at 5mhz.

    No R/C network here, and I see that it TVPin was given a value of 12 and my TV resistors are on pins 16,17,18 so I tried a variety of values from 15 to 18. With 15 I got a nice white to black fade, and with 17 I got some color bars.

    Which is the one we are trying to display?

    This is a very intriguing piece of code.

    Thanks for the image, it should show much better than that!

    to set pins for 16 use TV_PIN = 16 in the main code, the outputs used are then on pins 17 and 19

    17 for chroma .001 cap to resistors
    19 for luma

    I just left out the duty cap in my test.
    and use an alternate RCA connector to connect to TV
  • PerryPerry Posts: 253
    edited 2011-10-23 09:34
    Baggers wrote: »
    Hi Perry,

    I'm surprised I'm the first to respond to this driver!

    Fantastic colour range in this driver :D ( although at 5Mhz I get a lot of black flickery lines scrolling up the screen, so I swapped to a 6Mhz crystal on my demo board, and WOW, instant fix! )

    Looks brilliant Perry, now to get some free time and figure out some cool stuff to do with it :D, what with this, and PhiPi's Colour Video Grabber!

    This looks for interesting times ahead!

    Baggers.

    Thanks for your comments.

    I have using this with "stupid video capture", I was playing again last night with "false color" movies as I was testing the code that I hope to drop Phil's color decoder into.


    There seems to be at least one error in the first post
    around line 270 in the video driver
          MOV      VSCL, vscl_burst                   ' 9 cycles of colorburst
                              WAITVID  dblank, sburst
      xxxxxxx                       mov      frqb,#0  ' PJM  xxxxxx
                              mov      frqb,#pwmlut  ' PJM should be
    
    I normally use 108MHz clock

    but this code works at 80Mhz for me except for static horizontal funnys that aren't seen in videos.

    I have yet to study the part in the code that seems to time the instruction cycle! taskone?
  • BaggersBaggers Posts: 3,019
    edited 2011-10-24 02:43
    What is the best width you can get? so far I can get 96 width on 6Mhz crystal.

    And again I must add, the colour range with this driver is amazing! :) well done!
  • PerryPerry Posts: 253
    edited 2011-10-27 11:49
    Baggers wrote: »
    What is the best width you can get? so far I can get 96 width on 6Mhz crystal.

    And again I must add, the colour range with this driver is amazing! :) well done!

    Have not tried very high horz sizes, The vertical bars at 5Mhz are probably "i_clocks" set too small.

    My internet has been down lately, am visitinig a free access point!

    Perry
  • Bill HenningBill Henning Posts: 6,445
    edited 2011-10-27 16:49
    I am going to have to try this out soon... great work!
    Perry wrote: »
    Thanks to Eric Ball's work with his 8bpp http://forums.parallax.com/showthread.php?134384-NTSC-8bpp-bitmap-display-driver&highlight=8bpp

    I am offering for evaluation a 16bit NTSC video driver

    This driver only uses 2 pins, on the demo board they would likely be 13 and 15.
    pin 13 is the color signals capacitivly coupled to a Duty Dac from pin 15 to deliver luminence.

    the demo shows 16 colors and 119 shades


    Thanks to any spinner-heads who might try this


    Perry

    P.S.

    having trouble with attachment so here are the two spin files.

    first main test program: NTSC_16bit_demo.spin
    {{
    
     16bit NTSC video  modifications  Octber 2011   Perry James Mole pjm@hey-hello.ca
    
    &#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 demo (C) 2011-08-19 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;                                    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;
    }}
    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
    
    {
    Notes from Grey TV driver:
    
    This driver uses PWM to generate output so a low pass filter is recommended:
    
            0.001µF
     pin 13 &#61609;&#9472;&#9472;&#61611;&#9472;&#9472;&#9472;&#9488;
     pin 15 &#61609;&#9472;&#61629;&#61630;&#9472;&#9523;&#9472;&#9547;&#9472;&#9472;&#61627; Composit Video output
                 &#61628; &#61613;    value of this cap is small I'm unsure
                 &#61464; &#61464;
     The resistor values (124&#937; series, 191&#937; 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_16bit_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
    ' 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[80*120]
      
    PUB start  | color,i,j,ver
    
      tv.start( @i_ptr )
    
      i_width  := 80
      i_height := 120
      i_lines  := 480
      i_clocks := 36
      i_pin    := TV_PIN
      i_frqa   := 0
      i_ptr    := @bitmap
      i_gain   := 24
    
      repeat j from 0 to 119 step 1
        repeat i from 0 to 79 step 1
          bitmap[i+j*80] :=  ((((i/4 & $F )<<4) + 8) << 8)  + j*2
    
    

    and secondly the actual video driver: Pixelator_16bit_NTSC.spin
    {{
    &#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 Octber 2011   Perry James Mole
        most changes should be commented with  '    PJM
                                                                                                                                     &#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, burst '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                      ' 22 CLK
               '               WAITVID  pixel, #%%3210                     ' +6 CLK
               '               ADD      pixptr, #4                         ' +4 CLK
                              mov      luminance,pixel  ' PJM
                              shl      luminance,l_Gain  ' PJM
                              add      luminance,black  ' PJM
    
                              WAITVID  pixel, #%%1     ' PJM
                              MOV      FRQb,luminance  ' PJM
                              ADD      pixptr, #2  '    PJM
    
                              DJNZ     numpixels, #:loop                  ' +4 CLK = 36 CLK
    
                              MOV      VSCL, vscl_edge
                              WAITVID  dblank, sblank
                             MOV      FRQB, pwmlut      ' 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,#0  ' 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
                              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_000 '  PJM use PLL%1 was  #%0_00110_111             ' turn on PWM
    
                              AND      i_pin, #28                         ' 16 CLK
                              MOV      pixel, #%0010 'PJM use only color pin '7 summing network output
                              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     $4924a249 '$3d249249  ' PJM
    black                     LONG     $4a24a249 '$3e93B84A  ' PJM
    burst                     LONG     $4f24a249 '$3f000000  ' PJM
    'color_mask                long     $FFFFFFF0  ' PJM
    cog       long      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
    luminance      res     1
    vs4            res     1
                              FIT      496
    
  • ericballericball Posts: 774
    edited 2011-10-27 17:27
    Baggers wrote: »
    What is the best width you can get? so far I can get 96 width on 6Mhz crystal.

    Maximum resolution is a function of the inner loop:
    :loop                     RDword   pixel, pixptr   ' PJM'RDLONG   pixel, pixptr                      ' 22 CLK
                              mov      luminance,pixel  ' PJM ' +4 CLK
                              shl      luminance,l_Gain  ' PJM ' +4 CLK
                              add      luminance,black  ' PJM ' +4 CLK
    
                              WAITVID  pixel, #%%1     ' PJM +5 CLK
                              MOV      FRQb,luminance  ' PJM +4 CLK
                              ADD      pixptr, #2  '    PJM +4 CLK
    
                              DJNZ     numpixels, #:loop                  ' +4 CLK = 51 CLK
    

    So that's 51 CLK per pixel at 96MHz, or a minimum of 31 PLLA (i_clocks) per pixel, which works out to an i_width of around 89 pixels.
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-30 00:37
    I've been acting as product manager on a Internet startup. Big time sink. :( Caught a nice break, and just can't resist this.

    Having a great play with this right now. VERY impressive range of color. This is just a brilliant idea Perry. Very well done!

    Would anybody be interested in a higher resolution luma only overlay?

    I'm thinking of firing off another COG running another, simple 4 color tile driver to run right on the same pins. Been a while since I tried that. I think I can do it this time, particularly if it's luma only.

    Another alternative would be to run the luma channel on this one at the current 80 or so pixels, but open up the waitvid so that a few pixels fit in there... Both methods could be combined. Too bad I don't have some kind of simple latch, where I could do "AND" with the pins... More could be done, but RAM would be at issue. Calling Dr_A. :)

    This one works a pixel at a time, using waitvid for the color, then using the PWM for the luma. Why not just run the waitvid for a few pixels at a time? Since there are three pins, it's possible to "OR" the two outputs right? Different colors and a luma change for the brighter could be done. Run 4 pixels at a clip, keeping the PWM the same. I'm deffo gonna try that.

    There also appeared to be some bright luma values in the border region. When I configured Eric's driver to be 80x96 with borders, they appeared bright. Modifying a value or two in the TV driver fixed it, I think. Will post that shortly. Anyone else experience this?
  • BaggersBaggers Posts: 3,019
    edited 2011-10-30 08:43
    Nice Idea Potatohead, would be ace with a better resolution :) deffo worth the extra cog to do it!
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-30 10:47
    Hmmm... I can't seem to attach anything either. Wanted to upload my wip archive from last night.

    (attachment problems resolved... wierd)

    See post below for WIP code posting and sample image.
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-30 19:19
    In this post, I've created a quick bit of spin code that plots a dot, and does a few ramps, both intensity and color. Took a while to get that sorted out. Also took the opportunity to run this on my captures, and various TV's. Range of values for a good color space was the goal.

    As originally configured, the driver was running full screen, and IMHO the darker color ranges were well below "black". These value displayed are black and brighter. The Sony didn't like the below black signal at all, though the other devices seemed ok with it. Just a FYI. Well, mostly. The low color values dip a shade below black, but won't be at issue on any device I've ever seen.

    The code here is configured for a 80x96 display, centered in the NTSC "safe area", leaving a black border. It can run wider, but not too much narrower. I'm using a 100Mhz Propeller.

    My older, portable TV renders the graphics fairly well. Roll off the sharpness control a little, and it's basically useful. On my HD set, and the Sony WEGA, the PWM noise is very visible. IMHO, using the cap is recommended. I've gotta set one up for sure.

    Edit: The little lower resolution LCD displays should render this nicely. Just thought I would add that. I have one smaller one, and it looks great.

    The capture devices were not happy with this signal at all. One of the better captures attached. (and it's not very good, but does give you a sense of what the image looks like)
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-31 00:59
    Progress!

    Got the color spaces sorted nicely. This archive contains a nice color map showing the range of values, basic dot plotter, clipped to the screen, and range constrained color definition.

    Hues range from 0 to 15, all the basic hues a Propeller does with the built in video generator.
    Each HUE has 128 intensity values.

    2048 displayable colors are possible! Nice one Perry! :)

    If the HUE is set to a value greater than 15, it's interpreted as INTENSITY only. Monochrome.
    Each INTENSITY has 250 values.

    250 displayable grey scales are also possible!

    Resolution on this one is 80x96. It's not too big of a deal to run it at greater vertical resolutions, if desired. One page of graphics takes a little under 8k, leaving room for a higher resolution overlay. I'm thinking this may well be very useful for communicating levels, graphs and such, if some detail text can be included. Thinking on that...

    I'm currently working on a line command, and am going to run this on a better circuit, also S-video. The PWM raw from the demo board is kind of noisy, which actually limits the color resolution to about 80 pixels. Some color combinations are better than that, but dot crawl and fringing gets significant on most combinations. Load the archive code to see a sample display demonstrating color limits on your display. Tweak your sharpness, contrast and such, changing background colors to see what I mean.

    I'll post up S-video stuff shortly, once I get the PPDB wired properly.

    Right now, it's possible to plot out basic data, suppose somebody could do a nice ball 'n bat game, or display pictures. Use the setcolor method included to figure out graphics palette. Somebody needs to combine this with Phil's video capture. It's ready to go!
  • BaggersBaggers Posts: 3,019
    edited 2011-10-31 02:10
    You know what would be a great use for this?
    .
    .
    .
    A graphic adventure, top two thirds of the screen, using a bitmap, lower third using a charmap, maybe even half height rom text to save space?

    Just a thought!

    Great work though Perry and Potatohead :D kudos!
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-31 02:36
    Yeah, but there is more!

    I'm still puzzling over hues. Looks like there are 32 distinct hues. If so, that's ~4k colors... Found the extra set while tinkering around with an attempt to do S-video on this one.

    Got a more reasonable capture this time. Drawing darker color blocks interleaved with the bright palette samples cuts way down on the overall noise.

    Hues range from 0 to 31, all other values from post above apply. Values above 31 are seen as INTENSITY only, with the 250 value limit.

    I need some help understanding exactly how this one works. When I modified the TV driver to do S-video, I found colors and intensities combined. That was unexpected. I thought intensity was running on one pin, and color on the other... is that true?

    Got a fairly good handle on what it DOES however. :)
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-31 03:12
    @baggers:

    Yeah, like the old Apple ][ games sometimes did. Wouldn't be a big deal to do text. Could grab lots of pictures off the SD card.

    Until I get a better understanding of this one, I'm kind of limited though. It's totally possible to run more pixels in the waitvid, limiting intensity to the current 80 pixel resolution. Or... using another COG, with overlay video, or maybe just optimizing the PWM, better resolution could go that way too.

    Noise is the biggest hassle. I need some small value caps. Some values of intensity are decidedly "sparkly", and the color dot-crawl is being exacerbated, due to that, I believe. Color resolution right now is low. 80 pixels is actually pushing it some.

    @Perry

    What is needed is chroma only on the high pin, PWM running through another pin, on the standard DAC. The Demo Board has both the high pin and the DAC connected together, which actually displays most anything you throw at it, or it can do the broadcast carrier, as intended.

    I'm having trouble separating these two. Would love it if I could just get the LUMA signal out the composite connector, when chroma is routed to the high pin. I;m not quite seeing what you did here. Then again, it's late too. Gonna get sleep. Had a lot of fun though. Thanks for posting this!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-10-31 04:16
    Is it possible to mix a color with gray? Or in more technical terms, for a color with a fixed hue (say, blue) and a fixed lightness (say 50%), produce a color bar that goes from gray through to the full color?

    The reason I ask is that with the limited propeller palette, if you run that through a dithering algorithm, many colors need to be artificially created by mixing a color, then a gray pixel, then a color pixel, and these pixels do not blend particularly well and at higher pixel density start to cause artifacts.

    If you could produce a true mix of gray and a color then the lower number of pixels per line may not matter so much.

    I'm not sure how you would do this in one byte though, as you need to store HSL values and might need more than one byte per pixel.
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-10-31 04:59
    wow! I totally missed this thread because I have been interested in text only with color and low pin count.
  • BaggersBaggers Posts: 3,019
    edited 2011-10-31 06:56
    and to think, all these latest progresses would have probably been missed if PropII had already been out! another reason the Prop ( 1 ) is such a fantastic device!
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-31 12:34
    Dr_A, with this driver, that's not possible. What one could do though, is run it at 80x192, and simply interleave grey and the color vertically. Color pixel on top, with approximate grey level, and a grey pixel on bottom, for example, maybe checkerboard them. That works reasonably well to simulate what you are wanting to do. Would be acceptable for pictures, movies and simple data graphs and such.

    You should take a look with the code I have above. Very easy to do a coupla ramps to evaluate what that would look like. Not often we get to talk about a 3 - 4K color, any color anywhere type driver! :)

    Something like:
      repeat i from 0 to 31 step 2
         repeat j from 0 to 60 step 4
           setcolor(j/4, i*4)
           cplot(i+10, j+20)
           setcolor(32, i*4)
           cplot(i+10, j+21)
    

    Should draw a few pixels and or ramps for you to look at. Tweak the steps and or add cplot commands. (cplot = color plot, acts on last setcolor statement)

    Increase the graphics buffer to 16K
      word bitmap[80*96] 'The graphics screen, currently about 8k   (should be 192)
    

    Then adjust the screen driver parameters:
    i_height := 96   (should be 192)
      i_lines  := 96*4  (I think this can just change to 96*2)
    
    And the dot plotter:
    PUB cplot (x, y)
        '80 x 96 graphics screen
        x //= 79
        y //= 95  (SB 192)
        bitmap[y*80+x] := current_color
    

    IMHO, we need another video circuit to do what you are asking. Low resolution PWM could be done like this, using three COGs, maybe 4 pins total, outputting to YCbCr component video, or maybe RGB. The YCbCr format is Y = Intensity, Cb and Cr are blue and red color difference signals. Say you've got 50 percent grey on the Y, you could output a modest level difference on the Cb and or Cr channels to get saturation control, I think.

    (goes off to look at that more closely)

    I really like that format, and I have a display or two that will take it at up to HD resolutions. But... that input is not found on lower end displays, and isn't found at all, or much at all in various parts of the world. Why, I have no idea. I've done a quick test or two, then dropped it because it's a three cog solution to really be effective, and it's gonna take the RAM, and displays might not be all that easy, though I do believe there are converters out there. Maybe that makes sense? (goes off to look at those too)

    Could also run the Y channel at a higher resolution than the difference channels too, allowing for intensity detail, but lower resolution color. Been thinking about doing that, just because it's fun. Need to order up some resistors and a cap or two, it seems. PWM noise is too great without the caps for display on anything with any real capability.

    With RGB, we could do a similar thing, but without the ability to have intensity detail. Just fire off three of the PWM drivers, and figure out sync.

    Is three COGs worth it?

    Our lack of saturation control boils down to the color phase signal output by the prop video generator is just one fixed level. In composite land, we need to vary that, or move to a output form that gives us more direct control over the colors produced. Or... use clever dither techniques, like what I mentioned, or flicker it, alternating stuff across frames. (never did like that one, unless it's done on PAL, which actually renders the in-between color very nicely --one of the things I have always wanted to demonstrate on Prop, but we never got a good enough PAL signal for it to be worth doing)

    One other option in NTSC land, given RAM isn't too much of a consideration, is to simply crank up the resolution, using words or longs per pixel. Eric has written a stable artifacting driver, which means we can do mixing, like I showed here: http://propeller.wikispaces.com/Colors That one ended up being unstable, because it's hard to know the state of the phase counter with respect to the pixel clock. There are hue shifts on propeller start that occur, meaning one can't always count on a given value boiling down to a given color on screen, without a lot of fiddling. That works because the composite signal breaks higher frequency content into color. Crank up the pixel clock, so that the pixels fall inside the colorburst clock, and make sure they are consistently timed, and the pixels literally become color info. This was the method used in the old Apple ][ for color graphics. It quite literally just had small pixels. Where a person places one determines the color. This idea is just expanding on that with lots of pixels to get lots of colors.

    Did that one a long time ago, and didn't come back to it for that reason. Eric's recent NTSC efforts, inspired by the Apple, have the timings resolved, meaning that technique is on the table now. With more resolution, that method can produce more low saturation colors. Some of what that looks like can be seen on that driver screen capture.

    I used 320 pixels in that one, with one half of a 160 horizontal resolution pixel being a standard propeller color, and the other half being one of the intensities. With artifacting, the horizontal resolution is fixed to the period of the color burst, which is 160 pixels in the "safe area" inside the borders, just FYI. Can be lower multiples too, or the active part of the screen can be changed. Possibilities are 40, 64, 80, 128, 160, etc... horizontally. Vertically, it's all about RAM, or tiles and RAM, whichever. Could be that we only need 640, if the standard waitvid hues are incorporated. Haven't tried it.

    basically outputting color info, and luma info at a high rate, leaving the TV to average things out. A 640 pixel display will yield about 230 colors, a 1280 pixel one will output a lot! I never did the 1280 display trick, because it eats all the RAM... In C, maybe you don't care?

    1280 pixels @ 4 colors = 80 waitvids / line, one word per pixel, for a total of 160 pixels horizontal. 320 bytes per scan line... That means a 30k buffer would yield a 160x90 or so pixel display. Would look fab though! Maybe I can knock up a sample of that, running at a modest resolution, or just using part of the screen. Would be NTSC, composite only though. Edit: There are a LOT of possibilities here. Some tinkering would be needed. Might not need 1280. Lots of ways to "mix" and "artifact" in the display. (on the to-do list now, because I love that kind of thing)

    Prop video always comes down to RAM or pins, or both. Perry boosted what we can do though. I like this driver a lot. Opens the door to a lot of possibilities, and with the reference circuit too!

    @Baggers: I love Prop I for precisely that reason. It's a great chip! So many possibilities too. Seems the more we think about it, the more it will do. Love that. What other device can support the kinds of conversations we have here regularly? Video on Prop is fun! (too damn much fun, I'm tired today!)

    LOL!! Just thought about Perry and him getting Internet sorted. Bet he's gonna enjoy this thread!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-10-31 16:07
    This is a fascinating discussion. I'm still digesting that last posting and it will take a while longer yet.

    I need to find some time to push the ram chip to its limits - at the moment it is half the speed of the video scan line but I think it can go faster. If you can pull data off an external chip it opens up more possibilities.

    Researching NTSC is not as easy as I thought. I'm not even sure if some websites are correct - eg this wikipedia site http://en.wikipedia.org/wiki/Saturation_(color_theory) talks about colorfulness, chroma and saturation. They describe 'colorfulness' as the scale from gray to a full color. Then they talk about saturation and link to the HSL page where 'saturation' describes the scale from gray to full color. So what is 'saturation'?

    Whatever color format you pick, there are always 3 values, not two. eg http://en.wikipedia.org/wiki/File:Hsl-hsv-colorpickers.svg looking at the picture on the top right with a fixed hue, there are still two dimensions there to describe all the colors.

    The problem with some palettes is they are just 'too pretty', ie too colorful, ie, they only contain all the fully saturated colors and none of the ones with subtle mixes of gray.

    Thinking about it in terms of the full color palette, you can have very fine graduations between various hues but if the graduation between 'gray' and 'color' is only binary, ie one graduation, it leaves out a lot of colors.

    I'm not sure how to put this information back in? Is it the amplitude of the signal? I'm still not sure - I think amplitude is 'lightness', not 'grayness'.

    Maybe there is another pwm you could run to alter things further? I wonder what you could do if you considered using all 8 cogs. And maybe even had two ram chips working independently and you could pull data off those ram chips in parallel using separate cogs.

    I'd be interested to see what you could do with just a small tile in the middle of the screen.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2011-10-31 17:44
    Dr_A,

    Maybe this illustration will help:

    attachment.php?attachmentid=86503&d=1320108138

    The brightness is the signal level above "black" after lowpass filtering out the 3.57 MHz color component. The saturation is the amplitude of the 3.57 MHz chroma component, and the hue is the phase of the chroma component relative to the extrapolated phase of the color burst that occurs at the beginning of each line.

    A signal that's totally gray will have no chroma component. IOW the 3.57 MHz subcarrier will be flat. A signal that's totally saturated will have a very high chroma subcarrier riding on the brightness signal.

    Here's an image the illustrates the difference between saturation and brightness:

    attachment.php?attachmentid=86504&d=1320108138

    You can see that saturation is a measure of the "purity" of the color, whereas brightness is just a measure of how much light is emitted (or reflected).

    -Phil
    812 x 174 - 20K
    249 x 247 - 6K
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-10-31 17:54
    Brilliant - thanks PhiPi.

    So you want to adjust the amplitude to adjust the saturation. If there are clever tricks being done with pwm to adjust things, can we do more clever tricks? Run another cog to generate a pwm and use that to control a transconductance amplifier like the CA3080? Hmm - need to track the average level for the lightness so this sets the virtual earth of the op amp, then adjust the gain. That would be a low pass filter with a time constant about the same as for displaying a pixel. Though I see the open loop bandwidth is only 2Mhz - possibly not fast enough? There are other faster chips I see. (I'm searching for wide bandwidth transconductance).

    Ok, let me see. The Hue waveform is absolutely critical for timing and looking at the phase difference, would be too fast to store in an external ram (thinking sample-and-hold).

    But the Brightness and Saturation could be. And that information need not even go through a prop.

    Store Hue values in hub (could get up to 256x224). Store Saturation and Brightness in external ram (one chip and clock out alternate bytes, or two separate chips or one byte per pixel with 16 levels each of Sat/Brightness). The brightness goes to a level shifting op amp circuit and the saturation controls gain. Probably easier to do it in the other order because the earth is constant - so run gain first, then level shift.

    These are old-school circuits that I understand. Simple R/2R D to A, maybe start with 16 levels of saturation and 16 levels of brightness. Can store that in one byte per pixel. 16 levels of gain for the transconductance op amp and then 16 levels of shift by using an op-amp adder.

    Just need fast enough op amps. CA3280 transconductance amp has a 9Mhz bandwidth.
  • potatoheadpotatohead Posts: 10,261
    edited 2011-10-31 18:55
    Great chart Phil.

    This can also be done with just pixels. Draw a grey pixel, for example, then put a really bright, smaller pixel right in the middle of it. The result will be a moderate saturation-ish pixel as the timing of the small pixel and it's high frequency nature triggers the color circuits, it's intensity impacts the purity of the color, as does it's size, while the grey pixel tends to "fill-in" where there would normally be a more fully developed color signal.

    Frankly, we should fire off a thread about a NEW video circuit. The reference one has been, and will continue to be hacked all over the place. Very cool, but...

    It might be a great discussion to have. Say we target two COGs. It's not like people would have to write for two, but design the circuit so that two could really be put to use. Get pins sorted, and discuss various modulation techniques we have available to us. With monochrome NTSC / PAL, we can do overlay, where two COGs write to the same pins, with VGA, and things like component, we could do that too, and do so in color, and make choices, like running color at a lower bitrate than monochrome. Or... if we had a gate, where a logical AND operation could happen electrically, the output of one COG could gate another one. This has it's uses in TV land, many of which apply in VGA land too.

    One use would be to gate off a low resolution color signal, for example, allowing a dark intensity pixel to exist amidst a bright color. We can do the opposite now, as the pins are OR'ed together. Maybe that's not AND, but some other op. Something to be discussed.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-11-01 19:47
    I'm bursting with ideas at the moment thanks to post #23 and this whole thread. I think adjusting Sat and Brightness could be done using data clocked out from external ram chip(s).

    So I thought I might come back to Hue. On the propeller, Hue comes in 16 discrete steps. Let's say we took a signal with full saturation and zero brightness. Add saturation and brightness later, but internal to the prop you have 16 Hues. Can you get more Hues by tweaking the phase?

    The frequency is constant (3.57Mhz right?) and I was reading about phase shifts in R/C filters. There is a nice description about 1/3 of the way down this page http://www.geofex.com/article_folders/phasers/phase.html.

    So if you have a constant frequency and and RC network and you adjust the resistor, then does that mean you can adjust the phase. I still need to test out whether the prop can read bytes off memory fast enough, but let's say it can, you read a byte and use 3 bits to work out the Hue. The other bits are set for full saturation. At the same time another byte is read off another ram chip and that contains data fed into a programmable resistor (?? a 4066 fast enough?) and then that alters the R in a RC filter and thus tweaks the phase.

    Crazy ideas I know. I need to do more experiments on a breadboard. brb
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-11-02 04:33
    Many experiments later...

    It comes down to this loop
    rdloop                  mov             t2, ina                 ' read byte from SRAM \ ignores upper bits
                            wrbyte          t2, hubaddr             ' copy byte to hub    /
                            add             hubaddr, #1             ' inc hub pointer
                            add             outa, #(1 << 8)         ' inc sram address
                            djnz            len, #rdloop            ' loop for xxx bytes
    

    This is pulling the data off an sram as fast as possible and it is about 5/8 of the speed required. So even with one cog pulling data off a sram, the time it takes to do a wrbyte to move to hub is too long.

    Maybe maybe one could have two srams running in parallel, each with their own pins and cogs but it gets complex. You just can't pull the data off an sram and pass it through a propeller fast enough.

    I'm probably hijacking this thread too much, but I thought I might just throw the AD725 RGB to NTSC chip in the mix. http://www.analog.com/en/digital-to-analog-converters/video-encoders/ad725/products/product.html

    Get the prop to generate the hsync and vsync signals. Use a Word rather than a Byte and you can have 5 bits for red, 5 for green and 5 for blue (two sram chips, or even sram with 16 data lines)

    If you took that loop above and removed the wrbyte and one of the add instructions, could the propeller produce the timing clock fast enough?

    In an ideal world, I think a pixel is 80 nanoseconds. The SRAM chip is 55ns so that works.

    Even if that loop is fast enough, it may not divide in the right ratio. eg one loop might run too fast and you finish the line 3/4 of the way across the screen, and if you add a NOP it finishes off the screen and is too wide. The level of control is too coarse.

    Maybe it is better to use the propeller counters and tell it to give you 256 pulses at a period of x? (Can the prop do that). Or use HC161 counters and clock out using a separate osc, and use the prop to do clever things like detect when the 161 has counted up to 256 and then stop the clock.

    More silly thoughts. But if it gave the propeller a display of 32768 colors...
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-02 06:34
    Dr_Acula wrote: »
    This is pulling the data off an sram as fast as possible and it is about 5/8 of the speed required. So even with one cog pulling data off a sram, the time it takes to do a wrbyte to move to hub is too long.
    You can write ina to hub at clkfreq/4 MB/s. Because you write a byte at a time that's still clkfreq/16 MB/s. Only drawback is that you'd have to put the address handling (add outa, #256) into a 2nd cog. Would that be OK?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-11-03 00:08
    Hi kuroneko, can you explain that a bit more? What would you change on the code in post #27? And how much faster would it run?

    For this demo, one cog is running spin and one is running the TV driver and all the rest are free. And the only pins being used are the serial download, eeprom and 3 pins for TV. So there is a lot of flexibility.
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-03 00:33
    Dr_Acula wrote: »
    What would you change on the code in post #27? And how much faster would it run?
    Currently you occupy 2 hub windows for transferring a byte which gives you clkfreq/32 MB/s (2.5MB/s @80MHz). This can be done twice as fast (one byte per hub window or 5MB/s @80MHz). As I mentioned, address handling has to be done in a second cog, addresses will run from high to low. From your loop I'd remove the 2 add insns (so that it fits into 16 cycles). And I'd have to add some support ...
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-11-03 01:30
    Very cunning!

    The TV video driver cog is already updating a hub variable to say which line it is displaying, and the ram driver cog is using the change in this variable to sychronise the two, so having a third cog synch to that variable should be possible.
Sign In or Register to comment.