{{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │NTSC240H Demo / Copyright (C) 2009 Eric Ball / All rights reserved. │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │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: │ │ │ │1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following │ │ disclaimer in the source file containing the Software. │ │2. Redistributions which do not include the source code must include the above copyright in the same form and location as │ │ other third-party acknowledgments in the software itself and/or in end-user documentation included with the redistribution.│ │ │ │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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }} VAR BYTE vidcog[6] PUB start( NumCog, VGroup, VPins, TblPtr, NumSpr, ClutPtr, BGC, RowPtr ) '' Start LineRAM cogs '' Inputs: number of cogs, VGroup & VPins (VCFG fields), '' pointer to sprite table, number of sprites, '' pointer to color look up table (0=default), background color index, '' pointer to row counter (0=disable) '' Ouputs: 0 success; >0 parameter error; <0 insufficient cogs if CLKFREQ < 66818182 ' colorburst * 4 / 3 * 14 RETURN 10 if NumCog < 2 or NumCog > 5 RETURN 1 if VGroup < 0 or VGroup > 7 RETURN 2 if VPins < 1 or VPins > 255 RETURN 3 if TblPtr < $10 or TblPtr > $7FFF or TblPtr & 3 RETURN 4 if NumSpr < 1 or NumSpr > 511 or CLKFREQ*(NumCog-1)/15734-2980-36*NumSpr < 252 RETURN 5 if ClutPtr < $10 and ClutPtr <> 0 or ClutPtr > $7FFF or ClutPtr & 3 RETURN 6 if BGC < 1 or BGC > 88 RETURN 7 if RowPtr < $10 and RowPtr <> 0 or RowPtr > $7FFF RETURN 8 stop ' stop any existing cogs cNumCog := NumCog cVGroup := VGroup ' copy parameters to cog data cVPins := VPins cNumSpr := NumSpr if ( ClutPtr <> 0 ) LONGMOVE( @clut, ClutPtr, 88 ) cBGC := BGC cRowPtr := RowPtr sync &= CONSTANT( !($1FF<<9) ) ' reset cog # (hidden in JMP dest) cCNTsync := 11348 + 4112 * NumCog ' synchronization CNT cCNTsync += CNT repeat NumSpr from 1 to NumCog sync += CONSTANT(1<<9) ' increment cog # if (BGC := COGNEW( @sync, TblPtr )) < 0 ' no cogs available stop ' stop anything started RETURN NumSpr-NumCog-1 ' number of cogs short vidcog[NumSpr] := BGC + 1 ' save cogid RETURN 0 PUB stop | i '' Stop LineRAM cogs repeat i from 1 to 5 if vidcog[i] COGSTOP( vidcog[i]~ - 1 ) ' stop cog & set vidcog to 0 {{ 2007-05-06 genesys of idea 2007-07-06 release to forums.parallax.com of non-working testcode 2009-03-27 release to forums.parallax.com of 240H single cog proof of concept System clock must be > 67MHz The idea for this sprite routine is to copy the sprite data to a cog RAM based LineRAM buffer. Each long in the buffer is one cycle of colorburst (four 14,318,182Hz luma samples). The output routine outputs 3 luma samples per pixel and is capable of producing most/all of the NTSC color gamut (depending on the number of bits in the output DAC). This code assumes demoboard / Hydra composite output and is capable of a palette of 88 colors (plus transparent) out of 172 possible. See NTSC240.HTML for hovertext CLUT values. Multiple cogs (2-5) run the same code and are syncronized to output lines sequentially (i.e. while cog 1 is outputting line 1 from LineRAM, cog 2 is creating line 2.) to create a 240x240 image in a standard NTSC non-interlaced frame (303.3 pixels x 262 lines). The size of the sprite table and the number of active sprites is dependent on the number of cogs and the system clock. Sprite Table format addr[16]:xpos[8]:ypos[8] (lsb) (all 0 for unused) Sprites are 8x8 pixels (1 byte/pixel) stored in raster order color #0 is transparent, all others are color index into cog RAM xpos MUST be limitted to 0-232 - no wrap around or scrolling! vertical scrolling is possible (i.e. ypos=255 will scroll in from top, ypos=233 will scroll off bottom) current row number (240=vsync/vblank) stored in byte after Sprite Table Note: sprite table and sprite graphics must be long aligned }} DAT ORG $0 sync JMP #initall ' D=cog#, overwritten with sync ' LONG Y+V_Y+U_Y-V_Y-U ' CLUT format bit[7-4] = bit[3-0] = 40IRE 80IRE 40IRE 20IRE (lsb) clut LONG $22222222 ' #000000 black LONG $33333333 ' #333333 LONG $44444444 ' #666666 LONG $55555555 ' #999999 LONG $66666666 ' #CCCCCC LONG $77777777 ' #FFFFFF white LONG $33446655 ' #509C63 LONG $3355EE66 ' #63DF96 LONG $2244EE66 ' #3AD360 LONG $1144EE55 ' #11BC63 LONG $22336655 ' #27902D LONG $4455EE77 ' #8DF693 LONG $22337766 ' #30B72B LONG $1133EE66 ' #11C72B LONG $44445555 ' #6F8C63 LONG $66667777 ' #D5F2C9 LONG $33335555 ' #46802D LONG $55557777 ' #ACE693 LONG $33336666 ' #50A72B LONG $554477EE ' #ACF15B LONG $443377EE ' #83E526 LONG $55446677 ' #A2CA5E LONG $553366EE ' #A2D526 LONG $44334455 ' #66702D LONG $66556677 ' #CCD693 LONG $55335577 ' #99AE28 LONG $66445577 ' #C2BA5E LONG $663355EE ' #C2C526 LONG $774455EE ' #EBD15B LONG $773344EE ' #E1B526 LONG $44333344 ' #5C4930 LONG $66555566 ' #C2AF96 LONG $66444466 ' #B89360 LONG $77444477 ' #E1AA5E LONG $77443366 ' #D78360 LONG $77332266 ' #CE672B LONG $EE331166 ' #ED572B LONG $66443355 ' #AE6C63 LONG $77442255 ' #CE5C63 LONG $EE442266 ' #F77360 LONG $EE441155 ' #ED4C63 LONG $55443344 ' #855566 LONG $77665566 ' #EBBBCC LONG $77665566 ' #EBBBCC LONG $55331133 ' #711233 LONG $77553355 ' #D77899 LONG $77441144 ' #C43566 LONG $66553344 ' #AE629B LONG $77552244 ' #CE529B LONG $EE662244 ' #F75ED1 LONG $EE551144 ' #ED429B LONG $55441122 ' #71086B LONG $77663344 ' #D76ED1 LONG $66551122 ' #9B14A0 LONG $EE661133 ' #ED37D3 LONG $44443333 ' #5C3F68 LONG $66665555 ' #C2A5CE LONG $44442222 ' #52186B LONG $66664444 ' #B87ED1 LONG $55552222 ' #7B24A0 LONG $55663322 ' #8540D6 LONG $55662211 ' #7B19D8 LONG $33442211 ' #29016D LONG $55664433 ' #8F67D3 LONG $44662200 ' #5202DB LONG $44554433 ' #665B9E LONG $44664422 ' #6650D6 LONG $33663300 ' #3312DB LONG $33554422 ' #3C44A0 LONG $33664411 ' #3C39D8 LONG $22664400 ' #1322DB LONG $33665522 ' #4660D6 LONG $22665511 ' #1D49D8 LONG $22333322 ' #091C35 LONG $44555544 ' #6F829B LONG $33555533 ' #466B9E LONG $33666633 ' #5087D3 LONG $33667744 ' #5AAED1 LONG $22667733 ' #3097D3 LONG $1166EE33 ' #11A7D3 LONG $33556644 ' #50929B LONG $3366EE55 ' #63D5CE LONG $2266EE44 ' #3ABED1 LONG $1155EE44 ' #11B29B LONG $33445544 ' #467666 LONG $55667766 ' #ACDCCC LONG $22446644 ' #278666 LONG $4466EE66 ' #8DECCC nextrow MOV VSCL, vscl910 ' row delay WAITVID sync, #0 endinit MOV VSCL, vscl1 ' clear high bits of frame counter sNumCog ADD rownum, #cNumCog ADD $1F2, #cNumCog CMPSUB rownum, #263 ' odd number of lines per frame CMPSUB $1F2, #263 WC ' so colorburst phase alternates IF_C ADD $1F3, #1 ' increment frame counter on rollover zRowPtr WRBYTE rownum, cRowPtr ' write rownum to main memory CMP rownum, #240 WC,WZ IF_C JMP #doactive ' rownum=0-239 CMP rownum, #250 WC IF_NC_OR_Z JMP #doblank ' rownum=240,250-262 dovsync CMP rownum, #244 WZ IF_NZ CMP rownum, #245 WZ IF_NZ CMP rownum, #246 WZ MOV $1F0, #2 :half IF_NZ MOVS VSCL, #33 ' 33 PLLA @ -40 IRE (equalizing pulse) IF_Z MOVS VSCL, #388 ' 388 PLLA @ -40 IRE (serration pulse) WAITVID sync, #0 IF_NZ MOVS VSCL, #422 ' 422 PLLA @ 0 IRE (equalizing pulse) IF_Z MOVS VSCL, #67 ' 67 PLLA @ 0 IRE (serration pulse) WAITVID sync, blank DJNZ $1F0, #:half JMP #nextrow doactive MOVD sBGC, #lineram ' reset destination MOV $1F0, #240 ' number of pixels in LineRAM sBGC MOV lineram, cBGC ' background color ADD sBGC, d1 DJNZ $1F0, #sBGC sNumSpr MOV $1F0, #cNumSpr ' number of entries in sprite table sMaxSpr MOV $1F1, #MaxSpr2 ' maximum number of active sprites MOV sprptr, PAR :loop RDLONG sprdata, sprptr WZ ' 7 get sprite table entry IF_Z JMP #:nxtspr ' 11 zero entry, go to next sprite MOV sprtemp, rownum ' 15 format addr[16]:xpos[8]:ypos[8] SUB sprtemp, sprdata ' 19 relative to current row TEST sprtemp, #$0F8 WZ ' 23 check for outside 0-7 IF_NZ JMP #:nxtspr ' 27 outside range, go to next sprite MOV spraddr, sprdata ' 31 calculate sprite address SHR spraddr, #16 ' 35 base address for top left AND sprtemp, #7 ' 39 mask off offset SHL sprtemp, #3 ' 43 8 bytes per row ADD spraddr, sprtemp ' 47 RDLONG sprtemp, spraddr ' 48->7 ADD spraddr, #4 ' 11 SHR sprdata, #8 ' 15 shift xpos to lsb RDLONG spraddr, spraddr ' 16->7 AND sprdata, #$0FF ' 11 mask off xpos ADD sprdata, #lineram ' 15 add LineRAM base address MOV sprbyte, sprtemp ' handle each byte (LSB first!) AND sprbyte, #$0FF WZ ' $00 is transparent MOVS :byte0, sprbyte ' set source MOVD :byte0, sprdata ' set destination ADD sprdata, #1 :byte0 IF_NZ MOV sprdata, sprbyte SHR sprtemp, #8 MOV sprbyte, sprtemp AND sprbyte, #$0FF WZ MOVS :byte1, sprbyte MOVD :byte1, sprdata ADD sprdata, #1 :byte1 IF_NZ MOV sprdata, sprbyte SHR sprtemp, #8 MOV sprbyte, sprtemp AND sprbyte, #$0FF WZ MOVS :byte2, sprbyte MOVD :byte2, sprdata ADD sprdata, #1 :byte2 IF_NZ MOV sprdata, sprbyte SHR sprtemp, #8 WZ MOVS :byte3, sprtemp MOVD :byte3, sprdata ADD sprdata, #1 :byte3 IF_NZ MOV sprdata, sprbyte MOV sprbyte, spraddr AND sprbyte, #$0FF WZ MOVS :byte4, sprbyte MOVD :byte4, sprdata ADD sprdata, #1 :byte4 IF_NZ MOV sprdata, sprbyte SHR spraddr, #8 MOV sprbyte, spraddr AND sprbyte, #$0FF WZ MOVS :byte5, sprbyte MOVD :byte5, sprdata ADD sprdata, #1 :byte5 IF_NZ MOV sprdata, sprbyte SHR spraddr, #8 MOV sprbyte, spraddr AND sprbyte, #$0FF WZ MOVS :byte6, sprbyte MOVD :byte6, sprdata ADD sprdata, #1 :byte6 IF_NZ MOV sprdata, sprbyte SHR spraddr, #8 WZ MOVS :byte7, spraddr MOVD :byte7, sprdata DJNZ $1F1, #:byte7 MOV :byte7b, :byte7 JMP #:byte7b :byte7 IF_NZ MOV sprdata, sprbyte :nxtspr ADD sprptr, #4 DJNZ $1F0, #:loop WZ ' 48+16+223 = 287 (288) max cycles / sprite :byte7b IF_NZ MOV sprdata, sprbyte doblank MOVS VSCL, #67 ' 67 PLLA @ -40 IRE (sync pulse) WAITVID sync, #0 TEST rownum, #1 WZ ' even or odd row SHR $1F3, #1 WC,NR ' even or odd frame IF_Z_EQ_C MOVS :burst, #%%2123 ' rising -U burst IF_Z_NE_C MOVS :burst, #%%2321 ' falling -U burst IF_Z_EQ_C MOV :pixel0, evenp0 ' set colorburst phase & init pointers IF_Z_NE_C MOV :pixel0, oddp0 IF_Z_EQ_C MOV :pixel1, evenp1 IF_Z_NE_C MOV :pixel1, oddp1 IF_Z_EQ_C MOV :pixel2, evenp2 IF_Z_NE_C MOV :pixel2, oddp2 IF_Z_EQ_C MOV :pixel3, evenp3 IF_Z_NE_C MOV :pixel3, oddp3 MOVS VSCL, #9 ' 9 PLLA @ 0 IRE (blank) WAITVID sync, blank MOV $1F0, #9 ' 9 cycles colorburst MOVS VSCL, #4 ' 4 pixels (one cycle) per frame :burst WAITVID sync, #%%2123 DJNZ $1F0, #:burst ' 36 PLL total MOVS VSCL, #38 ' 38 PLLA @ 0 IRE (blank) WAITVID sync, blank CMP rownum, #240 WC,WZ ' active or blank IF_NC MOV VSCL, vscl760 IF_NC JMP #frontporch MOV $1F0, #240/4 MOVS VSCL, #3 ' 3 PLLA per frame (4.77MHz) :active ADD :pixel0, d4 ' note: doesn't take effect until next loop :pixel0 WAITVID lineram+0, #%%012 ' Y+U Y-V Y-U ADD :pixel1, d4 :pixel1 WAITVID lineram+1, #%%123 ' Y-V Y-U Y+V ADD :pixel2, d4 :pixel2 WAITVID lineram+2, #%%230 ' Y+U Y-V Y-U ADD :pixel3, d4 :pixel3 WAITVID lineram+3, #%%301 ' Y+V Y+U Y-V DJNZ $1F0, #:active ' 720 PLLA active video (240 pixels) WAITVID sync, blank ' 3 PLLA @ 0 IRE (blank) MOVS VSCL, #37 ' 37 PLLA @ 0 IRE (blank) frontporch WAITVID sync, blank JMP #nextrow evenp0 WAITVID lineram+0, #%%012 ' Y+U Y-V Y-U evenp1 WAITVID lineram+1, #%%123 ' Y+V Y+U Y-V evenp2 WAITVID lineram+2, #%%230 ' Y-U Y+V Y+U evenp3 WAITVID lineram+3, #%%301 ' Y-V Y-U Y+V oddp0 WAITVID lineram+0, #%%230 ' Y-U Y+V Y+U oddp1 WAITVID lineram+1, #%%301 ' Y-V Y-U Y+V oddp2 WAITVID lineram+2, #%%012 ' Y+U Y-V Y-U oddp3 WAITVID lineram+3, #%%123 ' Y+V Y+U Y-V d1 LONG 1<<9 ' opcode Dest field = 1 d4 LONG 4<<9 ' opcode Dest field = 4 vscl1 LONG 1<<12+1 ' 1 PLLA per pixel, 1 PLLA per frame vscl760 LONG 1<<12+760 ' 1 PLLA per pixel, 760 PLLA per frame vscl910 LONG 1<<12 ' (NumCog-1) * 910 PLLA per frame rownum LONG 241 ' current row (0 = first active row) blank LONG %%2222222222222222 ' 16 pixels, color = 2 (blank) sprptr LONG $63697245 ' pointer to sprite table entry (in main memory) sprdata LONG $6C6C6142 ' sprite table entry / pointer to lineRAM (xPos) sprtemp LONG $63697245 ' Ypos / left 4 pixels (LSByte = leftmost) spraddr LONG $6C6C6142 ' pointer to sprite graphics (main memory) ' / right 4 pixels (MSByte = rightmost) sprbyte LONG $63697245 ' current pixel cRowPtr LONG $6C6C6142 ' pointer to row counter (in main memory) FIT $100 lineram LONG $33221100 ' 20IRE 0IRE -20IRE -40IRE (lsb) initall RDLONG initall, #0 ' CLKFREQ MOV sprtemp, initall ' save CLKFREQ :shmax SHL l9090909, #1 WC ' maximize the two values RCL l7159090, #1 SHL sprtemp, #1 WC IF_NC JMP #:shmax RCR sprtemp, #1 ' undo overshoot :div CMPSUB l7159090, sprtemp WC ' do division RCL l9090909, #1 SHR sprtemp, #1 WZ IF_NZ JMP #:div ' division requires 650 CPU cycles MOV l7159090, initall ' save CLKFREQ SHR l7159090, #14 ' calculate VSCL startup delay WAITCNT cCNTsync, l7159090 ' sync to other cogs MOVI VCFG, #%0_01_1_0_0_000 ' VGA mode, 2 bits per pixel MOV VSCL, vscl1 ' set VSCL to 1 clock / frame (instant update) MOVI CTRA, #%0_00001_111 ' PLL internal mode, x16 PLLA = 114.54545MHz MOV FRQA, l9090909 ' set CTRA to 7.159MHz, start the counters MOVD VCFG, cVGroup ' set VGroup MOVS VCFG, cVPins ' set VPins SHL cVGroup, #3 ' calculate DIRA SHL cVPins, cVGroup MOV DIRA, cVpins ' set DIRA MOVS sNumCog, cNumCog ' set number of cogs MOVS sNumCog+1, cNumCog ' set number of cogs MOVS sBGC, cBGC ' set background color MOVS sNumSpr, cNumSpr ' set NumSpr CMP cRowPtr, #0 WZ IF_Z MOV zRowPtr, #0 ' NOP instruction if RowPtr = 0 SUB cNumCog, #1 ' NumCog -= 1 :loop1 ADD vscl910, l910 ' vscl910 = (NumCog-1) * 910 ADD sprtemp, initall ' sprtemp = (NumCog-1) * CLKFREQ DJNZ cNumCog, #:loop1 SHR sprtemp, #14 ' preshift :loop2 SHL MaxSpr1, #1 WZ ' calculate CPU cycles per line IF_NZ ADD MaxSpr2, sprtemp ' CLKFREQ / (15,734Hz*256/(288-36)) SHR sprtemp, #1 WZ IF_NZ JMP #:loop2 :loop3 SUB MaxSpr2, #36 ' 36 CPU cycles / sprite DJNZ cNumSpr, #:loop3 SHR MaxSpr2, #8 ' 288 CPU cycles / active sprite MOVS sMaxSpr, MaxSpr2 SHR sync, #9 ' extract embedded cog # AND sync, #7 MOV $1F2, sync ' initial rownum (0 = first vsync row) ADD rownum, sync ' initial rownum (241 = first vsync row) :loop4 ADD ivscl, l910 ' calculate initial delay DJNZ sync, #:loop4 MOV sync, lineram ' copy sync levels to CLUT[0] WAITCNT cCNTsync, #0 ' wait for 4096 PLLA clocks to expire MOVI CTRA, #%0_00001_100 ' set x2 PLLA = 14.31818MHz MOV VSCL, ivscl ' initial delay JMP #endinit l7159090 LONG $6D3D32 ' 7159090 Hz l9090909 LONG $E8BA2E8B ' 0.909090909 Hz l910 LONG 910 ivscl LONG 1<<12-455 ' initial delay MaxSpr1 LONG $20CCF<<14 ' 2^31 / (15,734Hz*256/(288-36)) (preshifted) MaxSpr2 LONG -3027 ' doactive overhead cycles 2980*256/(288-36) cVGroup LONG $63697245 cVPins LONG $6C6C6142 cNumCog LONG $63697245 cBGC LONG $6C6C6142 cNumSpr LONG $63697245 cCNTsync LONG $6C6C6142 {{ Notes: - 5 cog limit is due to 12 bits for VSCL frame counter - the code compensates for CLUT samples MSB = phase 0 while LSB = color 0 - broadcast is not supported - CLUT values should follow these rules: - samples between -40 IRE (all off) and 120 IRE - Y constant for all 4 values & between 0 and 100 IRE - RGB output between 0.0 & 1.0 using the following formulas: R = Y / 100 + V / 162.245 G = Y / 100 - U / 468.673 - V / 318.521 B = Y / 100 + U / 91.02 - CLUT values which do not follow these rules may exceed NTSC specs or produce unstable pixels - vertical edges may have dashed look Thanks to Doug Dingus (aka potatohead) for starting me on this think exercise and providing suggestions during development. }} {NTSC240.HTML